iamamyfoster
iamamyfoster
AmyF
2 posts
Ive decided to make this my little programmy and occasional writy area
Don't wanna be here? Send us removal request.
iamamyfoster · 2 years ago
Text
MCM; Light Client Mod
Recently, I was playing on a server, and creating a farm, but I just couldn't stop jumping - for some reason, I just got an urge to lay my thumb on the jump key. So, unlike a normal person, who would most likely ignore the urge, I decided to create a mod to solve the issue.
I have previously worked a little bit using Fabric to mod, although I've never fully completed anything. During the process of rediscovering the setup process, I learned that Fabric's site has a page which allows you to generate a template for your mod, which kickstarts development. Therefore, using this tool, I downloaded the template, and opened up the folder as a project using IntelliJ IDEA, which is my chosen java development environment.
Firstly, it is worth noting that Fabric provides two separate entrypoints - one for the client, and one for the server (note that minecraft runs a server in singleplayer, but it is locally executed behind the scenes; I would postulate that this is what makes opening a singleplayer world to the LAN so easy while in-game).
Since this mod is only going to interact with the client's controls/input, and nothing needs to be noticed server-side, none of the server-side java code is necessary. However, deleting the full "main" module of the project isn't going to work for us, because the "fabric.mod.json" file is a part of the resources there, and defines the location of both the client and server entrypoint files. Deleting this, and other files in that region of the file structure, will cause the mod to stop working entirely.
Therefore, the first order of business is to remove the serverside entrypoint, as that just adds bloatware to what really only needs to be a small clientside mod. This, I did by entering the "fabric.mod.json" file, and removing the "entrypoints/main" key-value (the value should be a list/array of entrypoints, and should contain just one to begin with). This leaves a single client entrypoint.
Furthermore, since the template project comes with a default mixin, references to this must also be removed. To do this, I went into the "<modname>.mixins.json" file, which should be adjacent to "fabric.mod.json", and removed all references within the "mixins" list. After this, the "main/java" directory was deleted.
Now that the server/main-side of the mod has been removed, work can begin on the proper body of the mod. The plan is to add a key which allows the player to toggle an option, allowing and disallowing the player to jump on farmland. To do so, we need a keybinding, a variable to toggle, and a way of interfering with the player's motion. The toggleable variable, obviously will be a boolean (true/false) type. To interfere with the player's motion, something called a "mixin" will be used.
Mixins are used to inject our own code into existing methods in the game's code. In this case, we want to inject a little bit of code which checks whether the player is on top of farmland, and, if so, then make sure the player can't jump. Therefore, we need to find which method controls the player's jumping/motion.
Now, if you don't like the odd tangent, feel free to skip ahead to the next paragraph, where all the answers are give. Fair warning. To find which method we should use to stop the player from jumping, we need to see how the code which controls their motion. Thankfully, most of what we need to know can be found in the game's sources, which are viewable as decompiled .class files. To find the relevant files, go to ".gradle/loom-cache/minecraftMaven/net/minecraft/minecraft-clientOnly-<charsHere>/1.20.2-net.fabricmc.yarn.<blablabla>/minecraft-clientOnly-<blabla>.jar/net/minecraft/client/input". Here, there should be a couple files: "CursorMovement", "Input", "KeyboardInput", and "KeyCodes". Obviously, since we're looking to affect jumping, the relevant file will be either "Input", or "KeyboardInput". Viewing the code, while "Input" doesn't contain much of anything, "KeyboardInput" extends from "Input", and accesses which keys are currently being pressed inside the "tick" method.
Given this, our project plan is pretty well laid out: create a variable, create a keybind, create a mixin to "KeyboardInput.tick". Great! Let's start with the variable and keybind:
Assuming you've been anywhere near a java file before, declaring a variable should be pretty easy. This could easily be done in any custom file, but since it's pretty much the only thing we're going to store, I'm just going to define it in the "<modname>Client.java" file (this should be the client entrypoint defined in "fabric.mod.json"). In this case, I've made the variable "public static boolean allowFarmJump = true;".
Since this is a simple little mod, and I'm currently the only person who's going to be using it, I'm not going to bother storing the current value between sessions. The variable is going to be static, just to make things a little easier when accessing it from other files. I've also decided to make its default value "true", but this is mostly a preferential decision on my part.
Next: the keybind. Fabric's documentation is pretty short and sweet on the matter, but it does contain a little bit hidden away that I didn't notice at first: "This tutorial assumes you have the key bindings API, if not add "fabric-key-binding-api-v1": "*" to the "depends" block in your fabric.mod.json file".
After adding the key bindings API, it's time to add the keybinding itself. Using the KeyBindingHelper described in the docs, I created and registered a KeyBinding with the translation key "key.<modname>.toggle", default key of "backslash" (GLFW.GLFW_KEY_BACKSLASH), and category of "key.categories.movement" (this is the category used by vanilla's "Movement" controls category, where I want this to display).
After registering the keybind, I continued to go through the docs and added the "END_CLIENT_TICK" listener + while loop on the keybinding (remember to replace the "keyBinding" in the example code with the name of your own keybinding variable). In the while loop, this is where the "allowFarmJump" variable is being toggled ("allowFarmJump = !allowFarmJump;"). I also added feedback in the form of text appearing above the hotbar: "client.inGameHud.setOverlayMessage(Text.literal("Farm jumping set to: %b".formatted(allowFarmJump)), false);".
At this point, the variable toggling and feedback message are running fine, but we haven't actually implemented anything to prevent the player jumping on farmland. This is where the mixin comes into play:
Under the "client/java/<modpackage>/mixin/client" directory, open the example mixin. Of course, this is currently injecting into the wrong class and method, so change the "@Mixin" decorator argument to "KeyboardInput.class". Then, modify the "@Inject" method decorator to "@Inject(at = @At("TAIL"), method = "tick", locals = LocalCapture.CAPTURE_FAILHARD)". This will take the code inside the decorated method and inject it at the end of the KeyboardInput.tick method. Then, make the mixin class extend from "Input" (this is so that the "jumping" property can be accessed).
Now, inside the mixin method, we need to set the jumping variable to "jumping ∧ (allowFarmJump ∨ playerFlying ∨ ¬onFarmland)" (this notation may be unfamiliar: "∧" represents a logical AND, "∨" represents a logical OR, and "¬" represents a logical NOT). The reason for the added check with regards to whether the player is flying accounts for the possibility that the player might be in creative or spectator mode. If this were the case, and jumping were disabled, flying up would be similarly disabled.
Collecting all the required values, the assignment expression we're left with should look something along these lines: "jumping &= AntiFarmJumpClient.allowFarmJump || MinecraftClient.getInstance().player.getAbilities().flying || !(MinecraftClient.getInstance().world.getBlockState(MinecraftClient.getInstance().player.getBlockPos()).getBlock() instanceof FarmlandBlock);".
Note the assignment operator "&=", which is basically like "+=" or "*=", etc. except instead of adding or multiplying, it performs an AND operation (this is a bitwise operator, but it works on booleans as long as both sides are of the same type).
Having successfully intercepted and modified the jumping controls, when the program runs this time, the keybinding should now allow you to stop the jump key working while on top of farmland, unless the player is flying. Note that to build the mod to a .jar file, you need to run the "build" gradle task (these are found at the top of the right-hand sidebar, for me). This builds the jar file and outputs it to the "build/libs/" directory. The correct file is the one without the "-sources" at the end.
Feel free to give feedback on how this could be improved in future, or ask questions!
Fabric
0 notes
iamamyfoster · 2 years ago
Text
Hi!
I tried my hand at writing a 2nd person piece, thoughts? ———— Wind, softly rustling through the foliage around you, plays with your hair, and you push it back, breathing deeply. Though you are tired, and night has long since blanketed the forest, you feel exhilarated, and your eyes glimmer with joy and awe. You spread your arms through the air, and beckon inwards, drawing out icy threads of power from the transient core within you - your soul. The cool strands spread through you, energizing, and you shiver as it becomes all-encompassing. Letting it build for a few breaths, you tentatively nudge the frosty aura to spread past the end of your hand, and in response, a gentle surge reinforces the magic's presence as it dimly illuminates a chill, swirling tendril of raw power around your hand. With your eyes closed, you deepen the connection, filling yourself with potential, and release it, spirally slowly out into the air around you, as you breath out. Opening your eyes, a thrill runs through you at the beauty of the weaving strings that twist through the air, and you smile, then blow carefully through your mouth, causing several snowflakes to flurry out, drifting away. Sitting down against a rock, you gaze upwards at the intermingling stars and specks of glowing magic, idly letting your mind wander. Eventually, in the dim hues of predawn, your eyes close, and you drift off, awaiting the coming of the next night when you will again play, sinking deeper into the ocean of magic permeating the universe. With time, the sun rises, and warms away the sheen of ice that had coated the forest during the night, chasing away the early vestiges of winter that now visit under the light of the moon. And, isolated from all but the trees and the animals and the sky, as the warm glow illuminates your face, you sleep. —————
1 note · View note