diff --git a/.github/actions/setup-normal-workspace/action.yml b/.github/actions/setup-normal-workspace/action.yml
new file mode 100644
index 000000000000..a0781d53c46c
--- /dev/null
+++ b/.github/actions/setup-normal-workspace/action.yml
@@ -0,0 +1,13 @@
+name: 'Setup Java, Gradle and check out the source code'
+
+runs:
+ using: composite
+ steps:
+ - name: Set up JDK 21
+ uses: actions/setup-java@v4
+ with:
+ distribution: 'temurin'
+ java-version: 21
+ cache: gradle
+ - name: Setup gradle
+ uses: gradle/actions/setup-gradle@v4
diff --git a/.github/scripts/process_detekt_sarif.sh b/.github/scripts/process_detekt_sarif.sh
new file mode 100644
index 000000000000..4d4f2af93298
--- /dev/null
+++ b/.github/scripts/process_detekt_sarif.sh
@@ -0,0 +1,37 @@
+#!/bin/bash
+
+# This script processes the Detekt SARIF file and outputs results in a format
+# suitable for annotation in CI/CD systems.
+
+SARIF_FILE="$1"
+
+# Check if SARIF file exists
+if [ ! -f "$SARIF_FILE" ]; then
+ echo "SARIF file not found: $SARIF_FILE"
+ exit 1
+fi
+
+# Define jq command to parse SARIF file
+read -r -d '' jq_command <<'EOF'
+.runs[].results[] |
+{
+ "full_path": .locations[].physicalLocation.artifactLocation.uri | sub("file://$(pwd)/"; ""),
+ "file_name": (.locations[].physicalLocation.artifactLocation.uri | split("/") | last),
+ "l": .locations[].physicalLocation,
+ "level": .level,
+ "message": .message.text,
+ "ruleId": .ruleId
+} |
+(
+ "::" + (.level) +
+ " file=" + (.full_path) +
+ ",line=" + (.l.region.startLine|tostring) +
+ ",title=" + (.ruleId) +
+ ",col=" + (.l.region.startColumn|tostring) +
+ ",endColumn=" + (.l.region.endColumn|tostring) +
+ "::" + (.message)
+)
+EOF
+
+# Run jq command to format the output
+jq -r "$jq_command" < "$SARIF_FILE"
diff --git a/.github/workflows/assign-relevant-labels.yml b/.github/workflows/assign-relevant-labels.yml
new file mode 100644
index 000000000000..4118c7172093
--- /dev/null
+++ b/.github/workflows/assign-relevant-labels.yml
@@ -0,0 +1,64 @@
+name: "Assign relevant labels"
+on:
+ pull_request_target:
+ types: [ opened, edited ]
+jobs:
+ assign-label:
+ if: github.event.pull_request.state == 'open'
+ runs-on: ubuntu-latest
+ permissions:
+ issues: write
+ pull-requests: write
+ contents: read
+ steps:
+ - name: label
+ env:
+ TITLE: ${{ github.event.pull_request.title }}
+ LABEL_FIX: Bug Fix
+ LABEL_BACKEND: Backend
+ uses: actions/github-script@v7
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN}}
+ script: |
+ const labelsToAdd = [];
+ const labelsToRemove = [];
+ const title = process.env.TITLE.split(":")[0].toUpperCase();
+
+ if(title.includes("FIX")){
+ labelsToAdd.push(process.env.LABEL_FIX);
+ } else {
+ labelsToRemove.push(process.env.LABEL_FIX);
+ }
+
+ if(title.includes("BACKEND")){
+ labelsToAdd.push(process.env.LABEL_BACKEND);
+ } else {
+ labelsToRemove.push(process.env.LABEL_BACKEND);
+ }
+
+ for (const label of labelsToAdd) {
+ github.rest.issues.addLabels({
+ issue_number: context.issue.number,
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ labels: [label]
+ });
+ }
+
+ const {data} = await github.rest.issues.listLabelsOnIssue({
+ issue_number: context.issue.number,
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ });
+
+ for (const label of labelsToRemove) {
+ const filtered = data.filter(l => l.name == label);
+ if(filtered.length == 1){
+ github.rest.issues.removeLabel({
+ issue_number: context.issue.number,
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ name: label
+ });
+ }
+ }
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 8275ac8b0740..c8a1303921e2 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -18,43 +18,50 @@ jobs:
runs-on: ubuntu-latest
name: "Build and test"
steps:
- - uses: actions/checkout@v3
- - name: Set up JDK 21
- uses: actions/setup-java@v3
- with:
- java-version: 21
- distribution: temurin
- cache: gradle
- - name: Setup gradle
- uses: gradle/gradle-build-action@v2
+ - name: Checkout code
+ uses: actions/checkout@v4
+ - uses: ./.github/actions/setup-normal-workspace
- name: Build with Gradle
run: ./gradlew assemble -x test --stacktrace
- - uses: actions/upload-artifact@v3
+ - uses: actions/upload-artifact@v4
name: Upload development build
with:
name: "Development Build"
- path: versions/1.8.9/build/libs/*.jar
+ path: build/libs/*.jar
- name: Test with Gradle
run: ./gradlew test
- - uses: actions/upload-artifact@v3
+ - uses: actions/upload-artifact@v4
name: "Upload test report"
if: ${{ !cancelled() }}
with:
name: "Test Results"
path: versions/1.8.9/build/reports/tests/test/
+ detekt:
+ name: Run detekt
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ - uses: ./.github/actions/setup-normal-workspace
+ # detektMain is a LOT slower than detekt, but it does type analysis
+ - name: Run detekt main (w/typing analysis)
+ run: |
+ ./gradlew detektMain --stacktrace
+ - name: Annotate detekt failures
+ if: ${{ !cancelled() }}
+ run: |
+ chmod +x .github/scripts/process_detekt_sarif.sh
+ ./.github/scripts/process_detekt_sarif.sh versions/1.8.9/build/reports/detekt/main.sarif
+
+
preprocess:
runs-on: ubuntu-latest
name: "Build multi version"
steps:
- - uses: actions/checkout@v3
- - name: Set up JDK 21
- uses: actions/setup-java@v3
- with:
- java-version: 21
- distribution: temurin
- cache: gradle
- - name: Setup gradle
- uses: gradle/gradle-build-action@v2
+ - name: Checkout code
+ uses: actions/checkout@v4
+ - uses: ./.github/actions/setup-normal-workspace
- name: Enable preprocessor
run: |
mkdir -p .gradle
diff --git a/.github/workflows/check-style.yaml.disabled b/.github/workflows/check-style.yaml.disabled
deleted file mode 100644
index ff172208f8bc..000000000000
--- a/.github/workflows/check-style.yaml.disabled
+++ /dev/null
@@ -1,16 +0,0 @@
-name: check-style
-on:
- - pull_request
-jobs:
- ktlint:
- name: Check Style
- runs-on: ubuntu-latest
-
- steps:
- - uses: actions/checkout@v3
- name: Checkout code
- - name: ktlint
- uses: ScaCap/action-ktlint@master
- with:
- github_token: ${{ secrets.github_token }}
- reporter: github-pr-check
diff --git a/.github/workflows/generate-constants.yaml b/.github/workflows/generate-constants.yaml
index c589676d0e2f..f6fab6f243ac 100644
--- a/.github/workflows/generate-constants.yaml
+++ b/.github/workflows/generate-constants.yaml
@@ -29,7 +29,7 @@ jobs:
- name: Generate Repo Patterns using Gradle
run: |
./gradlew generateRepoPatterns --stacktrace
- - uses: actions/upload-artifact@v3
+ - uses: actions/upload-artifact@v4
name: Upload generated repo regexes
with:
name: Repo Regexes
@@ -45,7 +45,7 @@ jobs:
with:
repository: ${{ env.data_repo }}
branch: main
- - uses: actions/download-artifact@v3
+ - uses: actions/download-artifact@v4
name: Upload generated repo regexes
with:
name: Repo Regexes
diff --git a/.github/workflows/illegal-imports.txt b/.github/workflows/illegal-imports.txt
index f53a5da78e25..83354f622960 100644
--- a/.github/workflows/illegal-imports.txt
+++ b/.github/workflows/illegal-imports.txt
@@ -5,7 +5,8 @@
at/hannibal2/skyhanni/ scala.
at/hannibal2/skyhanni/ jline.
-at/hannibal2/skyhanni/ io.github.moulberry.notenoughupdates.util.Constants
at/hannibal2/skyhanni/ io.github.moulberry.notenoughupdates.events.SlotClickEvent
at/hannibal2/skyhanni/ io.github.moulberry.notenoughupdates.events.ReplaceItemEvent
+at/hannibal2/skyhanni/ io.github.moulberry.notenoughupdates.util.Constants
+at/hannibal2/skyhanni/ io.github.moulberry.notenoughupdates.util.Utils
at/hannibal2/skyhanni/ java.util.function.Supplier
diff --git a/.github/workflows/label-bug-fix.yml b/.github/workflows/label-bug-fix.yml
deleted file mode 100644
index dae2980de630..000000000000
--- a/.github/workflows/label-bug-fix.yml
+++ /dev/null
@@ -1,44 +0,0 @@
-name: "Bug Fix label"
-on:
- pull_request_target:
- types: [ opened, edited ]
-jobs:
- assign-label:
- if: github.event.pull_request.state == 'open' # Condition to check if PR is open
- runs-on: ubuntu-latest
- permissions:
- issues: write
- pull-requests: write
- contents: read
- steps:
- - name: label
- env:
- TITLE: ${{ github.event.pull_request.title }}
- LABEL: Bug Fix - Sooner than Very Soon
- uses: actions/github-script@v7
- with:
- github-token: ${{ secrets.GITHUB_TOKEN}}
- script: |
- if(process.env.TITLE.split(":")[0].toUpperCase().includes("FIX")){
- github.rest.issues.addLabels({
- issue_number: context.issue.number,
- owner: context.repo.owner,
- repo: context.repo.repo,
- labels: [process.env.LABEL]
- })
- }else{
- const {data} = await github.rest.issues.listLabelsOnIssue({
- issue_number: context.issue.number,
- owner: context.repo.owner,
- repo: context.repo.repo,
- })
- const filtered = data.filter(label => label.name == process.env.LABEL)
- if(filtered.length == 1){
- github.rest.issues.removeLabel({
- issue_number: context.issue.number,
- owner: context.repo.owner,
- repo: context.repo.repo,
- name: process.env.LABEL
- })
- }
- }
diff --git a/.github/workflows/pr-check.yml b/.github/workflows/pr-check.yml
new file mode 100644
index 000000000000..3daad12374c5
--- /dev/null
+++ b/.github/workflows/pr-check.yml
@@ -0,0 +1,57 @@
+name: "PR Changelog Verification"
+
+on:
+ pull_request_target:
+ types: [ opened, edited, ready_for_review ]
+
+jobs:
+ verify-changelog:
+ if: github.event.pull_request.state == 'open' && '511310721' == github.repository_id && github.event.pull_request.draft == false
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - uses: ./.github/actions/setup-normal-workspace
+
+ - name: Grant execute permission for gradlew
+ run: chmod +x gradlew
+
+ - name: Run ChangeLog verification
+ env:
+ PR_TITLE: ${{ github.event.pull_request.title }}
+ PR_BODY: ${{ github.event.pull_request.body }}
+ run: |
+ ./gradlew checkPrDescription -PprTitle="${PR_TITLE}" -PprBody="${PR_BODY}"
+
+ - name: Add label if changelog verification fails
+ if: failure()
+ uses: actions-ecosystem/action-add-labels@v1
+ with:
+ github_token: ${{ secrets.GITHUB_TOKEN }}
+ labels: 'Wrong Title/Changelog'
+
+ - name: Remove label if changelog verification passes
+ if: success()
+ uses: actions-ecosystem/action-remove-labels@v1
+ with:
+ github_token: ${{ secrets.GITHUB_TOKEN }}
+ labels: 'Wrong Title/Changelog'
+
+ - name: Add comment to PR if changelog verification fails
+ if: failure()
+ uses: actions/github-script@v6
+ with:
+ github-token: ${{ secrets.GITHUB_TOKEN }}
+ script: |
+ const fs = require('fs');
+ const test = fs.readFileSync('versions/1.8.9/build/changelog_errors.txt', 'utf8');
+ const commentBody = `${test}`
+
+ github.rest.issues.createComment({
+ issue_number: context.issue.number,
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ body: commentBody
+ })
diff --git a/.gitignore b/.gitignore
index d77f4bd8b6f0..7f4081e02e52 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,7 @@
!.idea/icon.svg
!.idea/dictionaries/default_user.xml
!.idea/scopes/Mixins.xml
+!.idea/liveTemplates/SkyHanni.xml
.vscode/
run/
build/
diff --git a/.idea/dictionaries/default_user.xml b/.idea/dictionaries/default_user.xml
index b166e60d38fd..fc3066750657 100644
--- a/.idea/dictionaries/default_user.xml
+++ b/.idea/dictionaries/default_user.xml
@@ -63,6 +63,7 @@
dicer
disintegrator
disintegrators
+ draconic
dragontail
dreadfarm
dreadlord
@@ -81,10 +82,12 @@
explosivity
ezpz
fairylosopher
+ fels
fermento
firedust
firesale
firesales
+ flowstate
framebuffer
getfromsacks
ghast
@@ -100,6 +103,7 @@
hideparticles
hoppity
hoppity's
+ hoppitys
horsezooka
hotbar
hotm
@@ -145,7 +149,9 @@
millenia
minecart
mineman
+ mineshafts
miniboss
+ minigame
mirrorverse
misclick
missclick
@@ -190,6 +196,8 @@
pling
pocalypse
polarvoid
+ powerup
+ powerups
preinitialization
procs
prospection
@@ -248,9 +256,11 @@
supercraft
supercrafting
superlite
+ superpair
superpairs
tablist
terracottas
+ tessellator
thaumaturgist
thaumaturgy
townsquare
@@ -274,4 +284,4 @@
yolkar
-
+
\ No newline at end of file
diff --git a/.idea/liveTemplates/SkyHanni.xml b/.idea/liveTemplates/SkyHanni.xml
new file mode 100644
index 000000000000..08c6737b5ce2
--- /dev/null
+++ b/.idea/liveTemplates/SkyHanni.xml
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.live-plugins/module/plugin.kts b/.live-plugins/module/plugin.kts
index c0a65d3f431b..f7471e9fd98c 100644
--- a/.live-plugins/module/plugin.kts
+++ b/.live-plugins/module/plugin.kts
@@ -18,6 +18,7 @@ val forgeEvent = "SubscribeEvent"
val handleEvent = "HandleEvent"
val skyHanniModule = "SkyHanniModule"
+val skyhanniPath = "at.hannibal2.skyhanni"
val patternGroup = "at.hannibal2.skyhanni.utils.repopatterns.RepoPatternGroup"
val pattern = "java.util.regex.Pattern"
@@ -36,12 +37,17 @@ fun isRepoPattern(property: KtProperty): Boolean {
return false
}
+fun isFromSkyhanni(declaration: KtNamedDeclaration): Boolean {
+ return declaration.fqName?.asString()?.startsWith(skyhanniPath) ?: false
+}
+
class ModuleInspectionKotlin : AbstractKotlinInspection() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
val visitor = object : KtVisitorVoid() {
override fun visitClass(klass: KtClass) {
+ if (!isFromSkyhanni(klass)) return
val hasAnnotation = klass.annotationEntries.any { it.shortName?.asString() == skyHanniModule }
if (hasAnnotation) {
@@ -54,6 +60,7 @@ class ModuleInspectionKotlin : AbstractKotlinInspection() {
}
override fun visitObjectDeclaration(declaration: KtObjectDeclaration) {
+ if (!isFromSkyhanni(declaration)) return
val hasAnnotation = declaration.annotationEntries.any { it.shortName?.asString() == skyHanniModule }
if (hasAnnotation) return
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 57f22f2e2583..8229a00d5835 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -13,6 +13,7 @@ We use [IntelliJ](https://www.jetbrains.com/idea/) as an example.
- Download IntelliJ from the [JetBrains Website](https://www.jetbrains.com/idea/download/).
- Use the Community Edition. (Scroll down a bit.)
+- When you encounter any bug with IntelliJ, please make sure to use the version `2024.1.6`, not `2024.2.x` or above.
### Cloning the project
@@ -30,14 +31,66 @@ We use [IntelliJ](https://www.jetbrains.com/idea/) as an example.
### Setting up IntelliJ
-SkyHanni's Gradle configuration is very similar to the one used in **NotEnoughUpdates**, just
-follow [their guide](https://github.com/NotEnoughUpdates/NotEnoughUpdates/blob/master/CONTRIBUTING.md).
+
+Once your project is imported into IntelliJ from the previous step, all dependencies like Minecraft, NEU, and so on should be automatically
+downloaded. If not, you might need to link the Gradle project in the Gradle tab (little elephant) on the right.
+
+
+🖼️Show Gradle tab image
+
+![Gradle tab with Link Project and Gradle Settings highlighted](docs/gradle-tab.jpg)
+
+
+
+If importing fails, make sure the Gradle JVM (found in the settings wheel in the Gradle tab, or by searching Ctrl + Shift + A
+for "Gradle JVM") is set to a Java 21 JDK. While this is not the version of Java Minecraft 1.8.9 uses, we need this version for some of our
+build tools.
+
+
+🖼️Show Gradle JVM image
+
+![Gradle settings showing Java 21 being selected as JVM](docs/gradle-settings.png)
+
+
+
+After all importing is done (which might take a few minutes the first time you download the project), you should find a new IntelliJ run
+configuration.
+
+
+🖼️Show run configuration selection image
+
+![Where to select the run configuration](docs/minecraft-client.webp)
+
+
+
+That task might work out of the box, but very likely it will not. We will need to adjust the used Java version. Since Minecraft 1.8.9 uses
+Java 1.8, we will need to adjust the used JDK for running our Mod, as well as potentially changing the argument passing style.
+
+So select an appropriate Java 1.8 JDK (preferably [DCEVM](#hot-swap), but any Java 1.8 JDK or even JRE will do) and select None as the
+argument passing style.
+
+
+🖼️Show run configuration image
+
+![Run configuration settings](docs/run-configuration-settings.avif)
+
+
+
+Now that we are done with that, you should be able to launch your game from your IDE with that run configuration.
+
+SkyHanni's Gradle configuration is very similar to the one used in **NotEnoughUpdates**, so if you want to look at another guide, check
+out [their guide](https://github.com/NotEnoughUpdates/NotEnoughUpdates/blob/master/CONTRIBUTING.md).
## Creating a Pull Request
If you are not very familiar with git, you might want to try this out: https://learngitbranching.js.org/.
-_An explanation how to use intellij and branches will follow here soon._
+Proposed changes are better off being in their own branch, you can do this by doing the following from within IntelliJ with the SkyHanni project already open.
+- Click the beta dropdown at the top of IntelliJ
+- Click new branch
+- Give the branch a name relating to the changes you plan to make
+
+_A more in depth explanation how to use intellij and branches will follow here soon._
Please use a prefix for the name of the PR (E.g. Feature, Improvement, Fix, Backend, ...).
@@ -51,6 +104,13 @@ format like "- #821" to illustrate the dependency.
- Follow the [Hypixel Rules](https://hypixel.net/rules).
- Use the coding conventions for [Kotlin](https://kotlinlang.org/docs/coding-conventions.html)
and [Java](https://www.oracle.com/java/technologies/javase/codeconventions-contents.html).
+- **My build is failing due to `detekt`, what do I do?**
+ - `detekt` is our code quality tool. It checks for code smells and style issues.
+ - If you have a build failure stating `Analysis failed with ... weighted issues.`, you can check `versions/[target version]/build/reports/detekt/` for a comprehensive list of issues.
+ - **There are valid reasons to deviate from the norm**
+ - If you have such a case, either use `@Supress("rule_name")`, or re-build the `baseline.xml` file, using `./gradlew detektBaselineMain`.
+ After running detektBaselineMain, you should find a file called `baseline-main.xml` in the `version/1.8.9` folder, rename the file to
+ `baseline.xml` replacing the old one. You also should copy the new contents of this file to the [main baseline file](detekt/baseline.xml)
- Do not copy features from other mods. Exceptions:
- Mods that are paid to use.
- Mods that have reached their end of life. (Rip SBA, Dulkir and Soopy).
diff --git a/README.md b/README.md
index a7c00b495c57..3c7721cfb176 100644
--- a/README.md
+++ b/README.md
@@ -14,19 +14,19 @@
## What it does
-SkyHanni is a Forge mod for Minecraft 1.8.9 that adds many useful features to Hypixel SkyBlock. With SkyHanni, you'll get:
+SkyHanni is a Forge mod for Minecraft 1.8.9 that adds many useful features to [Hypixel SkyBlock](https://wiki.hypixel.net/Main_Page). With SkyHanni you have access to:
-* **Helpful GUIs:** Access important information at a glance.
-* **Extra Chat Messages:** Receive reminders and helpful tips.
-* **Message Hiders:** Control which messages you see in chat.
-* **Entity/Item Highlighters:** Focus on important mobs or items in the world/your inventory.
-* **[And much more!](docs/FEATURES.md)**
+* **Helpful GUIs:** View important information at a glance.
+* **Extra Chat Messages:** Receive reminders and tips at the right moment.
+* **Object Highlighters:** Focus on important items in inventories or highlight mobs in the world.
+* **Highly Customizable Displays:** Personalise your Scoreboard, Tab List or chat format.
+* [And **much** more!](docs/FEATURES.md)
-SkyHanni is especially helpful when doing activities like farming, slayers, Bingo, Diana, fishing, or Rift.
+SkyHanni is especially useful when doing farming, slayers, Bingo, Diana, fishing, Rift or mining.
## Getting Started
-1. **Install:** Check out the [installation guide](docs/INSTALLING.md).
+1. **Install:** Follow the [installation guide](docs/INSTALLING.md).
2. **Set Up:** Type `/sh` or `/skyhanni` in-game to configure your settings.
3. **Explore:** See all the features [here](docs/FEATURES.md).
@@ -34,17 +34,17 @@ SkyHanni is especially helpful when doing activities like farming, slayers, Bing
Give feedback or just chat with others on our community Discord!
-* **Bug Reports:** Use the `#bug-reports` channel.
-* **Feature Suggestions:** Use the `#suggestions` channel.
-* **General Chat:** Chat with other SkyHanni users in `#skyblock-general` channel.
+* **Bug Reports:** Use the `#bug-reports` channel when you find broken features (please check out `#faq` and `#known-bugs`).
+* **Quick Help** Ask in `#support` for questions and problems with the mod or Minecraft in general.
+* **Feature Suggestions:** Feel fre to tell your ideas in `#suggestions` channel for new features and improvements to the mod. (Don't copy from existing mods or break Hypixel rules).
+* **General Chat:** Chat with other SkyHanni users in `#skyblock-general` channel about the game.
-[Join the Discord](https://discord.gg/skyhanni-997079228510117908)
+**[Join the Discord!](https://discord.gg/skyhanni-997079228510117908)**
## Contributing
-Interested in writing your own SkyHanni feature or fixing that one annoying bug yourself? Check out our [contributing guide](CONTRIBUTING.md) for more information.
+Are you interested in writing your own SkyHanni feature? Do you want to fix that one annoying bug yourself? Check out our [contributing guide](CONTRIBUTING.md) for more information!
---
-**SkyHanni is part of an active modding community. Explore other useful mods [here](https://sbmw.ca/mod-lists/skyblock-mod-list/) to
-complete your SkyBlock setup!**
+**SkyHanni is part of an active modding community. Explore other useful mods [here](https://sbmw.ca/mod-lists/skyblock-mod-list/) for even more SkyBlock features!**
diff --git a/build.gradle.kts b/build.gradle.kts
index e8464ada27e8..cddef09fd3ca 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -3,9 +3,13 @@ import at.skyhanni.sharedvariables.MultiVersionStage
import at.skyhanni.sharedvariables.ProjectTarget
import at.skyhanni.sharedvariables.SHVersionInfo
import at.skyhanni.sharedvariables.versionString
+import io.gitlab.arturbosch.detekt.Detekt
+import io.gitlab.arturbosch.detekt.DetektCreateBaselineTask
import net.fabricmc.loom.task.RunGameTask
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
+import skyhannibuildsystem.ChangelogVerification
+import skyhannibuildsystem.DownloadBackupRepo
plugins {
idea
@@ -18,31 +22,12 @@ plugins {
kotlin("plugin.power-assert")
`maven-publish`
id("moe.nea.shot") version "1.0.0"
+ id("io.gitlab.arturbosch.detekt")
+ id("net.kyori.blossom")
}
val target = ProjectTarget.values().find { it.projectPath == project.path }!!
-repositories {
- mavenCentral()
- mavenLocal()
- maven("https://maven.minecraftforge.net") {
- metadataSources {
- artifact() // We love missing POMs
- }
- }
- maven("https://repo.spongepowered.org/maven/") // mixin
- maven("https://pkgs.dev.azure.com/djtheredstoner/DevAuth/_packaging/public/maven/v1") // DevAuth
- maven("https://jitpack.io") { // NotEnoughUpdates (compiled against)
- content {
- includeGroupByRegex("(com|io)\\.github\\..*")
- }
- }
- maven("https://repo.nea.moe/releases") // libautoupdate
- maven("https://maven.notenoughupdates.org/releases") // NotEnoughUpdates (dev env)
- maven("https://repo.hypixel.net/repository/Hypixel/") // mod-api
- maven("https://maven.teamresourceful.com/repository/thatgravyboat/") // DiscordIPC
-}
-
// Toolchains:
java {
toolchain.languageVersion.set(target.minecraftVersion.javaLanguageVersion)
@@ -56,11 +41,15 @@ val runDirectory = rootProject.file("run")
runDirectory.mkdirs()
// Minecraft configuration:
loom {
- if (this.isForgeLike)
+ if (this.isForgeLike) {
forge {
- pack200Provider.set(dev.architectury.pack200.java.Pack200Adapter())
+ pack200Provider.set(
+ dev.architectury.pack200.java
+ .Pack200Adapter(),
+ )
mixinConfig("mixins.skyhanni.json")
}
+ }
mixin {
useLegacyMixinAp.set(true)
defaultRefmapName.set("mixins.skyhanni.refmap.json")
@@ -84,11 +73,12 @@ loom {
}
}
-if (target == ProjectTarget.MAIN)
+if (target == ProjectTarget.MAIN) {
sourceSets.main {
resources.destinationDirectory.set(kotlin.destinationDirectory)
output.setResourcesDir(kotlin.destinationDirectory)
}
+}
val shadowImpl: Configuration by configurations.creating {
configurations.implementation.get().extendsFrom(this)
@@ -107,11 +97,26 @@ val headlessLwjgl by configurations.creating {
isTransitive = false
isVisible = false
}
+
+val includeBackupRepo by tasks.registering(DownloadBackupRepo::class) {
+ this.outputDirectory.set(layout.buildDirectory.dir("downloadedRepo"))
+ this.branch = "main"
+}
+
tasks.runClient {
- this.javaLauncher.set(javaToolchains.launcherFor {
- languageVersion.set(target.minecraftVersion.javaLanguageVersion)
- })
+ this.javaLauncher.set(
+ javaToolchains.launcherFor {
+ languageVersion.set(target.minecraftVersion.javaLanguageVersion)
+ },
+ )
+}
+
+tasks.register("checkPrDescription", ChangelogVerification::class) {
+ this.outputDirectory.set(layout.buildDirectory)
+ this.prTitle = project.findProperty("prTitle") as String
+ this.prBody = project.findProperty("prBody") as String
}
+
val shot = shots.shot("minecraft", rootProject.file("shots.txt"))
dependencies {
@@ -121,8 +126,9 @@ dependencies {
} else {
mappings(target.mappingDependency)
}
- if (target.forgeDep != null)
+ if (target.forgeDep != null) {
"forge"(target.forgeDep!!)
+ }
// Discord RPC client
shadowImpl("com.jagrosh:DiscordIPC:0.5.3") {
@@ -146,6 +152,9 @@ dependencies {
annotationProcessor("org.spongepowered:mixin:0.8.5-SNAPSHOT")
annotationProcessor("com.google.code.gson:gson:2.10.1")
annotationProcessor("com.google.guava:guava:17.0")
+ } else if (target == ProjectTarget.MODERN) {
+ modCompileOnly("net.fabricmc:fabric-loader:0.16.7")
+ modCompileOnly("net.fabricmc.fabric-api:fabric-api:0.102.0+1.21")
}
implementation(kotlin("stdlib-jdk8"))
@@ -153,15 +162,16 @@ dependencies {
exclude(group = "org.jetbrains.kotlin")
}
- modRuntimeOnly("me.djtheredstoner:DevAuth-forge-legacy:1.1.0")
+ if (target.isForge) modRuntimeOnly("me.djtheredstoner:DevAuth-forge-legacy:1.2.1")
+ else modRuntimeOnly("me.djtheredstoner:DevAuth-fabric:1.2.1")
modCompileOnly("com.github.hannibal002:notenoughupdates:4957f0b:all") {
exclude(module = "unspecified")
isTransitive = false
}
- // June 3, 2024, 9:30 PM AEST
- // https://github.com/NotEnoughUpdates/NotEnoughUpdates/tree/2.3.0
- devenvMod("com.github.NotEnoughUpdates:NotEnoughUpdates:2.3.0:all") {
+ // October 3, 2024, 11:43 PM AEST
+ // https://github.com/NotEnoughUpdates/NotEnoughUpdates/tree/2.4.0
+ devenvMod("com.github.NotEnoughUpdates:NotEnoughUpdates:2.4.0:all") {
exclude(module = "unspecified")
isTransitive = false
}
@@ -177,10 +187,17 @@ dependencies {
exclude(module = "unspecified")
isTransitive = false
}
- testImplementation("org.junit.jupiter:junit-jupiter:5.10.0")
+ testImplementation("org.junit.jupiter:junit-jupiter:5.11.0")
testImplementation("io.mockk:mockk:1.12.5")
implementation("net.hypixel:mod-api:0.3.1")
+
+ // getting clock offset
+ shadowImpl("commons-net:commons-net:3.8.0")
+
+ detektPlugins("org.notenoughupdates:detektrules:1.0.0")
+ detektPlugins(project(":detekt"))
+ detektPlugins("io.gitlab.arturbosch.detekt:detekt-formatting:1.23.7")
}
afterEvaluate {
@@ -207,6 +224,7 @@ kotlin {
// Tasks:
tasks.processResources {
+ from(includeBackupRepo)
inputs.property("version", version)
filesMatching(listOf("mcmod.info", "fabric.mod.json")) {
expand("version" to version)
@@ -231,10 +249,15 @@ if (target == ProjectTarget.MAIN) {
}
}
-if (target == ProjectTarget.MAIN)
+if (target == ProjectTarget.MAIN) {
tasks.compileJava {
dependsOn(tasks.processResources)
}
+}
+
+tasks.withType {
+ compilerOptions.jvmTarget.set(JvmTarget.fromTarget(target.minecraftVersion.formattedJavaLanguageVersion))
+}
if (target.parent == ProjectTarget.MAIN) {
val mainRes = project(ProjectTarget.MAIN.projectPath).tasks.getAt("processResources")
@@ -285,6 +308,7 @@ tasks.shadowJar {
relocate("io.github.notenoughupdates.moulconfig", "at.hannibal2.skyhanni.deps.moulconfig")
relocate("moe.nea.libautoupdate", "at.hannibal2.skyhanni.deps.libautoupdate")
relocate("com.jagrosh.discordipc", "at.hannibal2.skyhanni.deps.discordipc")
+ relocate("org.apache.commons.net", "at.hannibal2.skyhanni.deps.commons.net")
}
tasks.jar {
archiveClassifier.set("nodeps")
@@ -312,12 +336,19 @@ if (!MultiVersionStage.activeState.shouldCompile(target)) {
onlyIf { false }
}
}
+
preprocess {
vars.put("MC", target.minecraftVersion.versionNumber)
- vars.put("FORGE", if (target.forgeDep != null) 1 else 0)
+ vars.put("FORGE", if (target.isForge) 1 else 0)
+ vars.put("FABRIC", if (target.isFabric) 1 else 0)
vars.put("JAVA", target.minecraftVersion.javaVersion)
patternAnnotation.set("at.hannibal2.skyhanni.utils.compat.Pattern")
}
+
+blossom {
+ replaceToken("@MOD_VERSION@", version)
+}
+
val sourcesJar by tasks.creating(Jar::class) {
destinationDirectory.set(layout.buildDirectory.dir("badjars"))
archiveClassifier.set("src")
@@ -343,3 +374,32 @@ publishing.publications {
}
}
}
+
+detekt {
+ buildUponDefaultConfig = true // preconfigure defaults
+ config.setFrom(rootProject.layout.projectDirectory.file("detekt/detekt.yml")) // point to your custom config defining rules to run, overwriting default behavior
+ baseline = file(layout.projectDirectory.file("detekt/baseline.xml")) // a way of suppressing issues before introducing detekt
+ source.setFrom(project.sourceSets.named("main").map { it.allSource })
+}
+
+tasks.withType().configureEach {
+ onlyIf {
+ target == ProjectTarget.MAIN
+ }
+
+ reports {
+ html.required.set(true) // observe findings in your browser with structure and code snippets
+ xml.required.set(true) // checkstyle like format mainly for integrations like Jenkins
+ sarif.required.set(true) // standardized SARIF format (https://sarifweb.azurewebsites.net/) to support integrations with GitHub Code Scanning
+ md.required.set(true) // simple Markdown format
+ }
+}
+
+tasks.withType().configureEach {
+ jvmTarget = target.minecraftVersion.formattedJavaLanguageVersion
+ outputs.cacheIf { false } // Custom rules won't work if cached
+}
+tasks.withType().configureEach {
+ jvmTarget = target.minecraftVersion.formattedJavaLanguageVersion
+ outputs.cacheIf { false } // Custom rules won't work if cached
+}
diff --git a/buildSrc/build.gradle.kts b/buildSrc/build.gradle.kts
new file mode 100644
index 000000000000..505f438b98c2
--- /dev/null
+++ b/buildSrc/build.gradle.kts
@@ -0,0 +1,17 @@
+plugins {
+ `kotlin-dsl`
+}
+
+repositories {
+ mavenCentral()
+ maven("https://jitpack.io") {
+ content {
+ includeGroupByRegex("com\\.github\\..*")
+ }
+ }
+}
+
+dependencies {
+ implementation("org.jetbrains.kotlin:kotlin-stdlib")
+ implementation("com.github.SkyHanniStudios:SkyHanniChangelogBuilder:1.0.1")
+}
diff --git a/buildSrc/src/main/kotlin/skyhannibuildsystem/ChangelogVerification.kt b/buildSrc/src/main/kotlin/skyhannibuildsystem/ChangelogVerification.kt
new file mode 100644
index 000000000000..8ac54d6fc0b1
--- /dev/null
+++ b/buildSrc/src/main/kotlin/skyhannibuildsystem/ChangelogVerification.kt
@@ -0,0 +1,64 @@
+package skyhannibuildsystem
+
+import at.hannibal2.changelog.SkyHanniChangelogBuilder
+import org.gradle.api.DefaultTask
+import org.gradle.api.GradleException
+import org.gradle.api.file.DirectoryProperty
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.Internal
+import org.gradle.api.tasks.OutputDirectory
+import org.gradle.api.tasks.TaskAction
+import java.io.File
+
+abstract class ChangelogVerification : DefaultTask() {
+
+ @get:OutputDirectory
+ abstract val outputDirectory: DirectoryProperty
+
+ @Input
+ var prTitle: String = ""
+
+ @Input
+ var prBody: String = ""
+
+ @get:Internal
+ val prBodyLines get() = prBody.lines()
+
+ private val prLink = "ignored"
+ private val templateLocation = "https://github.com/hannibal002/SkyHanni/blob/beta/pull_request_template.md"
+
+ @TaskAction
+ fun scanChangelog() {
+ if (prBodyLines.contains("exclude_from_changelog")) {
+ println("PR is excluded from changelog verification")
+ return
+ }
+
+ val (changes, bodyErrors) = SkyHanniChangelogBuilder.findChanges(prBodyLines, prLink)
+ val titleErrors = SkyHanniChangelogBuilder.findPullRequestNameErrors(prTitle, changes)
+
+ if (bodyErrors.isEmpty() && titleErrors.isEmpty()) {
+ println("Changelog and title verification successful")
+ } else {
+ bodyErrors.forEach { println(it.message) }
+ titleErrors.forEach { println(it.message) }
+
+ // Export errors so that they can be listed in the PR comment
+ val errorFile = File(outputDirectory.get().asFile, "changelog_errors.txt")
+ println("saved error file to: ${errorFile.path}")
+
+ errorFile.appendText("I have detected some issues with your pull request:\n\n")
+
+ if (bodyErrors.isNotEmpty()) {
+ errorFile.appendText("Body issues:\n${bodyErrors.joinToString("\n") { it.formatLine() }}\n\n")
+ }
+ if (titleErrors.isNotEmpty()) {
+ errorFile.appendText("Title issues:\n${titleErrors.joinToString("\n") { it.message }}\n\n")
+ }
+
+ errorFile.appendText("Please fix these issues. For the correct format, refer to the [pull request template]($templateLocation).")
+
+ throw GradleException("Changelog verification failed")
+ }
+ }
+}
diff --git a/buildSrc/src/main/kotlin/skyhannibuildsystem/DownloadBackupRepo.kt b/buildSrc/src/main/kotlin/skyhannibuildsystem/DownloadBackupRepo.kt
new file mode 100644
index 000000000000..f59bf34a8c10
--- /dev/null
+++ b/buildSrc/src/main/kotlin/skyhannibuildsystem/DownloadBackupRepo.kt
@@ -0,0 +1,34 @@
+package skyhannibuildsystem
+
+import org.gradle.api.DefaultTask
+import org.gradle.api.file.DirectoryProperty
+import org.gradle.api.tasks.Input
+import org.gradle.api.tasks.Internal
+import org.gradle.api.tasks.OutputDirectory
+import org.gradle.api.tasks.TaskAction
+import java.net.URL
+
+// Code taken from NotEnoughUpdates
+abstract class DownloadBackupRepo : DefaultTask() {
+
+ @get:OutputDirectory
+ abstract val outputDirectory: DirectoryProperty
+
+ @get:Input
+ abstract var branch: String
+
+ @get:Internal
+ val repoFile get() = outputDirectory.get().asFile.resolve("assets/skyhanni/repo.zip")
+
+ @TaskAction
+ fun downloadRepo() {
+ val downloadUrl = URL("https://github.com/hannibal002/SkyHanni-Repo/archive/refs/heads/$branch.zip")
+ val file = repoFile
+ file.parentFile.mkdirs()
+ file.outputStream().use { out ->
+ downloadUrl.openStream().use { inp ->
+ inp.copyTo(out)
+ }
+ }
+ }
+}
diff --git a/detekt/baseline.xml b/detekt/baseline.xml
new file mode 100644
index 000000000000..338c7d66d3cf
--- /dev/null
+++ b/detekt/baseline.xml
@@ -0,0 +1,314 @@
+
+
+
+
+ ArrayPrimitive:CropMoneyDisplay.kt$CropMoneyDisplay$Array<Double>
+ ArrayPrimitive:CropMoneyDisplay.kt$CropMoneyDisplay$arrayOf(npcPrice)
+ ArrayPrimitive:CropMoneyDisplay.kt$CropMoneyDisplay$arrayOf(sellOffer)
+ ArrayPrimitive:LorenzVec.kt$Array<Double>
+ ArrayPrimitive:LorenzVec.kt$LorenzVec$Array<Double>
+ ArrayPrimitive:LorenzVec.kt$LorenzVec$Array<Float>
+ ArrayPrimitive:LorenzVec.kt$LorenzVec$arrayOf(x, y, z)
+ ArrayPrimitive:LorenzVec.kt$LorenzVec$arrayOf(x.toFloat(), y.toFloat(), z.toFloat())
+ CyclomaticComplexMethod:AdvancedPlayerList.kt$AdvancedPlayerList$fun newSorting(original: List<String>): List<String>
+ CyclomaticComplexMethod:CropMoneyDisplay.kt$CropMoneyDisplay$private fun calculateMoneyPerHour(debugList: MutableList<List<Any>>): Map<NEUInternalName, Array<Double>>
+ CyclomaticComplexMethod:CropMoneyDisplay.kt$CropMoneyDisplay$private fun drawDisplay(): List<List<Any>>
+ CyclomaticComplexMethod:DamageIndicatorManager.kt$DamageIndicatorManager$private fun checkThorn(realHealth: Long, realMaxHealth: Long): String?
+ CyclomaticComplexMethod:EstimatedItemValueCalculator.kt$EstimatedItemValueCalculator$private fun addEnchantments(stack: ItemStack, list: MutableList<String>): Double
+ CyclomaticComplexMethod:GardenBestCropTime.kt$GardenBestCropTime$fun drawBestDisplay(currentCrop: CropType?): List<List<Any>>
+ CyclomaticComplexMethod:GardenCropMilestoneDisplay.kt$GardenCropMilestoneDisplay$private fun drawProgressDisplay(crop: CropType): List<Renderable>
+ CyclomaticComplexMethod:GardenVisitorFeatures.kt$GardenVisitorFeatures$private fun readToolTip(visitor: VisitorAPI.Visitor, itemStack: ItemStack?, toolTip: MutableList<String>)
+ CyclomaticComplexMethod:GhostCounter.kt$GhostCounter$private fun drawDisplay()
+ CyclomaticComplexMethod:GraphEditor.kt$GraphEditor$private fun input()
+ CyclomaticComplexMethod:ItemDisplayOverlayFeatures.kt$ItemDisplayOverlayFeatures$private fun getStackTip(item: ItemStack): String?
+ CyclomaticComplexMethod:ItemNameResolver.kt$ItemNameResolver$internal fun getInternalNameOrNull(itemName: String): NEUInternalName?
+ CyclomaticComplexMethod:MinecraftConsoleFilter.kt$MinecraftConsoleFilter$override fun filter(event: LogEvent?): Filter.Result
+ CyclomaticComplexMethod:PacketTest.kt$PacketTest$private fun Packet<*>.print()
+ CyclomaticComplexMethod:ParkourHelper.kt$ParkourHelper$fun render(event: LorenzRenderWorldEvent)
+ CyclomaticComplexMethod:Renderable.kt$Renderable.Companion$internal fun shouldAllowLink(debug: Boolean = false, bypassChecks: Boolean): Boolean
+ CyclomaticComplexMethod:SkillProgress.kt$SkillProgress$private fun drawDisplay()
+ CyclomaticComplexMethod:VampireSlayerFeatures.kt$VampireSlayerFeatures$private fun EntityOtherPlayerMP.process()
+ CyclomaticComplexMethod:VisualWordGui.kt$VisualWordGui$override fun drawScreen(unusedX: Int, unusedY: Int, partialTicks: Float)
+ Filename:AreaChangeEvents.kt$at.hannibal2.skyhanni.events.skyblock.AreaChangeEvents.kt
+ InjectDispatcher:ClipboardUtils.kt$ClipboardUtils$IO
+ InjectDispatcher:GardenNextJacobContest.kt$GardenNextJacobContest$IO
+ InjectDispatcher:HypixelBazaarFetcher.kt$HypixelBazaarFetcher$IO
+ InjectDispatcher:MayorAPI.kt$MayorAPI$IO
+ LongMethod:CopyNearbyEntitiesCommand.kt$CopyNearbyEntitiesCommand$fun command(args: Array<String>)
+ LongMethod:CropMoneyDisplay.kt$CropMoneyDisplay$private fun drawDisplay(): List<List<Any>>
+ LongMethod:GhostCounter.kt$GhostCounter$private fun drawDisplay()
+ LongMethod:GraphEditor.kt$GraphEditor$private fun input()
+ LongMethod:ItemDisplayOverlayFeatures.kt$ItemDisplayOverlayFeatures$private fun getStackTip(item: ItemStack): String?
+ LongMethod:MinecraftConsoleFilter.kt$MinecraftConsoleFilter$override fun filter(event: LogEvent?): Filter.Result
+ LongMethod:RenderableTooltips.kt$RenderableTooltips$private fun drawHoveringText()
+ LongMethod:TabListRenderer.kt$TabListRenderer$private fun drawTabList()
+ LongMethod:VisualWordGui.kt$VisualWordGui$override fun drawScreen(unusedX: Int, unusedY: Int, partialTicks: Float)
+ LoopWithTooManyJumpStatements:AdvancedPlayerList.kt$AdvancedPlayerList$for
+ LoopWithTooManyJumpStatements:CropMoneyDisplay.kt$CropMoneyDisplay$for
+ LoopWithTooManyJumpStatements:CustomScoreboard.kt$CustomScoreboard$for
+ LoopWithTooManyJumpStatements:EstimatedItemValueCalculator.kt$EstimatedItemValueCalculator$for
+ LoopWithTooManyJumpStatements:GardenComposterInventoryFeatures.kt$GardenComposterInventoryFeatures$for
+ LoopWithTooManyJumpStatements:GardenVisitorFeatures.kt$GardenVisitorFeatures$for
+ LoopWithTooManyJumpStatements:IslandAreas.kt$IslandAreas$for
+ LoopWithTooManyJumpStatements:RiftBloodEffigies.kt$RiftBloodEffigies$for
+ LoopWithTooManyJumpStatements:SkyBlockItemModifierUtils.kt$SkyBlockItemModifierUtils$for
+ LoopWithTooManyJumpStatements:SkyHanniConfigSearchResetCommand.kt$SkyHanniConfigSearchResetCommand$for
+ LoopWithTooManyJumpStatements:SuperpairsClicksAlert.kt$SuperpairsClicksAlert$for
+ MapGetWithNotNullAssertionOperator:NavigationHelper.kt$NavigationHelper$distances[node]!!
+ MatchingDeclarationName:AreaChangeEvents.kt$ScoreboardAreaChangeEvent : SkyHanniEvent
+ MemberNameEqualsClassName:CaptureFarmingGear.kt$CaptureFarmingGear$fun captureFarmingGear()
+ MemberNameEqualsClassName:FameRanks.kt$FameRanks$var fameRanks = emptyMap<String, FameRank>() private set
+ MemberNameEqualsClassName:FirstMinionTier.kt$FirstMinionTier$fun firstMinionTier( otherItems: Map<NEUInternalName, Int>, minions: MutableMap<String, NEUInternalName>, tierOneMinions: MutableList<NEUInternalName>, tierOneMinionsDone: MutableSet<NEUInternalName>, )
+ MemberNameEqualsClassName:LastServers.kt$LastServers$private val lastServers = mutableMapOf<String, SimpleTimeMark>()
+ MemberNameEqualsClassName:PestSpawn.kt$PestSpawn$private fun pestSpawn(amount: Int, plotNames: List<String>, unknownAmount: Boolean)
+ MemberNameEqualsClassName:Shimmy.kt$Shimmy.Companion$private fun shimmy(source: Any?, fieldName: String): Any?
+ MemberNameEqualsClassName:TestBingo.kt$TestBingo$var testBingo = false
+ MemberNameEqualsClassName:Text.kt$Text$fun text(text: String, init: IChatComponent.() -> Unit = {})
+ NoNameShadowing:BucketedItemTrackerData.kt$BucketedItemTrackerData${ it.hidden = !it.hidden }
+ NoNameShadowing:BurrowWarpHelper.kt$BurrowWarpHelper${ it.startsWith("§bWarp to ") }
+ NoNameShadowing:ChunkedStat.kt$ChunkedStat.Companion${ it.showWhen() }
+ NoNameShadowing:ContributorManager.kt$ContributorManager${ it.isAllowed() }
+ NoNameShadowing:Graph.kt$Graph.Companion${ out.name("Name").value(it) }
+ NoNameShadowing:Graph.kt$Graph.Companion${ out.name("Tags") out.beginArray() for (tagName in it) { out.value(tagName) } out.endArray() }
+ NoNameShadowing:GraphEditorBugFinder.kt$GraphEditorBugFinder${ it.position.distanceSqToPlayer() }
+ NoNameShadowing:GuiOptionEditorUpdateCheck.kt$GuiOptionEditorUpdateCheck$width
+ NoNameShadowing:HoppityCollectionStats.kt$HoppityCollectionStats${ val displayAmount = it.amount.shortFormat() val operationFormat = when (milestoneType) { HoppityEggType.CHOCOLATE_SHOP_MILESTONE -> "spending" HoppityEggType.CHOCOLATE_FACTORY_MILESTONE -> "reaching" else -> "" // Never happens } // List indexing is weird existingLore[replaceIndex - 1] = "§7Obtained by $operationFormat §6$displayAmount" existingLore[replaceIndex] = "§7all-time §6Chocolate." return existingLore }
+ NoNameShadowing:HotmData.kt$HotmData.Companion${ it.setCurrent(it.getTotal()) }
+ NoNameShadowing:LorenzVec.kt$LorenzVec.Companion$pitch
+ NoNameShadowing:LorenzVec.kt$LorenzVec.Companion$yaw
+ NoNameShadowing:Renderable.kt$Renderable.Companion.<no name provided>$posX
+ NoNameShadowing:Renderable.kt$Renderable.Companion.<no name provided>$posY
+ NoNameShadowing:Renderable.kt$Renderable.Companion.<no name provided>${ it.value?.contains(textInput.textBox, ignoreCase = true) ?: true }
+ NoNameShadowing:RenderableUtils.kt$RenderableUtils${ it != null }
+ NoNameShadowing:ReplaceRomanNumerals.kt$ReplaceRomanNumerals${ it.isValidRomanNumeral() && it.removeFormatting().romanToDecimal() != 2000 }
+ NoNameShadowing:RepoManager.kt$RepoManager${ unsuccessfulConstants.add(it) }
+ NoNameShadowing:RepoPatternManager.kt$RepoPatternManager${ it == '.' }
+ NoNameShadowing:Shimmy.kt$Shimmy.Companion$source
+ NoNameShadowing:SkyHanniBucketedItemTracker.kt$SkyHanniBucketedItemTracker${ ItemPriceSource.entries[it.ordinal] }
+ ReturnCount:AnitaMedalProfit.kt$AnitaMedalProfit$private fun readItem(slot: Int, item: ItemStack, table: MutableList<DisplayTableEntry>)
+ ReturnCount:BingoNextStepHelper.kt$BingoNextStepHelper$private fun readDescription(description: String): NextStep?
+ ReturnCount:BroodmotherFeatures.kt$BroodmotherFeatures$private fun onStageUpdate()
+ ReturnCount:ChatPeek.kt$ChatPeek$@JvmStatic fun peek(): Boolean
+ ReturnCount:ChestValue.kt$ChestValue$private fun isValidStorage(): Boolean
+ ReturnCount:CollectionTracker.kt$CollectionTracker$fun command(args: Array<String>)
+ ReturnCount:CompactBingoChat.kt$CompactBingoChat$private fun onSkyBlockLevelUp(message: String): Boolean
+ ReturnCount:CrimsonMinibossRespawnTimer.kt$CrimsonMinibossRespawnTimer$private fun updateArea()
+ ReturnCount:CropMoneyDisplay.kt$CropMoneyDisplay$private fun drawDisplay(): List<List<Any>>
+ ReturnCount:DamageIndicatorManager.kt$DamageIndicatorManager$private fun checkThorn(realHealth: Long, realMaxHealth: Long): String?
+ ReturnCount:DamageIndicatorManager.kt$DamageIndicatorManager$private fun getCustomHealth( entityData: EntityData, health: Long, entity: EntityLivingBase, maxHealth: Long, ): String?
+ ReturnCount:EnchantParser.kt$EnchantParser$private fun parseEnchants( loreList: MutableList<String>, enchants: Map<String, Int>, chatComponent: IChatComponent?, )
+ ReturnCount:EstimatedItemValue.kt$EstimatedItemValue$private fun draw(stack: ItemStack): List<List<Any>>
+ ReturnCount:EstimatedItemValueCalculator.kt$EstimatedItemValueCalculator$private fun calculateStarPrice( internalName: NEUInternalName, inputStars: Int, ): Pair<EssenceItemUtils.EssenceUpgradePrice, Pair<Int, Int>>?
+ ReturnCount:FishingAPI.kt$FishingAPI$fun seaCreatureCount(entity: EntityArmorStand): Int
+ ReturnCount:GardenVisitorFeatures.kt$GardenVisitorFeatures$private fun showGui(): Boolean
+ ReturnCount:GraphEditor.kt$GraphEditor$private fun input()
+ ReturnCount:HideNotClickableItems.kt$HideNotClickableItems$private fun hideSalvage(chestName: String, stack: ItemStack): Boolean
+ ReturnCount:ItemDisplayOverlayFeatures.kt$ItemDisplayOverlayFeatures$private fun getStackTip(item: ItemStack): String?
+ ReturnCount:ItemNameResolver.kt$ItemNameResolver$internal fun getInternalNameOrNull(itemName: String): NEUInternalName?
+ ReturnCount:ItemPriceUtils.kt$ItemPriceUtils$fun NEUInternalName.getPriceOrNull( priceSource: ItemPriceSource = ItemPriceSource.BAZAAR_INSTANT_BUY, pastRecipes: List<PrimitiveRecipe> = emptyList(), ): Double?
+ ReturnCount:ItemUtils.kt$ItemUtils$private fun NEUInternalName.grabItemName(): String
+ ReturnCount:MinecraftConsoleFilter.kt$MinecraftConsoleFilter$override fun filter(event: LogEvent?): Filter.Result
+ ReturnCount:MiningEventTracker.kt$MiningEventTracker$private fun sendData(eventName: String, time: String?)
+ ReturnCount:MobDetection.kt$MobDetection$private fun entitySpawn(entity: EntityLivingBase, roughType: Mob.Type): Boolean
+ ReturnCount:MobFilter.kt$MobFilter$internal fun createSkyblockEntity(baseEntity: EntityLivingBase): MobResult
+ ReturnCount:MobFilter.kt$MobFilter$private fun armorStandOnlyMobs(baseEntity: EntityLivingBase, armorStand: EntityArmorStand): MobResult?
+ ReturnCount:MobFilter.kt$MobFilter$private fun exceptions(baseEntity: EntityLivingBase, nextEntity: EntityLivingBase?): MobResult?
+ ReturnCount:MobFinder.kt$MobFinder$private fun tryAddEntitySpider(entity: EntityLivingBase): EntityResult?
+ ReturnCount:MobFinder.kt$MobFinder$private fun tryAddRift(entity: EntityLivingBase): EntityResult?
+ ReturnCount:MultiFilter.kt$MultiFilter$fun matchResult(string: String): String?
+ ReturnCount:PacketTest.kt$PacketTest$private fun Packet<*>.print()
+ ReturnCount:PowderMiningChatFilter.kt$PowderMiningChatFilter$@Suppress("CyclomaticComplexMethod") fun block(message: String): String?
+ ReturnCount:PurseAPI.kt$PurseAPI$private fun getCause(diff: Double): PurseChangeCause
+ ReturnCount:QuestLoader.kt$QuestLoader$private fun addQuest(name: String, state: QuestState, needAmount: Int): Quest
+ ReturnCount:ShowFishingItemName.kt$ShowFishingItemName$fun inCorrectArea(): Boolean
+ ReturnCount:SkillAPI.kt$SkillAPI$fun onCommand(it: Array<String>)
+ ReturnCount:SkyHanniConfigSearchResetCommand.kt$SkyHanniConfigSearchResetCommand$private suspend fun setCommand(args: Array<String>): String
+ SpreadOperator:ItemUtils.kt$ItemUtils$(tag, displayName, *lore.toTypedArray())
+ SpreadOperator:LimboPlaytime.kt$LimboPlaytime$( itemID.getItemStack().item, ITEM_NAME, *createItemLore() )
+ SpreadOperator:Text.kt$Text$(*component.toTypedArray(), separator = separator)
+ TooManyFunctions:CollectionUtils.kt$CollectionUtils
+ TooManyFunctions:DailyQuestHelper.kt$DailyQuestHelper
+ TooManyFunctions:EstimatedItemValueCalculator.kt$EstimatedItemValueCalculator
+ TooManyFunctions:GuiRenderUtils.kt$GuiRenderUtils
+ TooManyFunctions:HypixelCommands.kt$HypixelCommands
+ TooManyFunctions:InventoryUtils.kt$InventoryUtils
+ TooManyFunctions:LocationUtils.kt$LocationUtils
+ TooManyFunctions:LorenzUtils.kt$LorenzUtils
+ TooManyFunctions:LorenzVec.kt$LorenzVec
+ TooManyFunctions:MobFinder.kt$MobFinder
+ TooManyFunctions:NumberUtil.kt$NumberUtil
+ TooManyFunctions:RegexUtils.kt$RegexUtils
+ TooManyFunctions:RenderUtils.kt$RenderUtils
+ TooManyFunctions:Renderable.kt$Renderable$Companion
+ TooManyFunctions:ScoreboardElements.kt$at.hannibal2.skyhanni.features.gui.customscoreboard.ScoreboardElements.kt
+ TooManyFunctions:ScoreboardEvent.kt$at.hannibal2.skyhanni.features.gui.customscoreboard.ScoreboardEvent.kt
+ TooManyFunctions:SkyBlockItemModifierUtils.kt$SkyBlockItemModifierUtils
+ TooManyFunctions:StringUtils.kt$StringUtils
+ UnsafeCallOnNullableType:BasketWaypoints.kt$BasketWaypoints$Basket.entries.minByOrNull { it.waypoint.distanceSqToPlayer() }!!
+ UnsafeCallOnNullableType:BasketWaypoints.kt$BasketWaypoints$notFoundBaskets.minByOrNull { it.waypoint.distanceSqToPlayer() }!!
+ UnsafeCallOnNullableType:BucketedItemTrackerData.kt$BucketedItemTrackerData$it.value[internalName]?.hidden!!
+ UnsafeCallOnNullableType:ChocolateFactoryDataLoader.kt$ChocolateFactoryDataLoader$upgradeCost!!
+ UnsafeCallOnNullableType:CollectionUtils.kt$CollectionUtils$this.merge(key, number, Double::plus)!!
+ UnsafeCallOnNullableType:CollectionUtils.kt$CollectionUtils$this.merge(key, number, Float::plus)!!
+ UnsafeCallOnNullableType:CollectionUtils.kt$CollectionUtils$this.merge(key, number, Int::plus)!!
+ UnsafeCallOnNullableType:CollectionUtils.kt$CollectionUtils$this.merge(key, number, Long::plus)!!
+ UnsafeCallOnNullableType:CombatUtils.kt$CombatUtils$a!!
+ UnsafeCallOnNullableType:CompactBestiaryChatMessage.kt$CompactBestiaryChatMessage$it.groups[1]!!
+ UnsafeCallOnNullableType:ConfigManager.kt$ConfigManager$file!!
+ UnsafeCallOnNullableType:CorpseTracker.kt$CorpseTracker$applicableKeys.first().key!!
+ UnsafeCallOnNullableType:CropMoneyDisplay.kt$CropMoneyDisplay$cropNames[internalName]!!
+ UnsafeCallOnNullableType:DailyMiniBossHelper.kt$DailyMiniBossHelper$getByDisplayName(name)!!
+ UnsafeCallOnNullableType:DamageIndicatorManager.kt$DamageIndicatorManager$data.deathLocation!!
+ UnsafeCallOnNullableType:DefaultConfigFeatures.kt$DefaultConfigFeatures$resetSuggestionState[cat]!!
+ UnsafeCallOnNullableType:DefaultConfigOptionGui.kt$DefaultConfigOptionGui$resetSuggestionState[cat]!!
+ UnsafeCallOnNullableType:DicerRngDropTracker.kt$DicerRngDropTracker$event.toolItem!!
+ UnsafeCallOnNullableType:DiscordStatus.kt$ownerRegex.find(colorlessLine)!!
+ UnsafeCallOnNullableType:DungeonAPI.kt$DungeonAPI$dungeonFloor!!
+ UnsafeCallOnNullableType:EasterEggWaypoints.kt$EasterEggWaypoints$EasterEgg.entries.minByOrNull { it.waypoint.distanceSqToPlayer() }!!
+ UnsafeCallOnNullableType:EasterEggWaypoints.kt$EasterEggWaypoints$notFoundEggs.minByOrNull { it.waypoint.distanceSqToPlayer() }!!
+ UnsafeCallOnNullableType:EntityMovementData.kt$EntityMovementData$entityLocation[entity]!!
+ UnsafeCallOnNullableType:EntityOutlineRenderer.kt$EntityOutlineRenderer$entityRenderCache.noXrayCache!!
+ UnsafeCallOnNullableType:EntityOutlineRenderer.kt$EntityOutlineRenderer$entityRenderCache.xrayCache!!
+ UnsafeCallOnNullableType:EntityOutlineRenderer.kt$EntityOutlineRenderer$frameToCopy!!
+ UnsafeCallOnNullableType:EntityOutlineRenderer.kt$EntityOutlineRenderer$frameToPaste!!
+ UnsafeCallOnNullableType:EntityOutlineRenderer.kt$EntityOutlineRenderer$isAntialiasing!!
+ UnsafeCallOnNullableType:EntityOutlineRenderer.kt$EntityOutlineRenderer$isFastRender!!
+ UnsafeCallOnNullableType:EntityOutlineRenderer.kt$EntityOutlineRenderer$isShaders!!
+ UnsafeCallOnNullableType:FFGuideGUI.kt$FFGuideGUI$currentCrop!!
+ UnsafeCallOnNullableType:FarmingContestAPI.kt$FarmingContestAPI$contestCrop!!
+ UnsafeCallOnNullableType:FarmingContestAPI.kt$FarmingContestAPI$contests[bracket]!!
+ UnsafeCallOnNullableType:FarmingContestAPI.kt$FarmingContestAPI$currentCrop!!
+ UnsafeCallOnNullableType:FarmingWeightDisplay.kt$FarmingWeightDisplay$weightPerCrop[CropType.CACTUS]!!
+ UnsafeCallOnNullableType:FarmingWeightDisplay.kt$FarmingWeightDisplay$weightPerCrop[CropType.SUGAR_CANE]!!
+ UnsafeCallOnNullableType:FeatureToggleProcessor.kt$FeatureToggleProcessor$latestCategory!!
+ UnsafeCallOnNullableType:FeatureTogglesByDefaultAdapter.kt$FeatureTogglesByDefaultAdapter$gson!!
+ UnsafeCallOnNullableType:FortuneUpgrades.kt$FortuneUpgrades$nextTalisman.upgradeCost?.first!!
+ UnsafeCallOnNullableType:GardenComposterUpgradesData.kt$GardenComposterUpgradesData$ComposterUpgrade.getByName(name)!!
+ UnsafeCallOnNullableType:GardenCropMilestoneDisplay.kt$GardenCropMilestoneDisplay$cultivatingData[crop]!!
+ UnsafeCallOnNullableType:GardenCropMilestonesCommunityFix.kt$GardenCropMilestonesCommunityFix$map[crop]!!
+ UnsafeCallOnNullableType:GardenPlotIcon.kt$GardenPlotIcon$originalStack[index]!!
+ UnsafeCallOnNullableType:GhostCounter.kt$GhostCounter$storage?.totalMF!!
+ UnsafeCallOnNullableType:Graph.kt$Graph.Companion$position!!
+ UnsafeCallOnNullableType:Graph.kt$distances.distances[end]!!
+ UnsafeCallOnNullableType:GriffinBurrowHelper.kt$GriffinBurrowHelper$particleBurrows[targetLocation]!!
+ UnsafeCallOnNullableType:HoppityCallWarning.kt$HoppityCallWarning$acceptUUID!!
+ UnsafeCallOnNullableType:IslandGraphs.kt$IslandGraphs$currentTarget!!
+ UnsafeCallOnNullableType:ItemBlink.kt$ItemBlink$offsets[item]!!
+ UnsafeCallOnNullableType:ItemPickupLog.kt$ItemPickupLog$listToCheckAgainst[key]!!
+ UnsafeCallOnNullableType:ItemPickupLog.kt$ItemPickupLog$listToCheckAgainst[key]?.second!!
+ UnsafeCallOnNullableType:ItemStackTypeAdapterFactory.kt$ItemStackTypeAdapterFactory$gson!!
+ UnsafeCallOnNullableType:ItemUtils.kt$ItemUtils$itemAmountCache[input]!!
+ UnsafeCallOnNullableType:JacobContestTimeNeeded.kt$JacobContestTimeNeeded$map[crop]!!
+ UnsafeCallOnNullableType:KSerializable.kt$KotlinTypeAdapterFactory$kotlinClass.memberProperties.find { it.name == param.name }!!
+ UnsafeCallOnNullableType:KSerializable.kt$KotlinTypeAdapterFactory$param.name!!
+ UnsafeCallOnNullableType:LorenzEvent.kt$LorenzEvent$this::class.simpleName!!
+ UnsafeCallOnNullableType:MinionFeatures.kt$MinionFeatures$newMinion!!
+ UnsafeCallOnNullableType:MobFinder.kt$MobFinder$floor6GiantsSeparateDelay[uuid]!!
+ UnsafeCallOnNullableType:NavigationHelper.kt$NavigationHelper$distances[node]!!
+ UnsafeCallOnNullableType:NumberUtil.kt$NumberUtil$romanSymbols[this]!!
+ UnsafeCallOnNullableType:PositionList.kt$PositionList$configLink!!
+ UnsafeCallOnNullableType:ReminderManager.kt$ReminderManager$storage[args.drop(1).first()]!!
+ UnsafeCallOnNullableType:ReminderManager.kt$ReminderManager$storage[args.first()]!!
+ UnsafeCallOnNullableType:RenderEntityOutlineEvent.kt$RenderEntityOutlineEvent$entitiesToChooseFrom!!
+ UnsafeCallOnNullableType:RenderEntityOutlineEvent.kt$RenderEntityOutlineEvent$entitiesToOutline!!
+ UnsafeCallOnNullableType:RenderGlobalHook.kt$RenderGlobalHook$camera!!
+ UnsafeCallOnNullableType:RenderLivingEntityHelper.kt$RenderLivingEntityHelper$entityColorCondition[entity]!!
+ UnsafeCallOnNullableType:RenderLivingEntityHelper.kt$RenderLivingEntityHelper$entityColorMap[entity]!!
+ UnsafeCallOnNullableType:RenderUtils.kt$RenderUtils$it.name!!
+ UnsafeCallOnNullableType:RepoManager.kt$RepoManager$latestRepoCommit!!
+ UnsafeCallOnNullableType:RepoUtils.kt$RepoUtils$file!!
+ UnsafeCallOnNullableType:SackAPI.kt$SackAPI$match.groups[1]!!
+ UnsafeCallOnNullableType:SackAPI.kt$SackAPI$match.groups[2]!!
+ UnsafeCallOnNullableType:SackAPI.kt$SackAPI$match.groups[3]!!
+ UnsafeCallOnNullableType:SackAPI.kt$SackAPI$oldData!!
+ UnsafeCallOnNullableType:SkyHanniBucketedItemTracker.kt$SkyHanniBucketedItemTracker$it.get(DisplayMode.SESSION).getItemsProp()[internalName]!!
+ UnsafeCallOnNullableType:SkyHanniBucketedItemTracker.kt$SkyHanniBucketedItemTracker$it.get(DisplayMode.TOTAL).getItemsProp()[internalName]!!
+ UnsafeCallOnNullableType:SkyHanniMod.kt$SkyHanniMod.Companion$Loader.instance().indexedModList[MODID]!!
+ UnsafeCallOnNullableType:SoopyGuessBurrow.kt$SoopyGuessBurrow$distance!!
+ UnsafeCallOnNullableType:SoopyGuessBurrow.kt$SoopyGuessBurrow$distance2!!
+ UnsafeCallOnNullableType:SoopyGuessBurrow.kt$SoopyGuessBurrow$firstParticlePoint?.distance(pos)!!
+ UnsafeCallOnNullableType:SoopyGuessBurrow.kt$SoopyGuessBurrow$lastParticlePoint2!!
+ UnsafeCallOnNullableType:SoopyGuessBurrow.kt$SoopyGuessBurrow$lastParticlePoint2?.distance(particlePoint!!)!!
+ UnsafeCallOnNullableType:SoopyGuessBurrow.kt$SoopyGuessBurrow$particlePoint!!
+ UnsafeCallOnNullableType:SoopyGuessBurrow.kt$SoopyGuessBurrow$particlePoint?.subtract(lastParticlePoint2!!)!!
+ UnsafeCallOnNullableType:SummoningSoulsName.kt$SummoningSoulsName$mobsName.getOrNull(nearestMob)!!
+ UnsafeCallOnNullableType:SuperpairsClicksAlert.kt$SuperpairsClicksAlert$match.groups[1]!!
+ UnsafeCallOnNullableType:TiaRelayWaypoints.kt$TiaRelayWaypoints$waypointName!!
+ UnsafeCallOnNullableType:Translator.kt$Translator$messageContentRegex.find(message)!!
+ UnsafeCallOnNullableType:TrevorFeatures.kt$TrevorFeatures$TrevorSolver.currentMob!!
+ UnsafeCallOnNullableType:TrevorSolver.kt$TrevorSolver$currentMob!!
+ UnsafeCallOnNullableType:TunnelsMaps.kt$TunnelsMaps$campfire.name!!
+ UnsafeCallOnNullableType:UpdateManager.kt$UpdateManager$potentialUpdate!!
+ UnsafeCallOnNullableType:VisitorRewardWarning.kt$VisitorRewardWarning$visitor.totalPrice!!
+ UnsafeCallOnNullableType:VisitorRewardWarning.kt$VisitorRewardWarning$visitor.totalReward!!
+ UnusedParameter:SkyHanniDebugsAndTests.kt$SkyHanniDebugsAndTests$args: Array<String>
+ UseIsNullOrEmpty:ItemUtils.kt$ItemUtils$name == null || name.isEmpty()
+ UseOrEmpty:SkyHanniDebugsAndTests.kt$SkyHanniDebugsAndTests$event.originalOre?.let { "$it " } ?: ""
+ VarCouldBeVal:AdvancedPlayerList.kt$AdvancedPlayerList$private var randomOrderCache = TimeLimitedCache<String, Int>(20.minutes)
+ VarCouldBeVal:BestiaryData.kt$BestiaryData$private var indexes = listOf( 10, 11, 12, 13, 14, 15, 16, 19, 20, 21, 22, 23, 24, 25, 28, 29, 30, 31, 32, 33, 34, 37, 38, 39, 40, 41, 42, 43 )
+ VarCouldBeVal:BucketedItemTrackerData.kt$BucketedItemTrackerData$@Expose private var bucketedItems: MutableMap<E, MutableMap<NEUInternalName, TrackedItem>> = HashMap()
+ VarCouldBeVal:CarnivalZombieShootout.kt$CarnivalZombieShootout$private var lastUpdate = Updates(SimpleTimeMark.farPast(), SimpleTimeMark.farPast())
+ VarCouldBeVal:ChocolateFactoryStrayTracker.kt$ChocolateFactoryStrayTracker$private var claimedStraysSlots = mutableListOf<Int>()
+ VarCouldBeVal:CompactBestiaryChatMessage.kt$CompactBestiaryChatMessage$private var bestiaryDescription = mutableListOf<String>()
+ VarCouldBeVal:CraftRoomHolographicMob.kt$CraftRoomHolographicMob$private var entityToHolographicEntity = mapOf( EntityZombie::class.java to HolographicEntities.zombie, EntitySlime::class.java to HolographicEntities.slime, EntityCaveSpider::class.java to HolographicEntities.caveSpider, )
+ VarCouldBeVal:CustomWardrobe.kt$CustomWardrobe$private var guiName = "Custom Wardrobe"
+ VarCouldBeVal:Enchant.kt$Enchant$@Expose private var goodLevel = 0
+ VarCouldBeVal:Enchant.kt$Enchant$@Expose private var maxLevel = 0
+ VarCouldBeVal:Enchant.kt$Enchant.Stacking$@Expose private var nbtNum: String? = null
+ VarCouldBeVal:Enchant.kt$Enchant.Stacking$@Expose private var stackLevel: TreeSet<Int>? = null
+ VarCouldBeVal:ErrorManager.kt$ErrorManager$private var cache = TimeLimitedSet<Pair<String, Int>>(10.minutes)
+ VarCouldBeVal:EstimatedItemValueCalculator.kt$EstimatedItemValueCalculator$var comboPrice = combo.asInternalName().getPriceOrNull()
+ VarCouldBeVal:EstimatedItemValueCalculator.kt$EstimatedItemValueCalculator$var internalName = removeKuudraArmorPrefix(stack.getInternalName().asString().removePrefix("VANQUISHED_"))
+ VarCouldBeVal:EstimatedItemValueCalculator.kt$EstimatedItemValueCalculator$var nameColor = if (!useless) "§9" else "§7"
+ VarCouldBeVal:ExperimentsProfitTracker.kt$ExperimentsProfitTracker$private var currentBottlesInInventory = mutableMapOf<NEUInternalName, Int>()
+ VarCouldBeVal:ExperimentsProfitTracker.kt$ExperimentsProfitTracker$private var lastBottlesInInventory = mutableMapOf<NEUInternalName, Int>()
+ VarCouldBeVal:FarmingWeightDisplay.kt$FarmingWeightDisplay$private var nextPlayers = mutableListOf<UpcomingLeaderboardPlayer>()
+ VarCouldBeVal:FlareDisplay.kt$FlareDisplay$private var flares = mutableListOf<Flare>()
+ VarCouldBeVal:FontRendererHook.kt$FontRendererHook$private var CHROMA_COLOR: Int = -0x1
+ VarCouldBeVal:FontRendererHook.kt$FontRendererHook$private var CHROMA_COLOR_SHADOW: Int = -0xAAAAAB
+ VarCouldBeVal:GardenPlotIcon.kt$GardenPlotIcon$private var cachedStack = mutableMapOf<Int, ItemStack>()
+ VarCouldBeVal:GardenPlotIcon.kt$GardenPlotIcon$private var originalStack = mutableMapOf<Int, ItemStack>()
+ VarCouldBeVal:GardenPlotMenuHighlighting.kt$GardenPlotMenuHighlighting$private var highlightedPlots = mutableMapOf<GardenPlotAPI.Plot, PlotStatusType>()
+ VarCouldBeVal:GardenVisitorColorNames.kt$GardenVisitorColorNames$private var visitorColors = mutableMapOf<String, String>() // name -> color code
+ VarCouldBeVal:GhostData.kt$GhostData$private var session = mutableMapOf( Option.KILLS to 0.0, Option.SORROWCOUNT to 0.0, Option.VOLTACOUNT to 0.0, Option.PLASMACOUNT to 0.0, Option.GHOSTLYBOOTS to 0.0, Option.BAGOFCASH to 0.0, Option.TOTALDROPS to 0.0, Option.SCAVENGERCOINS to 0.0, Option.MAXKILLCOMBO to 0.0, Option.SKILLXPGAINED to 0.0 )
+ VarCouldBeVal:GraphEditor.kt$GraphEditor$var vector = LocationUtils.calculatePlayerFacingDirection()
+ VarCouldBeVal:HoppityCollectionStats.kt$HoppityCollectionStats$private var highlightMap = mutableMapOf<String, LorenzColor>()
+ VarCouldBeVal:HoppityEggLocations.kt$HoppityEggLocations$// TODO add gui/command to show total data/missing islands private var collectedEggStorage: MutableMap<IslandType, MutableSet<LorenzVec>> get() = ChocolateFactoryAPI.profileStorage?.collectedEggLocations ?: mutableMapOf() set(value) { ChocolateFactoryAPI.profileStorage?.collectedEggLocations = value }
+ VarCouldBeVal:HoppityNpc.kt$HoppityNpc$private var slotsToHighlight = mutableSetOf<Int>()
+ VarCouldBeVal:IslandAreas.kt$IslandAreas$var suffix = ""
+ VarCouldBeVal:ItemPickupLog.kt$ItemPickupLog$private var itemList = mutableMapOf<Int, Pair<ItemStack, Int>>()
+ VarCouldBeVal:ItemPickupLog.kt$ItemPickupLog$private var itemsAddedToInventory = mutableMapOf<Int, PickupEntry>()
+ VarCouldBeVal:ItemPickupLog.kt$ItemPickupLog$private var itemsRemovedFromInventory = mutableMapOf<Int, PickupEntry>()
+ VarCouldBeVal:LocationUtils.kt$LocationUtils$var yaw = LocationUtils.calculatePlayerYaw() + 180
+ VarCouldBeVal:LorenzLogger.kt$LorenzLogger.Companion$private var LOG_DIRECTORY = File("config/skyhanni/logs")
+ VarCouldBeVal:MobDetection.kt$MobDetection$private var shouldClear: AtomicBoolean = AtomicBoolean(false)
+ VarCouldBeVal:MobFinder.kt$MobFinder$private var floor2summonsDiedOnce = mutableListOf<EntityOtherPlayerMP>()
+ VarCouldBeVal:MobFinder.kt$MobFinder$private var floor6GiantsSeparateDelay = mutableMapOf<UUID, Pair<Long, BossType>>()
+ VarCouldBeVal:MobFinder.kt$MobFinder$private var guardians = mutableListOf<EntityGuardian>()
+ VarCouldBeVal:NeuReforgeJson.kt$NeuReforgeJson$private lateinit var itemTypeField: Pair<String, List<NEUInternalName>>
+ VarCouldBeVal:PowerStoneGuideFeatures.kt$PowerStoneGuideFeatures$private var missing = mutableMapOf<Int, NEUInternalName>()
+ VarCouldBeVal:PunchcardHighlight.kt$PunchcardHighlight$private var playerQueue = mutableListOf<String>()
+ VarCouldBeVal:QuiverWarning.kt$QuiverWarning$private var arrowsInInstance = mutableSetOf<ArrowType>()
+ VarCouldBeVal:ReforgeHelper.kt$ReforgeHelper$private var waitForChat = AtomicBoolean(false)
+ VarCouldBeVal:ReminderManager.kt$ReminderManager$private var listPage = 1
+ VarCouldBeVal:RepoPatternGui.kt$RepoPatternGui$private var searchCache = ObservableList(mutableListOf<RepoPatternInfo>())
+ VarCouldBeVal:RepoPatternManager.kt$RepoPatternManager$/** * Map containing all keys and their repo patterns. Used for filling in new regexes after an update, and for * checking duplicate registrations. */ private var usedKeys: NavigableMap<String, CommonPatternInfo<*, *>> = TreeMap()
+ VarCouldBeVal:RepoPatternManager.kt$RepoPatternManager$/** * Map containing the exclusive owner of a regex key */ private var exclusivity: MutableMap<String, RepoPatternKeyOwner> = mutableMapOf()
+ VarCouldBeVal:SeaCreatureFeatures.kt$SeaCreatureFeatures$private var entityIds = TimeLimitedSet<Int>(6.minutes)
+ VarCouldBeVal:SeaCreatureFeatures.kt$SeaCreatureFeatures$private var rareSeaCreatures = TimeLimitedSet<Mob>(6.minutes)
+ VarCouldBeVal:ShowFishingItemName.kt$ShowFishingItemName$private var itemsOnGround = TimeLimitedCache<EntityItem, String>(750.milliseconds)
+ VarCouldBeVal:SkyblockGuideHighlightFeature.kt$SkyblockGuideHighlightFeature.Companion$private var missing = mutableSetOf<Int>()
+ VarCouldBeVal:SlayerItemsOnGround.kt$SlayerItemsOnGround$private var itemsOnGround = TimeLimitedCache<EntityItem, String>(2.seconds)
+ VarCouldBeVal:SoopyGuessBurrow.kt$SoopyGuessBurrow$private var dingSlope = mutableListOf<Float>()
+ VarCouldBeVal:SoopyGuessBurrow.kt$SoopyGuessBurrow$private var locations = mutableListOf<LorenzVec>()
+ VarCouldBeVal:TiaRelayHelper.kt$TiaRelayHelper$private var resultDisplay = mutableMapOf<Int, Int>()
+ VarCouldBeVal:TiaRelayHelper.kt$TiaRelayHelper$private var sounds = mutableMapOf<Int, Sound>()
+ VarCouldBeVal:TrevorFeatures.kt$TrevorFeatures$private var backupTrapperID: Int = 17
+ VarCouldBeVal:TrevorFeatures.kt$TrevorFeatures$private var trapperID: Int = 56
+ VarCouldBeVal:TrophyFishDisplay.kt$TrophyFishDisplay$private var recentlyDroppedTrophies = TimeLimitedCache<NEUInternalName, TrophyRarity>(5.seconds)
+ VarCouldBeVal:UserLuckBreakdown.kt$UserLuckBreakdown$private var fillerID = "STAINED_GLASS_PANE".asInternalName()
+ VarCouldBeVal:UserLuckBreakdown.kt$UserLuckBreakdown$private var limboID = "ENDER_PEARL".asInternalName()
+ VarCouldBeVal:UserLuckBreakdown.kt$UserLuckBreakdown$private var skillOverflowLuck = mutableMapOf<SkillType, Int>()
+ VarCouldBeVal:UserLuckBreakdown.kt$UserLuckBreakdown$private var skillsID = "DIAMOND_SWORD".asInternalName()
+
+
diff --git a/detekt/build.gradle.kts b/detekt/build.gradle.kts
new file mode 100644
index 000000000000..ce0de9ace8ce
--- /dev/null
+++ b/detekt/build.gradle.kts
@@ -0,0 +1,13 @@
+plugins {
+ kotlin("jvm")
+ id("com.google.devtools.ksp")
+}
+
+dependencies {
+ implementation("io.gitlab.arturbosch.detekt:detekt-api:1.23.7")
+ ksp(libs.autoservice.ksp)
+ implementation(libs.autoservice.annotations)
+ implementation("io.gitlab.arturbosch.detekt:detekt-formatting:1.23.7")
+ testImplementation("io.kotest:kotest-assertions-core:5.9.1")
+ testImplementation("io.gitlab.arturbosch.detekt:detekt-test:1.23.7")
+}
diff --git a/detekt/detekt.yml b/detekt/detekt.yml
new file mode 100644
index 000000000000..014ca8c5a309
--- /dev/null
+++ b/detekt/detekt.yml
@@ -0,0 +1,134 @@
+
+config:
+ validation: true
+
+GrammarRules:
+ active: true
+ AvoidBritishSpelling: # custom rule to prefer american spellings over british ones
+ active: true
+
+FormattingRules:
+ active: true
+ CustomCommentSpacing:
+ active: true
+
+ImportRules:
+ active: true
+ CustomImportOrdering:
+ active: true
+
+
+style:
+ MagicNumber: # I, Linnea Gräf, of sound mind and body, disagree with disabling this rule
+ active: false
+ UnusedParameter:
+ active: true
+ ignoreAnnotated:
+ - 'SubscribeEvent'
+ - 'HandleEvent'
+ - 'Mod.EventHandler'
+ ReturnCount:
+ active: true
+ max: 5
+ excludeGuardClauses: true
+ ignoreAnnotated:
+ - 'SubscribeEvent'
+ - 'HandleEvent'
+ - 'Mod.EventHandler'
+ MaxLineLength:
+ active: true
+ maxLineLength: 140
+ excludeCommentStatements: true
+ LoopWithTooManyJumpStatements:
+ active: true
+ maxJumpCount: 3
+ UnnecessaryAbstractClass: # gets horrendously messed up with Event classes
+ active: false
+ UnusedPrivateMember: # gets tripped up by API methods
+ active: false
+ UnusedPrivateProperty: # loops that don't use their iterator
+ active: true
+ allowedNames: "^(unused|_)$"
+ UseCheckOrError:
+ active: false
+ ForbiddenComment: # every TODO gets flagged
+ active: false
+ DestructuringDeclarationWithTooManyEntries: # too aggressive
+ active: true
+ maxDestructuringEntries: 5
+
+formatting:
+ MaximumLineLength: # ktlint - handled by detekt
+ active: false
+ MultiLineIfElse:
+ active: false
+ ArgumentListWrapping: # ktlint - way too aggressive
+ active: false
+ NoBlankLineBeforeRbrace: # pedantic
+ active: false
+ NoConsecutiveBlankLines: # pedantic
+ active: false
+ NoEmptyFirstLineInMethodBlock: # pedantic
+ active: false
+ ParameterListWrapping: # pedantic, can be useful in compact code
+ active: false
+ CommentSpacing: # handled by custom rule
+ active: false
+ SpacingBetweenDeclarationsWithAnnotations: # nah
+ active: false
+ SpacingBetweenDeclarationsWithComments: # also nah
+ active: false
+ ImportOrdering: # handled by custom rule
+ active: false
+
+complexity:
+ CyclomaticComplexMethod: # default threshold of 15, caught almost every complex method
+ active: true
+ threshold: 25
+ ignoreAnnotated:
+ - 'SubscribeEvent'
+ - 'HandleEvent'
+ - 'Mod.EventHandler'
+ LongParameterList: # too aggressive, classes can need a lot of params
+ active: false
+ NestedBlockDepth: # too aggressive
+ active: false
+ TooManyFunctions: # ktlint - also way too aggressive by default (11 on all file types)
+ active: true
+ thresholdInFiles: 15
+ thresholdInClasses: 20
+ thresholdInInterfaces: 20
+ thresholdInObjects: 20
+ thresholdInEnums: 11
+ ignoreAnnotated:
+ - 'SkyHanniModule'
+ ComplexCondition: # aggressive by default, at a complexity of 4
+ active: true
+ threshold: 6
+ LongMethod: # default max length of 60, caught way too much
+ active: true
+ threshold: 100
+ ignoreAnnotated:
+ - 'SubscribeEvent'
+ - 'HandleEvent'
+ - 'Mod.EventHandler'
+
+exceptions:
+ SwallowedException: # there are valid reasons to do this
+ active: false
+ ThrowingExceptionsWithoutMessageOrCause: # again, valid reasons
+ active: false
+ TooGenericExceptionCaught: # sometimes you just need to catch Exception
+ active: false
+ TooGenericExceptionThrown: # we don't have our own custom exceptions
+ active: false
+
+naming:
+ ConstructorParameterNaming: # pedantic
+ active: false
+
+potential-bugs:
+ DoubleMutabilityForCollection: # went crazy about all the mutable collections
+ active: false
+ HasPlatformType: # false positives on config get() methods
+ active: false
diff --git a/detekt/src/main/kotlin/PreprocessingPatterns.kt b/detekt/src/main/kotlin/PreprocessingPatterns.kt
new file mode 100644
index 000000000000..695f088cfe22
--- /dev/null
+++ b/detekt/src/main/kotlin/PreprocessingPatterns.kt
@@ -0,0 +1,19 @@
+package at.hannibal2.skyhanni.detektrules
+
+enum class PreprocessingPattern(val text: String) {
+ IF("#if"),
+ ELSE("#else"),
+ ELSEIF("#elseif"),
+ ENDIF("#endif"),
+ DOLLAR_DOLLAR("$$"),
+ ;
+
+ val asComment: String
+ get() = "//$text"
+
+ companion object {
+ fun String.containsPreprocessingPattern(): Boolean {
+ return entries.any { this.contains(it.text) }
+ }
+ }
+}
diff --git a/detekt/src/main/kotlin/formatting/CustomCommentSpacing.kt b/detekt/src/main/kotlin/formatting/CustomCommentSpacing.kt
new file mode 100644
index 000000000000..db9f2f8a75d4
--- /dev/null
+++ b/detekt/src/main/kotlin/formatting/CustomCommentSpacing.kt
@@ -0,0 +1,43 @@
+package at.hannibal2.skyhanni.detektrules.formatting
+
+import at.hannibal2.skyhanni.detektrules.PreprocessingPattern.Companion.containsPreprocessingPattern
+import io.gitlab.arturbosch.detekt.api.CodeSmell
+import io.gitlab.arturbosch.detekt.api.Config
+import io.gitlab.arturbosch.detekt.api.Debt
+import io.gitlab.arturbosch.detekt.api.Entity
+import io.gitlab.arturbosch.detekt.api.Issue
+import io.gitlab.arturbosch.detekt.api.Rule
+import io.gitlab.arturbosch.detekt.api.Severity
+import org.jetbrains.kotlin.com.intellij.psi.PsiComment
+
+class CustomCommentSpacing(config: Config) : Rule(config) {
+ override val issue = Issue(
+ "CustomCommentSpacing",
+ Severity.Style,
+ "Enforces custom spacing rules for comments.",
+ Debt.FIVE_MINS
+ )
+
+
+ override fun visitComment(comment: PsiComment) {
+ if (comment.text.containsPreprocessingPattern()) return
+
+ /**
+ * REGEX-TEST: // Test comment
+ * REGEX-TEST: /* Test comment */
+ */
+ val commentRegex = Regex("""^(?:\/{2}|\/\*)(?:\s.*|$)""", RegexOption.DOT_MATCHES_ALL)
+ if (!commentRegex.matches(comment.text)) {
+ report(
+ CodeSmell(
+ issue,
+ Entity.from(comment),
+ "Expected space after opening comment."
+ )
+ )
+ }
+
+ // Fallback to super (ostensibly a no-check)
+ super.visitComment(comment)
+ }
+}
diff --git a/detekt/src/main/kotlin/formatting/FormattingRuleSetProvider.kt b/detekt/src/main/kotlin/formatting/FormattingRuleSetProvider.kt
new file mode 100644
index 000000000000..a0a969bcf503
--- /dev/null
+++ b/detekt/src/main/kotlin/formatting/FormattingRuleSetProvider.kt
@@ -0,0 +1,17 @@
+package at.hannibal2.skyhanni.detektrules.formatting
+
+import com.google.auto.service.AutoService
+import io.gitlab.arturbosch.detekt.api.Config
+import io.gitlab.arturbosch.detekt.api.RuleSet
+import io.gitlab.arturbosch.detekt.api.RuleSetProvider
+
+@AutoService(RuleSetProvider::class)
+class FormattingRuleSetProvider : RuleSetProvider {
+ override val ruleSetId: String = "FormattingRules"
+
+ override fun instance(config: Config): RuleSet {
+ return RuleSet(ruleSetId, listOf(
+ CustomCommentSpacing(config)
+ ))
+ }
+}
diff --git a/detekt/src/main/kotlin/grammar/AvoidBritishSpelling.kt b/detekt/src/main/kotlin/grammar/AvoidBritishSpelling.kt
new file mode 100644
index 000000000000..6a4ea3d61eb4
--- /dev/null
+++ b/detekt/src/main/kotlin/grammar/AvoidBritishSpelling.kt
@@ -0,0 +1,46 @@
+package at.hannibal2.skyhanni.detektrules.grammar
+
+import io.gitlab.arturbosch.detekt.api.CodeSmell
+import io.gitlab.arturbosch.detekt.api.Config
+import io.gitlab.arturbosch.detekt.api.Debt
+import io.gitlab.arturbosch.detekt.api.Entity
+import io.gitlab.arturbosch.detekt.api.Issue
+import io.gitlab.arturbosch.detekt.api.Rule
+import io.gitlab.arturbosch.detekt.api.Severity
+import org.jetbrains.kotlin.psi.KtStringTemplateExpression
+
+/**
+ * This rule reports all usages of the british spelling over the american spelling in the codebase,
+ * this will ignore any type annotations, i.e., `@ConfigEditorColour` will not be reported.
+ */
+class AvoidBritishSpelling(config: Config) : Rule(config) {
+ override val issue = Issue(
+ "AvoidBritishSpelling",
+ Severity.Style,
+ "Avoid using the word british spelling over american spelling.",
+ Debt.FIVE_MINS,
+ )
+
+ private val scannedWords = mapOf(
+ "colour" to "color",
+ "armour" to "armor",
+ )
+
+ override fun visitStringTemplateExpression(expression: KtStringTemplateExpression) {
+ val text =
+ expression.text // Be aware .getText() returns the entire span of this template, including variable names contained within. This should be rare enough of a problem for us to not care about it.
+
+ for (word in scannedWords) {
+ if (text.contains(word.key, ignoreCase = true)) {
+ report(
+ CodeSmell(
+ issue,
+ Entity.from(expression),
+ "Avoid using the word '${word.key}' in code, '${word.value}' is preferred instead.",
+ ),
+ )
+ }
+ }
+ super.visitStringTemplateExpression(expression)
+ }
+}
diff --git a/detekt/src/main/kotlin/grammar/GrammarRuleSetProvider.kt b/detekt/src/main/kotlin/grammar/GrammarRuleSetProvider.kt
new file mode 100644
index 000000000000..8001fe42b5b2
--- /dev/null
+++ b/detekt/src/main/kotlin/grammar/GrammarRuleSetProvider.kt
@@ -0,0 +1,20 @@
+package at.hannibal2.skyhanni.detektrules.grammar
+
+import com.google.auto.service.AutoService
+import io.gitlab.arturbosch.detekt.api.Config
+import io.gitlab.arturbosch.detekt.api.RuleSet
+import io.gitlab.arturbosch.detekt.api.RuleSetProvider
+
+@AutoService(RuleSetProvider::class)
+class GrammarRuleSetProvider : RuleSetProvider {
+ override val ruleSetId: String = "GrammarRules"
+
+ override fun instance(config: Config): RuleSet {
+ return RuleSet(
+ ruleSetId,
+ listOf(
+ AvoidBritishSpelling(config),
+ ),
+ )
+ }
+}
diff --git a/detekt/src/main/kotlin/imports/CustomImportOrdering.kt b/detekt/src/main/kotlin/imports/CustomImportOrdering.kt
new file mode 100644
index 000000000000..9e0302153d0a
--- /dev/null
+++ b/detekt/src/main/kotlin/imports/CustomImportOrdering.kt
@@ -0,0 +1,114 @@
+package at.hannibal2.skyhanni.detektrules.imports
+
+import at.hannibal2.skyhanni.detektrules.PreprocessingPattern
+import at.hannibal2.skyhanni.detektrules.PreprocessingPattern.Companion.containsPreprocessingPattern
+import io.gitlab.arturbosch.detekt.api.CodeSmell
+import io.gitlab.arturbosch.detekt.api.Config
+import io.gitlab.arturbosch.detekt.api.Debt
+import io.gitlab.arturbosch.detekt.api.Entity
+import io.gitlab.arturbosch.detekt.api.Issue
+import io.gitlab.arturbosch.detekt.api.Rule
+import io.gitlab.arturbosch.detekt.api.Severity
+import org.jetbrains.kotlin.psi.KtImportDirective
+import org.jetbrains.kotlin.psi.KtImportList
+
+class CustomImportOrdering(config: Config) : Rule(config) {
+ override val issue = Issue(
+ "CustomImportOrdering",
+ Severity.Style,
+ "Enforces correct import ordering, taking into account preprocessed imports.",
+ Debt.FIVE_MINS,
+ )
+
+ companion object {
+ private val importOrder = ImportSorter()
+
+ private val packageImportOrdering = listOf("java.", "javax.", "kotlin.")
+
+ private class ImportSorter : Comparator {
+ override fun compare(
+ import1: KtImportDirective,
+ import2: KtImportDirective,
+ ): Int {
+ val importPath1 = import1.importPath!!.pathStr
+ val importPath2 = import2.importPath!!.pathStr
+
+ val isTypeAlias1 = import1.aliasName != null
+ val isTypeAlias2 = import2.aliasName != null
+
+ val index1 = packageImportOrdering.indexOfFirst { importPath1.startsWith(it) }
+ val index2 = packageImportOrdering.indexOfFirst { importPath2.startsWith(it) }
+
+ return when {
+ isTypeAlias1 && isTypeAlias2 -> importPath1.compareTo(importPath2)
+ isTypeAlias1 && !isTypeAlias2 -> 1
+ !isTypeAlias1 && isTypeAlias2 -> -1
+ index1 == -1 && index2 == -1 -> importPath1.compareTo(importPath2)
+ index1 == -1 -> -1
+ index2 == -1 -> 1
+ else -> index1.compareTo(index2)
+ }
+ }
+ }
+ }
+
+ private fun isImportsCorrectlyOrdered(imports: List, rawText: List): Boolean {
+ if (rawText.any { it.isBlank() }) {
+ return false
+ }
+
+ var inPreprocess = false
+ val linesToIgnore = mutableListOf()
+
+ for (line in rawText) {
+ if (line.contains(PreprocessingPattern.IF.asComment)) {
+ inPreprocess = true
+ continue
+ }
+ if (line.contains(PreprocessingPattern.ENDIF.asComment)) {
+ inPreprocess = false
+ continue
+ }
+ if (line.contains(PreprocessingPattern.DOLLAR_DOLLAR.asComment)) {
+ continue
+ }
+ if (inPreprocess) {
+ linesToIgnore.add(line)
+ }
+ }
+
+ val originalImports = rawText.filter { !it.containsPreprocessingPattern() && !linesToIgnore.contains(it) }
+ val formattedOriginal = originalImports.joinToString("\n") { it }
+
+ val expectedImports = imports.sortedWith(importOrder).map { "import ${it.importPath}" }
+ val formattedExpected = expectedImports.filter { !linesToIgnore.contains(it) }.joinToString("\n")
+
+ return formattedOriginal == formattedExpected
+ }
+
+ override fun visitImportList(importList: KtImportList) {
+
+ val testEntity = Entity.from(importList)
+
+ val rawText = importList.text.trim()
+ if (rawText.isBlank()) {
+ return
+ }
+
+ val importsCorrect = isImportsCorrectlyOrdered(importList.imports, rawText.lines())
+
+ if (!importsCorrect) {
+ report(
+ CodeSmell(
+ issue,
+ testEntity,
+ "Imports must be ordered in lexicographic order without any empty lines in-between " +
+ "with \"java\", \"javax\", \"kotlin\" and aliases in the end. This should then be followed by " +
+ "pre-processed imports.",
+ ),
+ )
+ }
+
+ super.visitImportList(importList)
+ }
+}
diff --git a/detekt/src/main/kotlin/imports/ImportRuleSetProvider.kt b/detekt/src/main/kotlin/imports/ImportRuleSetProvider.kt
new file mode 100644
index 000000000000..3b50cf22c2c8
--- /dev/null
+++ b/detekt/src/main/kotlin/imports/ImportRuleSetProvider.kt
@@ -0,0 +1,17 @@
+package at.hannibal2.skyhanni.detektrules.imports
+
+import com.google.auto.service.AutoService
+import io.gitlab.arturbosch.detekt.api.Config
+import io.gitlab.arturbosch.detekt.api.RuleSet
+import io.gitlab.arturbosch.detekt.api.RuleSetProvider
+
+@AutoService(RuleSetProvider::class)
+class ImportRuleSetProvider : RuleSetProvider {
+ override val ruleSetId: String = "ImportRules"
+
+ override fun instance(config: Config): RuleSet {
+ return RuleSet(ruleSetId, listOf(
+ CustomImportOrdering(config)
+ ))
+ }
+}
diff --git a/detekt/src/main/kotlin/root.kt b/detekt/src/main/kotlin/root.kt
new file mode 100644
index 000000000000..9b95a398f30a
--- /dev/null
+++ b/detekt/src/main/kotlin/root.kt
@@ -0,0 +1 @@
+package at.hannibal2.skyhanni.detektrules
diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md
index b31628859ee8..dc79cc80fd38 100644
--- a/docs/CHANGELOG.md
+++ b/docs/CHANGELOG.md
@@ -1,5 +1,765 @@
# SkyHanni - Change Log
+## Version 0.28 (in Beta)
+
+### New Features
+
+#### Hoppity Features
+
++ Added an easier way to check your unique Hoppity Eggs collected on each island. - martimavocado (https://github.com/hannibal002/SkyHanni/pull/2625)
+ + Shows your progress in the Warp Menu.
+ + Can be automatically hidden when an island is complete.
++ Added the ability to block opening the Chocolate Factory when Booster Cookie is inactive. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2713)
++ Added a feature to block opening Hoppity's trade menu from Abiphone calls if you do not have coins in your purse. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2664)
++ Added the ability to prevent closing Meal Eggs that have Rabbit the Fish inside. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2712)
+
+#### Inventory Features
+
++ Added Focus Mode. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/2694)
+ + In Focus Mode, only the name of the item is displayed instead of the full description.
+
+#### Fishing Features
+
++ Added Lava Replacement. - HiZe (https://github.com/hannibal002/SkyHanni/pull/1885)
+ + Replaces the lava texture with the water texture.
+ + Primarily used for lava fishing in the Crimson Isle, but can be used anywhere else if the option is enabled.
+
+#### Mining Features
+
++ Added Precision Mining Highlighter. - Cuz_Im_Clicks (https://github.com/hannibal002/SkyHanni/pull/2614)
+ + Draws a box over the Precision Mining particles.
++ Added highlighting boxes to Crystal Nucleus crystals during Hoppity's Hunt. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2598)
++ Added Flowstate Helper. - martimavocado (https://github.com/hannibal002/SkyHanni/pull/2561)
+ + Displays stats for the Flowstate enchantment on mining tools.
+
+#### Dungeon Features
+
++ Added Terminal Waypoints. - Stella (https://github.com/hannibal002/SkyHanni/pull/2719)
+ + Displays waypoints during the F7/M7 Goldor Phase.
+
+#### Chat Features
+
++ Added chat compacting for 'items in stash' warnings. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2639)
+
+### Improvements
+
+#### Inventory Improvements
+
++ Added Pocket Sack-in-a-Sack support to Estimated Item Value. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2698)
+
+#### Chat and Command Improvements
+
++ Replaced repeated SkyHanni messages with the previous message. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2700)
++ Added support for Guilds in player-related tab completions. - ThatGravyBoat (https://github.com/hannibal002/SkyHanni/pull/2637)
++ Added support for all Guild and Friend commands in tab completions. - ThatGravyBoat (https://github.com/hannibal002/SkyHanni/pull/2637)
++ Reordered commands in categories. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2642)
++ Renamed some commands for clarity. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2642)
+
+#### Combat Improvements
+
++ Added Totem of Corruption and Enrager to the Ability Cooldown feature. - DungeonHub (https://github.com/hannibal002/SkyHanni/pull/2706)
+
+#### Mining Improvements
+
++ Made the "You need a stronger tool to mine ..." chat filter hide every such message, not just Crystal Hollows gemstones. - Luna (https://github.com/hannibal002/SkyHanni/pull/2724)
++ Added an option to draw a line to your golden or diamond goblin. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/2717)
+
+#### Diana Improvements
+
++ Added support for detecting and handling Inquisitor spawn messages from other mods from chat. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2720)
+
+#### Fishing Improvements
+
++ Added an option to always display the Barn Fishing Timer anywhere. - NeoNyaa (https://github.com/hannibal002/SkyHanni/pull/2735)
+
+#### Hoppity Improvements
+
++ Improved the Time Tower Usage Warning so it doesn't spam messages. - MTOnline (https://github.com/hannibal002/SkyHanni/pull/2730)
+
+#### Misc Improvements
+
++ Added distance display to waypoints created by Patcher's Send Coords feature. - jani (https://github.com/hannibal002/SkyHanni/pull/2704)
++ Made multiple improvements to the Custom Scoreboard. - Empa, j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2162)
+ + Added an option to align the text.
+ + Improved overall performance.
+ + Added the Party Leader to the Party Element.
+ + Separated title and footer alignment.
+ + Added a custom alpha footer.
+
+### Fixes
+
+#### Dungeon Fixes
+
++ Fixed Magical Power resetting to 0 when opening "Your Bags" in the Catacombs. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2710)
++ Fixed a rare case where invisible Fels were highlighted even though they shouldn't. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/2746)
+
+#### Fishing Fixes
+
++ Fixed fishing displays showing in dungeons. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2697)
+
+#### Inventory Fixes
+
++ Fixed "No Guardian Pet warning" not supporting Pet Rule "On open Experimentation Table". - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2699)
++ Fixed an error with compact experiment rewards chat messages. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2702)
++ Fixed Craft Materials Bazaar not working with long item names. - Fazfoxy (https://github.com/hannibal002/SkyHanni/pull/2703)
++ Fixed the debug feature that allows you to add/remove stars being enabled by default. - CalMWolfs (https://github.com/hannibal002/SkyHanni/pull/2715)
++ Fixed displaying the Guardian warning text in the Experimentation Table even when using a Guardian Pet. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2718)
++ Fixed locked Ultimate enchantments being hidden by Enchant Parsing. - martimavocado (https://github.com/hannibal002/SkyHanni/pull/2732)
++ Fixed Chest Value Display on Carpentry Chests. - fazfoxy (https://github.com/hannibal002/SkyHanni/pull/2743)
++ Fixed Compact Item Stars. - Empa, Fazfoxy (https://github.com/hannibal002/SkyHanni/pull/2741)
+
+#### Combat Fixes
+
++ Fixed Ghost Counter display appearing while in incorrect areas on the map. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2696)
++ Fixed Ashfang Blazes sometimes being highlighted with the wrong color. - Empa (https://github.com/hannibal002/SkyHanni/pull/2112)
++ Fixed Ashfang Reset Cooldown counting in the wrong direction. - Empa (https://github.com/hannibal002/SkyHanni/pull/2112)
++ Fixed Millennia-Aged Blaze not being highlighted by the Area Boss Highlight feature. - jani (https://github.com/hannibal002/SkyHanni/pull/2707)
++ Fixed a small typo in Bestiary Display. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2748)
+
+#### Custom Scoreboard Fixes
+
++ Fixed Custom Scoreboard not showing the Second Barbarian Quest. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2709)
+
+#### Hoppity Fixes
+
++ Fixed the chocolate egg share message sometimes displaying the wrong location name. - martimavocado (https://github.com/hannibal002/SkyHanni/pull/2711)
++ Fixed El Dorado not receiving a compacted chat message. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2742)
+
+#### Garden Fixes
+
++ Fixed farming weight not disappearing when the config option is off. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2731)
++ Fixed New Visitor Ping triggering too late if the player is actively farming. - Luna (https://github.com/hannibal002/SkyHanni/pull/2767)
+
+#### Crimson Isle Fixes
+
++ Fixed Replace Lava not working with OptiFine. - CalMWolfs + nopo (https://github.com/hannibal002/SkyHanni/pull/2727)
+
+#### Diana Fixes
+
++ Fixed Griffin Pet Warning not supporting Diana Autopet rules. - hannibal2 (https://github.com/hannibal002/SkyHanni/pull/2722)
+
+#### Commands Fixes
+
++ Fixed /shtranslate not working in most cases. - Obsidian (https://github.com/hannibal002/SkyHanni/pull/2693)
+
+#### Mining Fixes
+
++ Fixed a crash when attempting to edit the Flowstate Helper config. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2740)
+
+#### Chocolate Factory Fixes
+
++ Fixed an issue where the Time Tower Usage Warning would notify you after expiration or when you have 0 charges. - MTOnline (https://github.com/hannibal002/SkyHanni/pull/2751)
+
+#### Event Fixes
+
++ Fixed Fear Stat Display. - Fazfoxy (https://github.com/hannibal002/SkyHanni/pull/2766)
+
+#### Misc Fixes
+
++ Fixed SkyHanni messages being sent twice. - CalMWolfs (https://github.com/hannibal002/SkyHanni/pull/2736)
++ Fixed the formatting of negative durations. - Empa (https://github.com/hannibal002/SkyHanni/pull/2726)
+
+### Technical Details
+
++ Assigned 'backend' label to PRs with 'backend' in the title. - CalMWolfs (https://github.com/hannibal002/SkyHanni/pull/2690)
+ + This does not change the functionality of the current bug fix label.
++ Added SuggestionProvider. - ThatGravyBoat (https://github.com/hannibal002/SkyHanni/pull/2637)
+ + This new class simplifies building suggestion trees.
++ Added Stats API. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/2253)
++ Cleaned up LorenzVec and added drawLineToEye. - Empa (https://github.com/hannibal002/SkyHanni/pull/2056)
++ Added SkyHanni event inheritance. - ThatGravyBoat (https://github.com/hannibal002/SkyHanni/pull/2047)
++ Added /shtranslateadvanced command. - Obsidian (https://github.com/hannibal002/SkyHanni/pull/2693)
+ + Allows specifying both the source and target language.
++ Added changelog verification. - CalMWolfs (https://github.com/hannibal002/SkyHanni/pull/2692)
+ + This action ensures your PR is in the correct format so that it can be used by the release notes tool.
++ Added dungeon phase detection. - martimavocado (https://github.com/hannibal002/SkyHanni/pull/1865)
++ Added EntityLeaveWorldEvent. - Empa (https://github.com/hannibal002/SkyHanni/pull/2112)
++ Made command registration event-based. - j10a1n15, ThatGravyBoat (https://github.com/hannibal002/SkyHanni/pull/2642)
++ Added "line to the mob" handler. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/2717)
++ No longer use NEU GUI elements for the auto-update button. - CalMWolfs (https://github.com/hannibal002/SkyHanni/pull/2725)
++ Added SkyHanni notifications. - CalMWolfs (https://github.com/hannibal002/SkyHanni/pull/2630)
++ Moved Hoppity Warp Menu config to `HoppityWarpMenuConfig`. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2712)
++ Migrated some LorenzEvents without parameters to SkyHanniEvents. - Empa (https://github.com/hannibal002/SkyHanni/pull/2744)
++ Changed SkyHanniEvents without parameters to be objects. - Empa (https://github.com/hannibal002/SkyHanni/pull/2744)
+
+## Version 0.27
+
+### New Features
+
+#### Garden Features
+
++ Added No Pests Chat Filter. - saga (https://github.com/hannibal002/SkyHanni/pull/1957)
+ + Removed the chat message "There are no Pests on your Garden!".
++ Added No Pests Title. - saga (https://github.com/hannibal002/SkyHanni/pull/1957)
+ + Shows a title when you use the Pest Tracker without any pests to clear.
+
+#### Mining Features
+
++ Added a "Get from Sack" button in the forge recipe menu to retrieve ingredients. - minhperry (https://github.com/hannibal002/SkyHanni/pull/2106)
++ Added Tracker for Glacite Corpses. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2306)
+ + Tracks overall loot and loot per type.
++ Hides tooltips of items inside the Fossil Excavator in Glacite Tunnels. - Cuz_Im_Clicks (https://github.com/hannibal002/SkyHanni/pull/2539)
+
+#### Rift Features
+
++ Added Motes per Session. - Empa (https://github.com/hannibal002/SkyHanni/pull/2323)
++ Added Crafting Room Helper. - HiZe (https://github.com/hannibal002/SkyHanni/pull/2178)
+ + Shows a holographic mob at the location where the mob is present in the real room inside the Mirrorverse in Rift.
++ Added Rift Time Real-Time Nametag Format. - Empa (https://github.com/hannibal002/SkyHanni/pull/2015)
++ Added a helper for tracking the Buttons Enigma Soul in the Rift. - MTOnline (https://github.com/hannibal002/SkyHanni/pull/2616)
+ + Using pathfinding to guide the player to the nearest spot with unpressed buttons and highlights them.
++ Added a route helper for Gunther's Rift Race in the West Village. - MTOnline (https://github.com/hannibal002/SkyHanni/pull/2616)
++ Added the ability to mute Wilted Berberis sounds when not farming. - MTOnline (https://github.com/hannibal002/SkyHanni/pull/2616)
+
+#### Dungeon Features
+
++ Added highlight for starred dungeon mobs. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/1558)
++ Added highlight for Fel skulls. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/1558)
+ + Optionally draws a line to them as well.
++ Added a Secret Chime for Dungeons with adjustable pitch and sound. - Ovi_1 (https://github.com/hannibal002/SkyHanni/pull/2478)
+ + The sound and pitch of chimes in dungeons are customizable.
+
+#### Scoreboard Features
+
++ Added Soulflow to the Custom Scoreboard. - Empa (https://github.com/hannibal002/SkyHanni/pull/1837)
+ + Requires Soulflow to be enabled in Hypixel settings: /tab -> Profile Widget -> Show Soulflow.
++ Added the current minister to the calendar. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2342)
++ Allowed the use of the Custom Scoreboard outside of SkyBlock, but only on Hypixel. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/1881)
++ Added an option to disable custom lines in the Custom Scoreboard. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/1881)
+
+#### Hoppity Features
+
++ Added Hoppity Hunt event summary. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2311)
+ + Use /shhoppitystats for live stats.
++ Added optional warning when Hoppity calls you with a rabbit to sell. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2272)
++ Added hotkey for picking up Abiphone calls from Hoppity. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2272)
++ Added a draggable list for Hoppity's Collection menu rabbit highlighting. - the1divider (https://github.com/hannibal002/SkyHanni/pull/2438)
+ + Factory/Shop milestones = Yellow/Gold.
+ + Stray rabbits = Dark Aqua.
+ + Abi = Dark Green.
++ Added a toggle to highlight found rabbits in Hoppity's Collection menu. - the1divider (https://github.com/hannibal002/SkyHanni/pull/2438)
++ Added the ability to change the color of missing rabbit dyes in Hoppity's Collection to reflect rabbit rarity. - Daveed (https://github.com/hannibal002/SkyHanni/pull/2522)
+
+#### The Carnival Features
+
++ Added a Reminder to claim Carnival Tickets. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/2498)
++ Added display for Carnival Goals. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/2498)
++ Added a feature to double-click the Carnival NPC to start the game without clicking in the chat. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/2498)
++ Added Zombie Shootout QoL improvements for the Carnival. - ILike2WatchMemes (https://github.com/hannibal002/SkyHanni/pull/2497)
+ + Colored hitboxes.
+ + Lamp timer + line.
+
+#### GUI Features
+
++ Added Editable XP Bar. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/1944)
+ + Enabled moving and scaling of the XP bar in the SkyHanni GUI Editor.
++ Added Display Tab Widgets. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/1276)
+ + Allows the display of information from the Tab (e.g., Bestiary info).
+
+#### Inventory Features
+
++ Added a warning when opening the Experimentation Table without a Guardian pet. - martimavocado (https://github.com/hannibal002/SkyHanni/pull/2127)
++ Added Enchanting Experience as Stack Size in Experimentation Table. - saga (https://github.com/hannibal002/SkyHanni/pull/1988)
+ + Added to the Item Number list.
++ Show dye hex code as the actual color in the item lore. - nopo (https://github.com/hannibal002/SkyHanni/pull/2321)
++ Added Display for Bits on Cookie buy. - Thunderblade73 (https://github.com/hannibal002/SkyHanni/pull/2265)
+ + Shows the Bits you would gain.
+ + Can show the change for the available Bits.
+ + Also shows the time more clearly (or adds it if not present).
++ Added Compact Experimentation Table chat rewards. - ILike2WatchMemes (https://github.com/hannibal002/SkyHanni/pull/2209)
+ + Uses a compact chat message of rewards gained from Add-ons/Experiments.
++ Added Personal Compactor/Deletor Overlay. - Empa (https://github.com/hannibal002/SkyHanni/pull/1869)
+ + Shows what items are currently inside the personal compactor/deletor, and whether the accessory is turned on or off.
++ Added Accessory magical power display as stack size. - minhperry (https://github.com/hannibal002/SkyHanni/pull/2243)
+ + Only works inside the Accessory Bag and Auction House.
++ Added `/gfs` to fix a broken Piggy Bank. - j10a1n15 (https://github.com/hannibal002/SkyHanni/pull/2150)
++ Added Superpair Display. - ILike2WatchMemes (https://github.com/hannibal002/SkyHanni/pull/2171)
+ + Displays found and matched pairs, power-ups, and missing pairs/normals.
++ Added Experiments Dry-Streak Display. - ILike2WatchMemes (https://github.com/hannibal002/SkyHanni/pull/2171)
+ + Shows attempts and XP since the last ULTRA-RARE.
++ Added Experiments Profit Tracker. - ILike2WatchMemes (https://github.com/hannibal002/SkyHanni/pull/2171)
+ + Tracks profits in Coins and Enchanting XP.
++ Added Ultimate Enchant Star. - Empa (https://github.com/hannibal002/SkyHanni/pull/2612)
+ + Shows a star on Enchanted Books with an Ultimate Enchant.
+
+#### Chat Features
+
++ Added `/shcolors` command. - minhperry (https://github.com/hannibal002/SkyHanni/pull/2216)
+ + Prints a list of all Minecraft color and formatting codes in chat.
++ Added Remind command. - ThatGravyBoat & Zickles (https://github.com/hannibal002/SkyHanni/pull/1708)
+ + Use `/shremind