-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
118 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
Strings: Untying the Knot | ||
============================= | ||
|
||
If you've come this far without knowing what [Strings](https://en.wikipedia.org/wiki/String_(computer_science)) are, I applaud you. As Variables are primarily String driven, a suite of conditions exist for manipulating them. | ||
|
||
These literally call the Java function for the String and inserts the result into a variable. Find the function documentation [here](https://docs.oracle.com/javase/8/docs/api/java/lang/String.html). | ||
|
||
| Name | ID | | ||
| ------------------------------------ | ---------------- | | ||
| [StringConcatCondition](url.tbd) | `stringConcat` | | ||
| [StringCompareToCondition](url.tbd) | `stringCompare` | | ||
| [StringContainsCondition](url.tbd) | `stringContains` | | ||
| [StringEndsWithCondition](url.tbd) | `stringEnds` | | ||
| [StringEqualsCondition](url.tbd) | `stringEquals` | | ||
| [StringIndexOfCondition](url.tbd) | `stringIndex` | | ||
| [StringLowerCaseCondition](url.tbd) | `stringLower` | | ||
| [StringReplaceCondition](url.tbd) | `stringReplace` | | ||
| [StringSplitCondition](url.tbd) | `stringSplit` | | ||
| [StringStartsWithCondition](url.tbd) | `stringStarts` | | ||
| [StringSubStringCondition](url.tbd) | `stringSubstr` | | ||
| [StringTrimCondition](url.tbd) | `stringTrim` | | ||
| [StringUpperCaseCondition](url.tbd) | `stringUpper` | | ||
| [StringLengthCondition](url.tbd) | `stringLength` | | ||
|
||
|
||
However, there are also a few special Conditions made to handle Strings: | ||
|
||
| Name | ID | Description | | ||
| -------------------------------- | -------------- | ------------------------------------------------------- | | ||
| [Base64DecodeCondition](url.tbd) | `base64Decode` | Decodes a Base64 String | | ||
| [Base64EncodeCondition](url.tbd) | `base64Encode` | Encodes a String into a Base64 String | | ||
| [JsonSafeCondition](url.tbd) | `jsonSafe` | Converts a String to be safe to insert into JSON files. | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
Game Event Hooks: The Config Maker's Nightmare | ||
====================================================== | ||
|
||
**Warning**: You will need a [Forge Development Environment](https://mcforge.readthedocs.io/en/1.16.x/gettingstarted/#from-zero-to-modding) for this. This feature is not available on Fabric builds of CCI. | ||
|
||
## Game Event Hooks: An Introduction | ||
|
||
Game Event Hooks, or which I will refer to more frequently, Game Hooks, is a special layer that CCI adds to hook into things that happen in the game using the [Forge Event system](https://github.com/MinecraftForge/MinecraftForge/tree/1.16.x/src/main/java/net/minecraftforge/event). | ||
|
||
Understanding how to use this will require a decent grasp of Java, Java's [Reflection](https://www.oracle.com/technical-resources/articles/java/javareflection.html), and modding with Forge. | ||
|
||
Game Hooks is primarily driven by Reflection, and has three helper classes to aid it: | ||
|
||
| Class Name | Function | | ||
| ------------------------- | ----------------------------------------------------------------------------------------------------------------- | | ||
| [Listener](url.tbd) | Checks Forge's Events if it matches what you're looking for and passes the Event to ObjectAccessors | | ||
| [ObjectAccessor](url.tbd) | Used to access fields or functions in an Object's class, which is then passed into its own set of ObjectAccessors | | ||
| [ParamInjector](url.tbd) | Used to provide arguments types (and their objects) for method invoking | | ||
|
||
These three Classes will be outlined better in on their documentation page. This page's aim is more to describe the practical approach of using them. | ||
|
||
<br /> | ||
|
||
## The Helpers, and How to Use Them | ||
|
||
I find that the best way to show how these work, is by walking you through an example. In this example, I will show how to make a Pig jump upwards when you punch it. I will be using [IntelliJ IDEA](https://www.jetbrains.com/idea/) and MCP mappings. This guide also requires a **minimum of CCI 1.9.0** or later. | ||
|
||
### The Flow of Thought | ||
|
||
Let's have a look at a new Listener in the Editor first: | ||
|
||
![](./images/gamehooks/listener.png){: class="img_center"} | ||
<br /> | ||
<br /> | ||
|
||
Listeners will require the Class name (including the parent class, if this is an inner class) (name: `className`) and optionally the full package path (name: `isFullName`) of the Forge Event you would like to listen to. | ||
|
||
For this, we will need to listen for the `LivingHurtEvent` (package: `net.minecraftforge.event.entity.living`). Go ahead and create a listener and set the `className`. | ||
|
||
For this to also work on Servers, you will need to tell the Listener that we're listening to an event triggered on the server (name: `isServerEvent`). | ||
|
||
Next up we have the `preAccessorEvent`. This is an optional Config Event that you can create just to populate the variables before the rest is triggered. Variables here are a bit different. Usually, the variables list contains an assortment of `int`, `double`, `boolean`, `String`, `Object[]` types. Here, our variables will contain Objects themselves. There is one special variable called `stopEventProcessing`. If this variable exists at any point between ObjectAccessors, processing is stopped. | ||
|
||
After that, we have the `ObjectAccessor` fields, starting with `staticAccessors`. What `staticAccessors` is for is to allow you to retrieve or call static fields or methods to add to the variables list before actually referencing the actual Forge Event. You may need these objects for use with `ParamInjector`. These Object Accessors will need the full class path set in `classForStaticAccess`, as there is no object reference for them to access currently. We don't need any static accessors here, so let's move on. | ||
|
||
Now, we're at the `accessor` field. These ObjectAccessors will have the Forge Event object passed to them. We will need an `ObjectAccessor` here, so let's make one. This is what it'll look like in the Editor: | ||
|
||
![](./images/gamehooks/objectaccessor.png){: class="img_center"} | ||
<br /> | ||
<br /> | ||
|
||
For readibility sake, we'll name this Object Accessor `ForgeEvent:getEntityLiving` (name: `name`). Now, move to your [IDE](https://en.wikipedia.org/wiki/Integrated_development_environment) and look at `LivingHurtEvent`. Note how it extends `LivingEvent`. We want to get the entity that got hurt here, and we get that from the function `getEntityLiving()`. Let's put that in our Object Accessor in `function`. | ||
|
||
We know we want a Pig here, so for `instanceCheck`, we put in the full class name for the pig, `net.minecraft.entity.passive.PigEntity`. We also want to keep the pig object reference, so let's keep it in a variable called `pigEntity`, in the `argName` field. | ||
|
||
Before I move on, here is where it gets a little complicated. Minecraft code is obfuscated, and Forge deobfuscates things. In runtime, all the fields and functions are mapped to something called Searge names (srgName), meaning the field names start with `field_`, and the method names start with `func_`, but in our IDE we see the deobfuscated name, like `world` or `getName`, which are MCP names. For the fields in ObjectAccessor, use Searge names. I will put the ones used in this guide as reference here, but if you want to convert the mapping names, consider checking out the `forge-bot` user in the Minecraft Forge discord (URL in their [Twitter bio](https://twitter.com/forgedevteam)). | ||
|
||
Now, although we marked the event as triggering on a Server, that only makes things work for clients when connected to on Dedicated Servers. We still need to make sure that the Event is triggering on the entity on the Server side. Add a new `ObjectAccessor` in `nextAccessors`. These set of accessors will have the pig object as reference. | ||
|
||
To check if we're on the Server side of things, the easy way is to check the `isRemote` (srgName: `field_72995_K`) field of the World object of the Entity. So now we need to get a reference to the World object via method `getEntityWorld()` (srgName: `func_130014_f_()`), or from accessing the `world` (srgName: `field_145850_b`) field. Either way is up to you, but I will be using the field. Set this in the ObjectAccessor. Don't forget to name your ObjectAccessor for readability eg `Entity:world`. This ObjectAccessor will be grabbing the World object, and passing it along to its own `nextAccessors`. We aren't keeping a reference to the world here, so no `argName` is required. | ||
|
||
We're not done yet, make a new `ObjectAccessor` in `Entity:world`'s `nextAccessors`, we'll name this one `World:isRemote`. Set the `function` to `isRemote` (srgName: `field_72995_K`), and set the `argName` to `isRemote`, we will need it in a bit. Now we want to compare the `isRemote` value. This is a `boolean`, so it is either `true` or `false`. We will be checking to see if it is `true`, and if it is, to stop processing. | ||
|
||
Make a new Event in `postAccessorEvent`. Add a `VariableCondition` (id: `variableCheck`), set `variableName` to the `argName` we set earlier: `isRemote`, and `variableResult` as `true`. Now, make a new `VariableInsertCondition` (id: `variableInsert`), and set `variableName` to `stopEventProcessing`. `variableInput` can be anything except for `null`, else it is considered invalid. What we've just done is checked if `isRemote` is true, and if it is, the Event is on the client side, and to stop processing. | ||
|
||
We've done our checks, now to make the pig jump. Go back to `ForgeEvent:getEntityLiving` and add a new `ObjectAccessor` in `nextAccessors`. This is where we make the pig "jump". I'll be naming this ObjectAccessor `Entity:setMotion`. Here's where things get a little special, `Entity`'s `setMotion` (srgName: `func_213317_d`) method takes a `Vector3d` argument. | ||
|
||
Backtrack a bit and return to `staticAccessors` in the `Listener` and add a new one `ObjectAccessor`. This one, we'll call `Vector3d:init`, set the `classForStaticAccess` as `net.minecraft.util.math.vector.Vector3d` and for the function, we put in `<>`. This tells CCI that you'd like to use a Constructor for the Class `Vector3d`. We will need 3 `ParamInjector`s here, so, create them. For all three, set `isPrimitive` to `true` and the `classType` to `double`. Set the `argToPull` for the first and third as `0`, and for the second, `1.5`. Go back to `Vector3d:init` and set the `argName` as `velocity`. What we've just done is we've created a `Vector3d` Object with `x = 0; y = 1.5; z = 0;`. However, as we're using `argToPull` to define the primitive, you must ensure the variable does not exist, as it will pull that variable instead, first. | ||
|
||
Go back to the `Entity:setMotion` ObjectAccessor and create a `ParamInjector`. Here, set the `classType` as `net.minecraft.util.math.vector.Vector3d` and `argToPull` as `velocity`. You've just told CCI to call the method `setMotion(Vector3d)`, and to use the `Vector3d` we have in the variable `velocity`. | ||
|
||
And that's all to it. Save, and test. If done right, the next time you punch a pig, it should shoot up into the sky. | ||
|
||
For your reference, your Editor should look something like this once you're done: | ||
|
||
TODO insert picture and JSON. | ||
|
||
|
||
Attached is the JSON file generated from this so you can reference in case something went wrong: |
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters