Skip to content

Commit

Permalink
feat(docs): Create 1.21.3 version
Browse files Browse the repository at this point in the history
  • Loading branch information
ChampionAsh5357 committed Dec 3, 2024
1 parent 840a612 commit 57563ac
Show file tree
Hide file tree
Showing 86 changed files with 17,291 additions and 0 deletions.
3 changes: 3 additions & 0 deletions docusaurus.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ const config = {
includeCurrentVersion: true,
versions: {
current: {
label: "1.21.4",
},
"1.21.3": {
label: "1.21.2 - 1.21.3",
},
"1.21.1": {
Expand Down
4 changes: 4 additions & 0 deletions versioned_docs/version-1.21.3/advanced/_category_.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"label": "Advanced Topics",
"position": 12
}
147 changes: 147 additions & 0 deletions versioned_docs/version-1.21.3/advanced/accesstransformers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# Access Transformers

Access Transformers (ATs for short) allow for widening the visibility and modifying the `final` flags of classes, methods, and fields. They allow modders to access and modify otherwise inaccessible members in classes outside their control.

The [specification document][specs] can be viewed on the NeoForged GitHub.

## Adding ATs

Adding an Access Transformer to your mod project is as simple as adding a single line into your `build.gradle`:

Access Transformers need to be declared in `build.gradle`. AT files can be specified anywhere as long as they are copied to the `resources` output directory on compilation.

```groovy
// In build.gradle:
// This block is where your mappings version is also specified
minecraft {
accessTransformers {
file('src/main/resources/META-INF/accesstransformer.cfg')
}
}
```

By default, NeoForge will search for `META-INF/accesstransformer.cfg`. If the `build.gradle` specifies access transformers in any other location, then their location needs to be defined within `neoforge.mods.toml`:

```toml
# In neoforge.mods.toml:
[[accessTransformers]]
## The file is relative to the output directory of the resources, or the root path inside the jar when compiled
## The 'resources' directory represents the root output directory of the resources
file="META-INF/accesstransformer.cfg"
```

Additionally, multiple AT files can be specified and will be applied in order. This can be useful for larger mods with multiple packages.

```groovy
// In build.gradle:
minecraft {
accessTransformers {
file('src/main/resources/accesstransformer_main.cfg')
file('src/additions/resources/accesstransformer_additions.cfg')
}
}
```

```toml
# In neoforge.mods.toml
[[accessTransformers]]
file="accesstransformer_main.cfg"

[[accessTransformers]]
file="accesstransformer_additions.cfg"
```

After adding or modifying any Access Transformer, the Gradle project must be refreshed for the transformations to take effect.

## The Access Transformer Specification

### Comments

All text after a `#` until the end of the line will be treated as a comment and will not be parsed.

### Access Modifiers

Access modifiers specify to what new member visibility the given target will be transformed to. In decreasing order of visibility:

- `public` - visible to all classes inside and outside its package
- `protected` - visible only to classes inside the package and subclasses
- `default` - visible only to classes inside the package
- `private` - visible only to inside the class

A special modifier `+f` and `-f` can be appended to the aforementioned modifiers to either add or remove respectively the `final` modifier, which prevents subclassing, method overriding, or field modification when applied.

:::danger
Directives only modify the method they directly reference; any overriding methods will not be access-transformed. It is advised to ensure transformed methods do not have non-transformed overrides that restrict the visibility, which will result in the JVM throwing an error.

Examples of methods that can be safely transformed are `private` methods, `final` methods (or methods in `final` classes), and `static` methods.
:::

### Targets and Directives

#### Classes

To target classes:

```
<access modifier> <fully qualified class name>
```

Inner classes are denoted by combining the fully qualified name of the outer class and the name of the inner class with a `$` as separator.

#### Fields

To target fields:

```
<access modifier> <fully qualified class name> <field name>
```

#### Methods

Targeting methods require a special syntax to denote the method parameters and return type:

```
<access modifier> <fully qualified class name> <method name>(<parameter types>)<return type>
```

##### Specifying Types

Also called "descriptors": see the [Java Virtual Machine Specification, SE 21, sections 4.3.2 and 4.3.3][jvmdescriptors] for more technical details.

- `B` - `byte`, a signed byte
- `C` - `char`, a Unicode character code point in UTF-16
- `D` - `double`, a double-precision floating-point value
- `F` - `float`, a single-precision floating-point value
- `I` - `integer`, a 32-bit integer
- `J` - `long`, a 64-bit integer
- `S` - `short`, a signed short
- `Z` - `boolean`, a `true` or `false` value
- `[` - references one dimension of an array
- Example: `[[S` refers to `short[][]`
- `L<class name>;` - references a reference type
- Example: `Ljava/lang/String;` refers to `java.lang.String` reference type _(note the use of slashes instead of periods)_
- `(` - references a method descriptor, parameters should be supplied here or nothing if no parameters are present
- Example: `<method>(I)Z` refers to a method that requires an integer argument and returns a boolean
- `V` - indicates a method returns no value, can only be used at the end of a method descriptor
- Example: `<method>()V` refers to a method that has no arguments and returns nothing

### Examples

```
# Makes public the ByteArrayToKeyFunction interface in Crypt
public net.minecraft.util.Crypt$ByteArrayToKeyFunction
# Makes protected and removes the final modifier from 'random' in MinecraftServer
protected-f net.minecraft.server.MinecraftServer random
# Makes public the 'makeExecutor' method in Util,
# accepting a String and returns a TracingExecutor
public net.minecraft.Util makeExecutor(Ljava/lang/String;)Lnet/minecraft/TracingExecutor;
# Makes public the 'leastMostToIntArray' method in UUIDUtil,
# accepting two longs and returning an int[]
public net.minecraft.core.UUIDUtil leastMostToIntArray(JJ)[I
```

[specs]: https://github.com/NeoForged/AccessTransformers/blob/main/FMLAT.md
[jvmdescriptors]: https://docs.oracle.com/javase/specs/jvms/se21/html/jvms-4.html#jvms-4.3.2
148 changes: 148 additions & 0 deletions versioned_docs/version-1.21.3/advanced/extensibleenums.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
# Extensible Enums

Extensible Enums are an enhancement of specific Vanilla enums to allow new entries to be added. This is done by modifying the compiled bytecode of the enum at runtime to add the elements.

## `IExtensibleEnum`

All enums that can have new entries implement the `IExtensibleEnum` interface. This interface acts as a marker to allow the `RuntimeEnumExtender` launch plugin service to know what enums should be transformed.

:::warning
You should **not** be implementing this interface on your own enums. Use maps or registries instead depending on your usecase.
Enums which are not patched to implement the interface cannot have the interface added to them via mixins or coremods due to the order the transformers run in.
:::

### Creating an Enum Entry

To create new enum entries, a JSON file needs to be created and referenced in the `neoforge.mods.toml` with the `enumExtensions` entry of a `[[mods]]` block. The specified path must be relative to the `resources` directory:
```toml
# In neoforge.mods.toml:
[[mods]]
## The file is relative to the output directory of the resources, or the root path inside the jar when compiled
## The 'resources' directory represents the root output directory of the resources
enumExtensions="META-INF/enumextensions.json"
```

The definition of the entry consists of the target enum's class name, the new field's name (must be prefixed with the mod ID), the descriptor of the constructor to use for constructing the entry and the parameters to be passed to said constructor.

```json5
{
"entries": [
{
// The enum class the entry should be added to
"enum": "net/minecraft/world/item/ItemDisplayContext",
// The field name of the new entry, must be prefixed with the mod ID
"name": "EXAMPLEMOD_STANDING",
// The constructor to be used
"constructor": "(ILjava/lang/String;Ljava/lang/String;)V",
// Constant parameters provided directly.
"parameters": [ -1, "examplemod:standing", null ]
},
{
"enum": "net/minecraft/world/item/Rarity",
"name": "EXAMPLEMOD_CUSTOM",
"constructor": "(ILjava/lang/String;Ljava/util/function/UnaryOperator;)V",
// The parameters to be used, provided as a reference to an EnumProxy<Rarity> field in the given class
"parameters": {
"class": "example/examplemod/MyEnumParams",
"field": "CUSTOM_RARITY_ENUM_PROXY"
}
},
{
"enum": "net/minecraft/world/damagesource/DamageEffects",
"name": "EXAMPLEMOD_TEST",
"constructor": "(Ljava/lang/String;Ljava/util/function/Supplier;)V",
// The parameters to be used, provided as a reference to a method in the given class
"parameters": {
"class": "example/examplemod/MyEnumParams",
"method": "getTestDamageEffectsParameter"
}
}
]
}
```

```java
public class MyEnumParams {
public static final EnumProxy<Rarity> CUSTOM_RARITY_ENUM_PROXY = new EnumProxy<>(
Rarity.class, -1, "examplemod:custom", (UnaryOperator<Style>) style -> style.withItalic(true)
);

public static Object getTestDamageEffectsParameter(int idx, Class<?> type) {
return type.cast(switch (idx) {
case 0 -> "examplemod:test";
case 1 -> (Supplier<SoundEvent>) () -> SoundEvents.DONKEY_ANGRY;
default -> throw new IllegalArgumentException("Unexpected parameter index: " + idx);
});
}
}
```

#### Constructor

The constructor must be specified as a [method descriptor][jvmdescriptors] and must only contain the parameters visible in the source code, omitting the hidden constant name and ordinal parameters.
If a constructor is marked with the `@ReservedConstructor` annotation, then it cannot be used for modded enum constants.

#### Parameters

The parameters can be specified in three ways with limitations depending on the parameter types:

- Inline in the JSON file as an array of constants (only allowed for primitive values, Strings and for passing null to any reference type)
- As a reference to a field of type `EnumProxy<TheEnum>` in a class from the mod (see `EnumProxy` example above)
- The first parameter specifies the target enum and the subsequent parameters are the ones to be passed to the enum constructor
- As a reference to a method returning `Object`, where the return value is the parameter value to use. The method must have exactly two parameters of type `int` (index of the parameter) and `Class<?>` (expected type of the parameter)
- The `Class<?>` object should be used to cast (`Class#cast()`) the return value in order to keep `ClassCastException`s in mod code.

:::warning
The fields and/or methods used as sources for parameter values should be in a separate class to avoid unintentionally loading mod classes too early.
:::

Certain parameters have additional rules:

- If the parameter is an int ID parameter related to a `@IndexedEnum` annotation on the enum, then it is ignored and replaced by the entry's ordinal. If said parameter is specified inline in the JSON, then it must be specified as `-1`, otherwise an exception is thrown.
- If the parameter is a String name parameter related to a `@NamedEnum` annotation on the enum, then it must be prefixed by the mod ID in the `namespace:path` format known from `ResourceLocation`s, otherwise an exception is thrown.

#### Retrieving the Generated Constant

The generated enum constant can be retrieved via `TheEnum.valueOf(String)`. If a field reference is used to provide the parameters, then the constant can also be retrieved from the `EnumProxy` object via `EnumProxy#getValue()`.

## Contributing to NeoForge

To add a new extensible enum to NeoForge, there are at least two required things to do:

- Make the enum implement `IExtensibleEnum` to mark that this enum should be transformed via the `RuntimeEnumExtender`.
- Add a `getExtensionInfo` method that returns `ExtensionInfo.nonExtended(TheEnum.class)`.

Further action is required depending on specific details about the enum:

- If the enum has an int ID parameter which should match the entry's ordinal, then the enum should be annotated with `@NumberedEnum` with the ID's parameter index as the annotation's value if it's not the first parameter
- If the enum has a String name parameter which is used for serialization and should therefore be namespaced, then the enum should be annotated with `@NamedEnum` with the name's parameter index as the annotation's value if it's not the first parameter
- If the enum is sent over the network, then it should be annotated with `@NetworkedEnum` with the annotation's parameter specifying in which direction the values may be sent (clientbound, serverbound or bidirectional)
- If the enum has constructors which are not usable by mods (i.e. because they require registry objects on an enum that may be initialized before modded registration runs), then they should be annotated with `@ReservedConstructor`

:::note
The `getExtensionInfo` method will be transformed at runtime to provide a dynamically generated `ExtensionInfo` if the enum actually had any entries added to it.
:::

```java
// This is an example, not an actual enum within Vanilla
public enum ExampleEnum implements net.neoforged.fml.common.asm.enumextension.IExtensibleEnum {
// VALUE_1 represents the name parameter here
VALUE_1(0, "value_1", false),
VALUE_2(1, "value_2", true),
VALUE_3(2, "value_3");

ExampleEnum(int arg1, String arg2, boolean arg3) {
// ...
}

ExampleEnum(int arg1, String arg2) {
this(arg1, arg2, false);
}

public static net.neoforged.fml.common.asm.enumextension.ExtensionInfo getExtensionInfo() {
return net.neoforged.fml.common.asm.enumextension.ExtensionInfo.nonExtended(ExampleEnum.class);
}
}
```

[jvmdescriptors]: https://docs.oracle.com/javase/specs/jvms/se21/html/jvms-4.html#jvms-4.3.2
Loading

1 comment on commit 57563ac

@neoforged-pages-deployments
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Deploying with Cloudflare Pages

Name Result
Last commit: 57563ac330b949f5bbbc2467ccfb6d688ca0ecfa
Status: ✅ Deploy successful!
Preview URL: https://2e470032.neoforged-docs-previews.pages.dev
PR Preview URL: https://pr-211.neoforged-docs-previews.pages.dev

Please sign in to comment.