This is a technical guide that helps Kotlin and Java developers understand how SkyHanni works, and provides the first steps for new Fabric developers to take.
When making changes to the code, it is recommended to use an IDE for live debugging and testing. This tutorial explains how to set up the development environment for SkyHanni. We use IntelliJ as an example.
- Download IntelliJ from the JetBrains Website.
- Use the Community Edition. (Scroll down a bit.)
- Create an account on GitHub
- Go to https://github.com/hannibal002/SkyHanni
- Click on the fork button to create a fork.
- Leave the settings unchanged
- Click on
create fork
- Open IntelliJ
- Link the GitHub account with IntelliJ.
- Install Git in IntelliJ.
- In IntelliJ, go to
new→project from version control. - Select
SkyHannifrom the list. - Open the project.
Once your project is imported into IntelliJ from the previous step, all dependencies like Minecraft 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.
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 25 JDK.
Now that Gradle is done importing (which might take a few minutes the first time you download the project) we want to set up the java version for the project.
To do this we press (CTRL+ALT+SHIFT+S) in IntelliJ, or go to File → Project Structure....
We want to set the project structure to use Java 25.
Finally, we then want to reload Gradle which can be done from the Gradle tab from earlier.
After all importing is done (which should be much quicker this time), you should find a new IntelliJ run configuration. If not, you can restart IntelliJ and reload the Gradle project again.
Select an appropriate Java 25 JDK (preferably Adoptium, but any Java 25 JDK will do).
Now that we are done with that, you should be able to launch your game from your IDE with that run configuration.
General infos about Pull Request can be found on the GitHub Docs.
If you are not very familiar with Git, you might want to check out some of these resources:
Proposed changes are best made in their own branch, as this makes development easier for both you and the maintainers of this repository. You can do this by following the instructions within the IntelliJ window in the open SkyHanni project:
- Click the beta dropdown at the top of IntelliJ.
- Click New branch.
- Give the branch a name related to the changes you plan to make.
Please use a prefix for the PR name (e.g., Feature, Improvement, Fix, Backend, etc.).
Bug fixes should generally be submitted as standalone PRs. Including a bug fix alongside other changes is only acceptable if the total PR size is small (under 500 lines changed). Large PRs whose primary goal is not a bug fix should not include Fixes changelog entries. If you discover a bug while working on a large PR, extract the fix into a separate standalone PR instead of including it in the current one.
When writing the PR description, ensure you fill out the template with all the necessary information. In the What section, write technical details or explanations that don't belong in the changelog. Including that field is optional for small changes.
If your PR relies on another PR, please include this information at the beginning of the description, under a ## Dependencies heading.
Use the format - #<pr number> for the dependency, or - <url> for REPO dependencies.
Only the lines belonging to that section are read. The list may start after a blank line. The first line that does not start with
- ends the section, so keep the entries together. Anything wrong with the section, for example a mistyped number or a line in the wrong
format, blocks the pull request until it is corrected.
The PR description is processed by our ChangeLog Builder, which is included
as a build dependency of the SkyHanni project. The ChangelogVerification Gradle task in buildSrc/ uses it to validate PR descriptions in
CI. Do not manually edit docs/CHANGELOG.md or docs/FEATURES.md. These files are maintained by the project maintainer.
- Follow the format examples from the template and remove the categories that do not apply to your PR.
- A PR might include multiple changelog categories simultaneously.
Here is an explanation of which changes belong to each category:
New standalone features that are independent of existing features. Additional settings for existing features belong in the Improvements category.
Changes that improve or expand the code's logic and have a visible impact on users. This category is for enhancements, not for fixing incorrect logic. The line between bug fixes and improvements is sometimes not clear. If you are unsure whether a change is an improvement or a bug fix, ask for guidance.
Bug fixes, including typos (only those visible to the user, e.g., in config or chat messages), missing checks that cause incorrect behavior under specific conditions, or logic errors. Only significant performance issues are considered bugs; otherwise, they fall under Technical Details. If the code does not behave as intended by the original developer, it is a bug. If the original code had logical errors, it is a bug. If the original code lacks nuance but is not incorrect, it is not a bug.
Internal changes that do not impact the end user. Examples include:
- Refactoring (renaming or moving members, functions, classes, files or packages)
- Typos in object names (which the end user will not see)
- API updates
- Minor performance improvements (noticeable performance improvements belong in Improvements)
- Documentation changes to Markdown files, e.g., in
/docsor this file.
Try to avoid using this when the main goal of the PR is a user facing change, and the included backend change is related to that change. We mostly only need standalone changes or big/relevant backend changes marked as Technical Details, everything else can go in the normal PR description (What area).
Features that have merged with existing features (in the config) or have become obsolete (e.g., if Hypixel implements them on the server side).
Some changes don't fit any categories.
E.g. when reverting pull requests or doing quick fixes to PRs merged immediately beforehand but not yet released in a beta.
To tell the changelog build this, write either exclude_from_changelog or ignore_from_changelog in one line.
Make sure such pull requests have a good explanation in the What section.
-
Follow the Hypixel Rules.
-
Do not submit AI-generated content.
- This includes code, pull requests, issues, and review comments generated by tools such as GitHub Copilot, ChatGPT, Claude, or similar systems.
- All contributions must be written and understood by the person submitting them. Using AI tools to help you learn something is fine, but the code and text you submit must be your own work.
- AI-generated content often introduces subtle bugs, hallucinated APIs, or misleading context that costs reviewers significant time to identify. Contributors who repeatedly submit AI-generated content may be blocked from the repository.
-
My build is failing due to
detekt, what do I do?detektis our code quality tool. It checks for code smells and style issues.- When you open or update a pull request, Detekt runs automatically in CI. Any findings are posted as a comment on the PR listing the affected files, line numbers, and rule names.
- If you have a build failure stating
Analysis failed with ... weighted issues., you can checkbuild/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
@Suppress("rule_name"), or re-build thebaseline-main.xmlfile, using./gradlew detektBaselineMain.
- If you have such a case, either use
-
Setting up the Detekt IntelliJ plugin (one time, per machine)
- Install the Detekt plugin from the JetBrains Marketplace to see Detekt findings inline while editing.
- Open
Settings → Tools → Detektand set:Configuration Filestodetekt/detekt.ymlBaseline Filetodetekt/baseline-main.xml
- Both
detekt.ymlandbaseline-main.xmlare committed to the repository, but the plugin's reference to them is stored per machine, not shared through git. Every contributor needs to set this link once after cloning the repository. Without it, the plugin flags issues that CI does not (rules disabled indetekt.yml, or issues already covered by the baseline).
-
When the SkyHanni IntelliJ plugin flags issues in a file you are already editing, fix those issues in the same PR. Do not create standalone PRs to sweep plugin warnings across the entire codebase.
-
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).
- The mod has, according to Hypixel rules, illegal features ("cheat mod/client").
- If you can improve the existing feature in a meaningful way.
-
All new classes should be written in Kotlin, with a few exceptions:
- Mixin classes in
at.hannibal2.skyhanni.mixins.transformersKeep mixin code minimal. The mixin method should contain only a single call to a Kotlin function. All logic belongs in Kotlin. Exception: a standaloneifthat callsci.cancel()is acceptable when theCallbackInfoobject must stay in the mixin.
- Mixin classes in
-
New features should be made in Kotlin objects unless there is a specific reason for it not to.
- If the feature needs to register Fabric events, uses SkyHanni events or creates repo patterns, annotate the feature class with
@SkyHanniModule - This will automatically register all events to the respective event bus, and loads the repo patterns.
- Until the project is compiled for the first time, the IDE will show a red error in
SkyHanniMod.kt. This is expected and resolves after the first build.
- If the feature needs to register Fabric events, uses SkyHanni events or creates repo patterns, annotate the feature class with
-
All functions and properties must be defined inside a class or object. Top-level Kotlin functions and properties are not permitted.
-
Use
Unitinstead ofVoidin Kotlin code. -
Avoid using deprecated functions.
- These functions are marked for removal in future versions.
- If you're unsure why a function is deprecated or how to replace it, please ask for guidance.
-
Future JSON data objects should be made in kotlin.
-
Config files should be made in Kotlin.
- There may be legacy config files left as Java files, however they will all be ported eventually.
-
Please use the existing event system, or expand on it.
- Custom SkyHanni events are located in the
eventspackage, organized into sub packages by category. When creating a new event, place it in the appropriate sub package. Thematically related events can be placed together in a single file. - To expand the event system, you can create a new event that is called from a Mixin,
or you can subscribe to a Fabric event and then post a SkyHanni event from that.
See the
api/minecrafteventspackage for examples. - If you make a new event, make sure it extends one of these base types:
- SkyHanniEvent: The base class for all events. Use this directly for simple events that don't need cancellation or rendering.
- CancellableSkyHanniEvent: An event that listeners can cancel via
cancel(), preventing further processing. GenericSkyHanniEvent<T>: An event with a type parameter, allowing listeners to subscribe only for a specific type. For example, an event with typeZombiewould only be received by listeners registered for that type. Generic events are also cancellable.- RenderingSkyHanniEvent: An event in which listeners are allowed to do GUI rendering.
- Events can also use the
SkyHanniEvent.CancellableandSkyHanniEvent.Renderinginterfaces directly if needed. - Functions annotated with
@HandleEventmust be declaredprivate. The SkyHanni IntelliJ plugin flags non-private handlers and provides a quick fix.
- Custom SkyHanni events are located in the
-
Do not subscribe to Fabric events directly in feature classes. Instead, subscribe to SkyHanni events. Only backend data classes in the
apipackages should listen to Fabric events. Their job is to process the Fabric event and fire a corresponding SkyHanni event that feature classes then use. See theapi/minecrafteventspackage for examples. -
Every event class must have a KDoc comment that describes: what the event represents, when it is fired, what each parameter means, and optionally, when to use or not use it.
- For new events, the KDoc is required in the same PR.
- For existing events, add the KDoc the next time the event is touched or newly used in a PR.
-
Please use existing utils methods.
-
Never use
System.currentTimeMillis(). Use our own classSimpleTimeMarkinstead.- See this commit as an example.
-
Try to avoid using Kotlin's
!!(catch if not null) feature.- Replace it with
?:(if null return this). - This will most likely not be possible to avoid when working with objects from java.
- Replace it with
-
Don't forget to add
@FeatureToggleto new standalone features (not options to that feature) in the config. -
Do not use
e.printStackTrace(), useErrorManager.logErrorWithData(error, "explanation for users", ...extraOptionalData)instead. -
Do not use
toRegex()ortoPattern(). UseRepoPatterninstead. RepoPattern allows regex patterns to be updated remotely via the repo without requiring a mod update. Each pattern has a local fallback defined in code, but can be overridden by the repo at runtime. See RepoPattern.kt- Define patterns using
RepoPattern.pattern(key, fallback)with Kotlin delegation (by). - When a file contains more than one pattern, use
RepoPattern.group(prefix)to group them under a shared key prefix. - Pattern variables should be named in the scheme
variableNamePattern. - All repo patterns must be accompanied by a regex test. Add lines starting with
REGEX-TEST:in a KDoc comment above the pattern variable to provide test examples. - Look at existing patterns in the codebase for reference.
- Define patterns using
-
Please use Regex instead of String comparison when it is likely Hypixel will change the message in the future.
-
Do not use
fixedRateTimerwhen possible and instead useSecondPassedEventto safely execute the repeating event on the main thread. -
When updating a config option variable, use the
ConfigUpdaterMigrator.ConfigFixEventwith event.move() when moving a value, and event.transform() when updating a value. For Example. -
Use American English spelling conventions (e.g., "color" not "colour").
-
When creating/updating a command, move it out of the
Commands.ktclass, if it isn't already, into the class that it belongs to. -
Avoid direct function imports. Always access functions or members through their respective namespaces or parent classes to improve readability and maintain encapsulation. Extension functions are an exception to this rule.
-
Enum entries may be written without their enum class name wherever the compiler resolves them from context. This is preferred, not just tolerated, because the enum class name carries no information there. It covers
whensubjects and branches, comparisons, assignments and arguments with a known parameter type. Stay consistent within a block: do not mixMyEnum.ENTRYandENTRYin the same place. -
Use named parameters for boolean and numeric arguments where the meaning is not immediately clear from context (e.g.,
findMobHeight(height, above = true)instead offindMobHeight(height, true)). -
Follow Kotlin conventions for acronym naming:
- Use all-uppercase for two-letter acronyms (e.g.,
XP). - Treat three or more letter acronyms as regular words with only the first letter capitalized (e.g.,
Api).
- Use all-uppercase for two-letter acronyms (e.g.,
-
Always combine title messages with chat message.
- This way users know what feature and what mod sends the title, if they want to disable it.
- Also, we can include more information on why the title just showed up, as the title should not be too long.
DevAuth is a tool that allows logging in to a Minecraft account while debugging in IntelliJ. This is very useful for coding live on Hypixel without the need to compile a jar.
- The library is already downloaded by Gradle.
- SkyHanni will automatically set up DevAuth.
- Start Minecraft inside IntelliJ normally.
- Click on the link in the console and verify with a Microsoft account.
- The verification process will reappear every few days (after the session token expires).
Allows project specific plugins to run. Eg: Regex Intention
Imports our custom live templates automatically. Live Templates allow for quicker code writing.
Helps you write Minecraft specific code such as mixins and access wideners.
Provides syntax highlighting and quick version switching for our multiversion development setup.
An IntelliJ plugin with SkyHanni-specific development assistance. It validates, for example, event handler
declarations, flags missing or incorrect @HandleEvent, @PrimaryFunction, and @SkyHanniModule
annotations, resolves config paths, and suppresses false-positive IntelliJ warnings in SkyHanni-specific
patterns such as RepoPattern keys and Brigadier command names.
Install it manually: download the latest release from the GitHub releases page and install via Settings → Plugins → ⚙ → Install plugin from disk....
SkyHanni is a Fabric mod for Minecraft, written in Kotlin and Java.
We use a Gradle configuration to build the mod, written in Kotlin DSL: build.gradle.kts
This start script will automatically download all required libraries.
SkyHanni reads the NEU Item Repo for item internal names, display names and recipes. NEU is not a dependency of SkyHanni.
SkyHanni stores the config (settings and user data) as a JSON object in a single text file. For rendering the /sh config (categories, toggles, search, etc.), SkyHanni uses MoulConfig, the same config system as NotEnoughUpdates.
SkyHanni utilizes the Elite API (view the public site here) for some farming features and for LBIN price data.
This includes features relating to Farming Weight, as well as syncing Jacob contests amongst players for convenience. Features that upload data to the Elite API are optional and opt-in. All requests to the Elite API are subject to its privacy policy.
A system to inject code into the original Minecraft code. Mixin is bundled with the Fabric Loader and does not need to be included as a runtime dependency.
It allows to easily modify methods in Minecraft itself, without conflicting with other mods.
For more information, see https://github.com/SpongePowered/Mixin or our existing mixins.
When creating new Mixins, try to keep the code inside the mixin as small as possible, and call a hook as soon as possible. The mixin method itself should ideally contain only a single call to a Kotlin function. All logic belongs in Kotlin, not in the Java mixin.
SkyHanni uses KSP via the annotation-processors module to generate code at compile time.
@SkyHanniModule: GeneratesLoadedModules.kt, which registers all event handlers and repo patterns automatically.- Mixin registration: Scans for
@Mixin-annotated classes and generates the mixin configuration. There is no manual mixin list to update.
SkyHanni uses a repo system to easily change static variables without the need for a mod update.
The repo is located at https://github.com/hannibal002/SkyHanni-REPO.
A copy of all JSON files is stored on the computer under .minecraft\config\skyhanni\repo.
On every game start, the copy gets updated (if outdated and if not manually disabled).
If you add stuff to the repo make sure it gets serialized. See
the JsonObjects
folder for how to properly do this. You also may have to disable repo auto update in game.
If your PR adds or changes data in the repo, open a separate PR on the SkyHanni-REPO
repository as well.
Keep the main mod PR as a draft until the repo PR is ready.
Link the repo PR URL in the ## Dependencies section of the mod PR.
DiscordIPC is a service that SkyHanni uses to send information from SkyBlock to Discord in Rich Presence.
For info on usage, look
at DiscordRPCManager.kt
We use the auto update library from nea89.
While not directly part of the Minecraft mod, it is useful to know that we have a Discord Bot that helps with small tasks related to PRs.
Several GitHub Actions workflows run automatically on pull requests to enforce code quality and keep PR metadata up to date.
All workflows use .github/scripts/pr_review.main.kts as the shared review script, invoked with a MODE parameter.
When a PR is updated, any existing comment posted by a workflow is collapsed into a <details> spoiler. What follows depends on the
workflow. The Detekt, Build Failure, Merge Conflict and Changelog Check comments only ever announce that something is wrong: a new comment
is posted while issues still exist, and once everything is resolved the label is removed without a new comment. The Dependency Label and
Description Keyword Labels comments announce both directions, so they also post when the situation resolves.
Every one of these comments carries an invisible marker that lets a later run recognize its own previous comment. For the two that announce both directions the marker also records which direction was announced last, and that record decides whether a new comment is needed. The label does not. A label edited by hand therefore no longer influences whether a comment appears. Labels are still read elsewhere: they select which PRs get re-evaluated after a dependency PR is closed, and the Merge Conflict workflow skips a PR that already carries its label.
Most workflows use a two-workflow split to allow write-operations on fork PRs without granting untrusted code elevated permissions.
The first workflow is triggered by pull_request, runs with limited permissions, and uploads results as an artifact. The second is
triggered by workflow_run on completion of the first, always uses the base branch version, carries write access (issues: write,
pull-requests: write, actions: read), and runs the review script against the artifact. The PR number is resolved at runtime from
the head branch of the triggering workflow run. The Merge Conflict Comment, Dependency Label and Description Keyword Labels sections
do not use this split.
Detekt runs automatically on every pull request. When findings are present, they are posted as a comment on the PR and the Detekt
label is applied.
.github/workflows/detekt.yml: Triggered bypull_request. Runs withcontents: readonly. RunsdetektMainand uploads the SARIF output as an artifact nameddetekt-output. Fork code never executes with write access..github/workflows/detekt-review.yml: Triggered byworkflow_runon completion ofdetekt.yml. Always uses the version from the base branch, so a fork PR cannot modify it. Runs withissues: write,pull-requests: write, andactions: read. Downloads the artifact torunner.temp(outside the workspace) and runs the review script. The script only reads the pre-generated SARIF, so fork code has no influence over what runs here..github/scripts/pr_review.main.kts(invoked withMODE=detekt): Parses the SARIF, formats findings into a PR comment, manages theDetektlabel, and handles the stale-comment logic.
When the multi-version build fails on a pull request, the stack trace is posted as a comment on the PR and the Fails Multi-Version
label is applied.
.github/workflows/build.yml: Triggered bypull_requestandpushto beta. On assemble failure, captures the Gradle output viateeinside.github/actions/gradle-retry/action.yml, saves it as a log file, and uploads it as an artifact namedbuild-failure-output-<version>(one per matrix version). Usescontinue-on-error: trueon the assemble step so the artifact is uploaded before the job fails..github/workflows/build-review.yml: Triggered byworkflow_runon completion ofbuild.yml. Always uses base branch code. Runs withissues: write,pull-requests: write, andactions: read. Downloads all version artifacts withcontinue-on-error: true, resolves the PR number by branch name, and runs the review script..github/scripts/pr_review.main.kts(invoked withMODE=build): Reads the log files, extracts a one-liner (firste:compiler error line) and the stack trace starting fromFAILURE: Build failed with an exception(capped at 10,000 characters). Posts the result as a PR comment and manages theFails Multi-Versionlabel.
If no open PR matches the branch (e.g. for a direct push to beta), the script exits without posting a comment.
When a pull request has merge conflicts with the base branch, the Merge Conflicts label is applied and a comment is posted. When
conflicts are resolved, the comment is collapsed into a <details> spoiler and the label is removed without posting a new comment.
.github/workflows/label-merge-conflict.yml: Triggered bypull_request_targetonopenedandsynchronizeevents, and bypushto beta. Runs withissues: writeandpull-requests: write. Does not use the two-workflow split becausepull_request_targetalready provides write access while running base branch code. On a push to beta, no PR number is available and all open PRs are rechecked..github/scripts/pr_review.main.kts(invoked withMODE=merge_conflict): Queries the GitHub Pulls API for themergeablefield of the PR. Ifnull(GitHub has not yet computed the state), the script exits without making any changes. Iffalse, an existing conflict comment is staled and a new one is posted, and the label is added. Iftrue, an existing conflict comment is staled and the label is removed.
When a pull request has changelog or title issues detected by the checkPrDescription Gradle task, the Wrong Title/Changelog label is
applied and a comment is posted with the list of issues. When the issues are resolved, the comment is collapsed into a <details> spoiler
and the label is removed.
The checkPrDescription task writes a formatted changelog_errors.txt to build/changelog-verification/ on failure. The comment
content is read directly from this file without additional parsing.
.github/workflows/pr-check.yml: Triggered bypull_requestonopened,edited,ready_for_review, andsynchronizeevents. ThecheckPrDescriptionsteps run withcontinue-on-error: trueand uploadbuild/changelog-verification/changelog_errors.txtas thechangelog-check-failureartifact on failure. A separate step at the end fails the job so the overall check result is still a failure..github/workflows/changelog-review.yml: Triggered byworkflow_runon completion ofpr-check.yml. Always uses base branch code. Runs withissues: write,pull-requests: write, andactions: read. Downloads the artifact, resolves the PR number by branch name, and runs the review script..github/scripts/pr_review.main.kts(invoked withMODE=changelog): Readschangelog_errors.txtfrom the artifact directory. If the file is present, it stales any existing comment and posts a new one, then adds the label. If the file is absent (check passed), it stales any existing comment and removes the label.
When a pull request declares dependencies in its ## Dependencies section, the Waiting on Dependency PR label is automatically added or
removed based on whether any listed dependencies are still open.
Two dependency formats are supported:
- #<pr number>for same-repository PRs- <url>for external repository PRs
Both are read only from the ## Dependencies section, never from the rest of the description. The section starts at the heading, may be
followed by blank lines, and ends at the first line that does not start with - . Text after a valid entry is ignored, so
- #1234 (needed for the item API) is recognized. A line matching neither format is ignored.
Dependencies on hannibal002/SkyHanni-REPO are explicitly excluded from the open check, as that repository is considered part of the same
release unit.
The section itself can also be wrong: an entry that fails to resolve, for example through a mistyped number, a line matching neither format, the template placeholder left in place, or the heading appearing twice, in which case only the first one is read. The comment then shows only the problems, because the state of the other dependencies says nothing useful while the section is broken, and the commit status fails. The label still follows the genuinely open dependencies.
A comment is posted when a PR starts waiting, when the list of open dependencies changes, when a problem with the section appears or disappears, and when a dependency PR is closed. It names the closed PR if the run was triggered by one, followed by the dependencies that are still open, or by the note that the PR is no longer waiting on any. A run that has nothing new to announce produces no comment.
The check runs on every opened, edited, closed, and synchronize event via pull_request_target. On closed, all open PRs currently
carrying the label are re-evaluated so the label is removed from dependent PRs when their dependency merges.
Known limitation: if a dependency PR in an external repository merges, the workflow does not fire for that repository. The label on the dependent PR remains until the PR itself is edited or another supported event occurs.
Known limitation: the heading is matched line by line without looking at markdown structure. A description that quotes
## Dependencies a second time, for example inside a fenced code block, is reported as having a duplicate heading.
Relevant files: .github/workflows/check_dependencies.yml, .github/scripts/pr_review.main.kts.
Some labels are controlled by keywords in the pull request description. Writing the keyword on its own line adds the label, removing the line removes it again. Both directions post a comment on the PR explaining what happened, but only when the state actually changed compared to what the last comment announced. Editing the description without touching the keyword line posts nothing.
Supported keywords:
waiting_on_hypixel_alphaadds theWaiting on Hypixellabel. Use it when the relevant feature is only available on the Hypixel alpha server, so the pull request can only be tested there and must not be merged before the feature reaches the main server. This keyword also publishes a failing commit status while the line is present.
The line has to match exactly, the same way exclude_from_changelog works. Leading or trailing spaces, list markers such as - , and
a different capitalization all prevent the keyword from being recognized.
The check runs on every opened, edited, reopened, and synchronize event via pull_request_target.
Relevant files: .github/workflows/keyword-labels.yml, .github/scripts/pr_review.main.kts (invoked with MODE=keyword_labels).
You may want to use private Minecraft methods or fields, this is where access wideners come in.
Access wideners are a way to access private methods and fields in Minecraft classes. They are used to modify the access level of a method or
field and allow it to be accessed from other classes. This is an easier alternative to using mixins and making an accessor.
To get an access widener entry, you can use the Minecraft Development plugin for IntelliJ. Then you can right-click on a method or field and
select Copy / Paste Special -> AW Entry and paste this into the bottom
of src/main/resources/skyhanni.classtweaker.
Then you need to reload Gradle for the changes to apply.
This requires you to have the Minecraft Development plugin installed as mentioned earlier.






