-
-
Notifications
You must be signed in to change notification settings - Fork 60
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add 1.20.5 base and fix broken links
- Loading branch information
Showing
79 changed files
with
9,727 additions
and
16 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
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,3 @@ | ||
{ | ||
"label": "Advanced Topics" | ||
} |
158 changes: 158 additions & 0 deletions
158
versioned_docs/version-1.20.4/advanced/accesstransformers.mdx
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,158 @@ | ||
import Tabs from '@theme/Tabs'; | ||
import TabItem from '@theme/TabItem'; | ||
|
||
# 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`: | ||
|
||
<Tabs defaultValue="latest"> | ||
<TabItem value="latest" label="Latest"> | ||
Access Transformers need to be declared in both `build.gradle` and `mods.toml`: | ||
|
||
```groovy | ||
// In build.gradle: | ||
// This block is where your mappings version is also specified | ||
minecraft { | ||
accessTransformers { | ||
file('src/main/resources/META-INF/accesstransformer.cfg') | ||
} | ||
} | ||
``` | ||
|
||
```toml | ||
# In mods.toml: | ||
[[accessTransformers]] | ||
file="META-INF/accesstransformer.cfg" | ||
``` | ||
|
||
AT files can be anywhere specified by the lines above, though NeoForge will default to searching for `META-INF/accesstransformer.cfg` if no other file is specified. | ||
|
||
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 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. | ||
</TabItem> | ||
|
||
<TabItem value="1.20.1" label="1.20.1-47.1 and older"> | ||
Adding an Access Transformer to your mod project is as simple as adding a single line into your `build.gradle`: | ||
|
||
```groovy | ||
// This block is where your mappings version is also specified | ||
minecraft { | ||
accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg') | ||
} | ||
``` | ||
|
||
During development, the AT file can be anywhere specified by the line above. However, when loading in a non-development environment, NeoForge will only search for the exact path of `META-INF/accesstransformer.cfg` in your JAR file. | ||
|
||
After adding or modifying the Access Transformer, the Gradle project must be refreshed for the transformations to take effect. | ||
</TabItem> | ||
</Tabs> | ||
|
||
## 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 8, 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 an ExecutorService | ||
public net.minecraft.Util makeExecutor(Ljava/lang/String;)Ljava/util/concurrent/ExecutorService; | ||
# 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/se8/html/jvms-4.html#jvms-4.3.2 |
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,28 @@ | ||
BlockEntityRenderer | ||
================== | ||
|
||
A `BlockEntityRenderer` or `BER` is used to render blocks in a way that cannot be represented with a static baked model (JSON, OBJ, B3D, others). A block entity renderer requires the block to have a `BlockEntity`. | ||
|
||
Creating a BER | ||
-------------- | ||
|
||
To create a BER, create a class that inherits from `BlockEntityRenderer`. It takes a generic argument specifying the block's `BlockEntity` class. The generic argument is used in the BER's `render` method. | ||
|
||
Only one BER exists for a given `BlockEntityType`. Therefore, values that are specific to a single instance in the level should be stored in the block entity being passed to the renderer rather than in the BER itself. For example, an integer that increments every frame, if stored in the BER, will increment every frame for every block entity of this type in the level. | ||
|
||
### `render` | ||
|
||
This method is called every frame in order to render the block entity. | ||
|
||
#### Parameters | ||
* `blockEntity`: This is the instance of the block entity being rendered. | ||
* `partialTick`: The amount of time, in fractions of a tick, that has passed since the last full tick. | ||
* `poseStack`: A stack holding four-dimensional matrix entries offset to the current position of the block entity. | ||
* `bufferSource`: A rendering buffer able to access a vertex consumer. | ||
* `combinedLight`: An integer of the current light value on the block entity. | ||
* `combinedOverlay`: An integer set to the current overlay of the block entity, usually `OverlayTexture#NO_OVERLAY` or 655,360. | ||
|
||
Registering a BER | ||
----------------- | ||
|
||
In order to register a BER, you must subscribe to the `EntityRenderersEvent$RegisterRenderers` event on the mod event bus and call `#registerBlockEntityRenderer`. |
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,146 @@ | ||
Block Entities | ||
====== | ||
|
||
`BlockEntities` are like simplified `Entities` that are bound to a Block. | ||
They are used to store dynamic data, execute tick based tasks, and dynamic rendering. | ||
Some examples from vanilla Minecraft would be handling of inventories on chests, smelting logic on furnaces, or area effects on beacons. | ||
More advanced examples exist in mods, such as quarries, sorting machines, pipes, and displays. | ||
|
||
:::note | ||
`BlockEntities` aren't a solution for everything and they can cause lag when used wrongly. | ||
When possible, try to avoid them. | ||
::: | ||
|
||
## Registering | ||
|
||
Block Entities are created and removed dynamically and as such are not registry objects on their own. | ||
|
||
In order to create a `BlockEntity`, you need to extend the `BlockEntity` class. As such, another object is registered instead to easily create and refer to the *type* of the dynamic object. For a `BlockEntity`, these are known as `BlockEntityType`s. | ||
|
||
A `BlockEntityType` can be [registered][registration] like any other registry object. To construct a `BlockEntityType`, its builder form can be used via `BlockEntityType$Builder#of`. This takes in two arguments: a `BlockEntityType$BlockEntitySupplier` which takes in a `BlockPos` and `BlockState` to create a new instance of the associated `BlockEntity`, and a varargs of `Block`s which this `BlockEntity` can be attached to. Building the `BlockEntityType` is done by calling `BlockEntityType$Builder#build`. This takes in a `Type` which represents the type-safe reference used to refer to this registry object in a `DataFixer`. Since `DataFixer`s are an optional system to use for mods, this can be passed as `null`. | ||
|
||
```java | ||
// For some DeferredRegister<BlockEntityType<?>> REGISTER | ||
public static final RegistryObject<BlockEntityType<MyBE>> MY_BE = REGISTER.register("mybe", () -> BlockEntityType.Builder.of(MyBE::new, validBlocks).build(null)); | ||
|
||
// In MyBE, a BlockEntity subclass | ||
public MyBE(BlockPos pos, BlockState state) { | ||
super(MY_BE.get(), pos, state); | ||
} | ||
``` | ||
|
||
## Creating a `BlockEntity` | ||
|
||
To create a `BlockEntity` and attach it to a `Block`, the `EntityBlock` interface must be implemented on your `Block` subclass. The method `EntityBlock#newBlockEntity(BlockPos, BlockState)` must be implemented and return a new instance of your `BlockEntity`. | ||
|
||
## Storing Data within your `BlockEntity` | ||
|
||
In order to save data, override the following two methods: | ||
```java | ||
BlockEntity#saveAdditional(CompoundTag tag) | ||
|
||
BlockEntity#load(CompoundTag tag) | ||
``` | ||
These methods are called whenever the `LevelChunk` containing the `BlockEntity` gets loaded from/saved to a tag. | ||
Use them to read and write to the fields in your block entity class. | ||
|
||
:::note | ||
Whenever your data changes, you need to call `BlockEntity#setChanged`; otherwise, the `LevelChunk` containing your `BlockEntity` might be skipped while the level is saved. | ||
::: | ||
|
||
:::danger | ||
It is important that you call the `super` methods! | ||
|
||
The tag names `id`, `x`, `y`, `z`, `ForgeData` and `ForgeCaps` are reserved by the `super` methods. | ||
::: | ||
|
||
## Ticking `BlockEntities` | ||
|
||
If you need a ticking `BlockEntity`, for example to keep track of the progress during a smelting process, another method must be implemented and overridden within `EntityBlock`: `EntityBlock#getTicker(Level, BlockState, BlockEntityType)`. This can implement different tickers depending on which logical side the user is on, or just implement one general ticker. In either case, a `BlockEntityTicker` must be returned. Since this is a functional interface, it can just take in a method representing the ticker instead: | ||
|
||
```java | ||
// Inside some Block subclass | ||
@Nullable | ||
@Override | ||
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> type) { | ||
return type == MyBlockEntityTypes.MYBE.get() ? MyBlockEntity::tick : null; | ||
} | ||
|
||
// Inside MyBlockEntity | ||
public static void tick(Level level, BlockPos pos, BlockState state, MyBlockEntity blockEntity) { | ||
// Do stuff | ||
} | ||
``` | ||
|
||
:::note | ||
This method is called each tick; therefore, you should avoid having complicated calculations in here. If possible, you should make more complex calculations every X ticks. (The amount of ticks in a second may be lower then 20 (twenty) but won't be higher) | ||
::: | ||
## Synchronizing the Data to the Client | ||
There are three ways of syncing data to the client: synchronizing on chunk load, on block updates, and with a custom network message. | ||
### Synchronizing on LevelChunk Load | ||
For this you need to override | ||
```java | ||
BlockEntity#getUpdateTag() | ||
IForgeBlockEntity#handleUpdateTag(CompoundTag tag) | ||
``` | ||
Again, this is pretty simple, the first method collects the data that should be sent to the client, | ||
while the second one processes that data. If your `BlockEntity` doesn't contain much data, you might be able to use the methods out of the [Storing Data within your `BlockEntity`][storing-data] section. | ||
|
||
:::caution | ||
Synchronizing excessive/useless data for block entities can lead to network congestion. You should optimize your network usage by sending only the information the client needs when the client needs it. For instance, it is more often than not unnecessary to send the inventory of a block entity in the update tag, as this can be synchronized via its [`AbstractContainerMenu`][menu]. | ||
::: | ||
|
||
### Synchronizing on Block Update | ||
|
||
This method is a bit more complicated, but again you just need to override two or three methods. | ||
Here is a tiny example implementation of it: | ||
```java | ||
@Override | ||
public CompoundTag getUpdateTag() { | ||
CompoundTag tag = new CompoundTag(); | ||
//Write your data into the tag | ||
return tag; | ||
} | ||
|
||
@Override | ||
public Packet<ClientGamePacketListener> getUpdatePacket() { | ||
// Will get tag from #getUpdateTag | ||
return ClientboundBlockEntityDataPacket.create(this); | ||
} | ||
|
||
// Can override IForgeBlockEntity#onDataPacket. By default, this will defer to the #load. | ||
``` | ||
The static constructors `ClientboundBlockEntityDataPacket#create` takes: | ||
|
||
* The `BlockEntity`. | ||
* An optional function to get the `CompoundTag` from the `BlockEntity`. By default, this uses `BlockEntity#getUpdateTag`. | ||
|
||
Now, to send the packet, an update notification must be given on the server. | ||
```java | ||
Level#sendBlockUpdated(BlockPos pos, BlockState oldState, BlockState newState, int flags) | ||
``` | ||
The `pos` should be your `BlockEntity`'s position. | ||
For `oldState` and `newState`, you can pass the current `BlockState` at that position. | ||
`flags` is a bitmask that should contain `2`, which will sync the changes to the client. See `Block` for more info as well as the rest of the flags. The flag `2` is equivalent to `Block#UPDATE_CLIENTS`. | ||
### Synchronizing Using a Custom Network Message | ||
This way of synchronizing is probably the most complicated but is usually the most optimized, | ||
as you can make sure that only the data you need to be synchronized is actually synchronized. | ||
You should first check out the [`Networking`][networking] section and especially [`SimpleImpl`][simple_impl] before attempting this. | ||
Once you've created your custom network message, you can send it to all users that have the `BlockEntity` loaded with `SimpleChannel#send(PacketDistributor$PacketTarget, MSG)`. | ||
|
||
:::caution | ||
It is important that you do safety checks, the `BlockEntity` might already be destroyed/replaced when the message arrives at the player! You should also check if the chunk is loaded (`Level#hasChunkAt(BlockPos)`). | ||
::: | ||
|
||
[registration]: ../concepts/registries.md#methods-for-registering | ||
[storing-data]: #storing-data-within-your-blockentity | ||
[menu]: ../gui/menus.md | ||
[networking]: ../networking/index.md | ||
[simple_impl]: ../networking/simpleimpl.md |
Oops, something went wrong.
1a734ab
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Deploying with Cloudflare Pages