Skip to content

Commit

Permalink
fix(docs): Address the rest of xfact's comments
Browse files Browse the repository at this point in the history
  • Loading branch information
ChampionAsh5357 committed Nov 12, 2024
1 parent 3658e6e commit 1cd2cd9
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 44 deletions.
1 change: 0 additions & 1 deletion docs/advanced/extensibleenums.md
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@ 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)
- Warning: networked enums will require additional steps once network checks for enums are implemented in NeoForge
- 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
Expand Down
3 changes: 2 additions & 1 deletion docs/blockentities/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public static final Supplier<BlockEntityType<MyBlockEntity>> MY_BLOCK_ENTITY = B
```

:::note
Remember that the `DeferredRegister` must be [registered][registration] to the mod event bus!
Remember that the `DeferredRegister` must be registered to the [mod event bus][modbus]!
:::

Now that we have our block entity type, we can use it in place of the `type` variable we left earlier:
Expand Down Expand Up @@ -237,6 +237,7 @@ It is important that you do safety checks, as the `BlockEntity` might already be
[blockreg]: ../blocks/index.md#basic-blocks
[blockstate]: ../blocks/states.md
[dataattachments]: ../datastorage/attachments.md
[modbus]: ../concepts/events.md#event-buses
[nbt]: ../datastorage/nbt.md
[networking]: ../networking/index.md
[registration]: ../concepts/registries.md#methods-for-registering
Expand Down
12 changes: 10 additions & 2 deletions docs/blocks/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -169,15 +169,23 @@ We already discussed how to create a `DeferredRegister.Blocks` [above], as well
```java
public static final DeferredRegister.Blocks BLOCKS = DeferredRegister.createBlocks("yourmodid");

public static final DeferredBlock<Block> EXAMPLE_BLOCK = BLOCKS.register(
"example_block", registryName -> new Block(
BlockBehaviour.Properties.of()
// The ID must be set on the block
.setId(ResourceKey.create(Registries.BLOCK, registryName))
)
);

// Same as above, except that the block properties are constructed eagerly.
// setId is also called internally on the properties object.
public static final DeferredBlock<Block> EXAMPLE_BLOCK = BLOCKS.registerBlock(
"example_block",
Block::new, // The factory that the properties will be passed into.
BlockBehaviour.Properties.of() // The properties to use.
);
```

Internally, this will simply call `BLOCKS.register("example_block", registryName -> new Block(BlockBehaviour.Properties.of().setId(ResourceKey.create(Registries.BLOCK, registryName))))` by applying the properties parameter to the provided block factory (which is commonly the constructor). The id is set on the properties.

If you want to use `Block::new`, you can leave out the factory entirely:

```java
Expand Down
10 changes: 4 additions & 6 deletions docs/resources/server/enchantments/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,15 +134,13 @@ Enchantment effect component types must be [registered] to `BuiltInRegistries.EN

```java
// In some registration class
public static final DeferredRegister<DataComponentType<?>> ENCHANTMENT_COMPONENT_TYPES =
DeferredRegister.create(BuiltInRegistries.ENCHANTMENT_EFFECT_COMPONENT_TYPE, "examplemod");
public static final DeferredRegister.DataComponents ENCHANTMENT_COMPONENT_TYPES =
DeferredRegister.createDataComponents(BuiltInRegistries.ENCHANTMENT_EFFECT_COMPONENT_TYPE, "examplemod");

public static final Supplier<DataComponentType<Increment>> INCREMENT =
ENCHANTMENT_COMPONENT_TYPES.register(
ENCHANTMENT_COMPONENT_TYPES.registerComponentType(
"increment",
() -> DataComponentType.builder()
.persistent(Increment.CODEC)
.build()
builder -> builder.persistent(Increment.CODEC)
);
```

Expand Down
121 changes: 89 additions & 32 deletions docs/resources/server/recipes/custom.md
Original file line number Diff line number Diff line change
Expand Up @@ -470,73 +470,130 @@ public record ClientboundRightClickBlockRecipesPayload(
// ...
}
// Packet stores data in an instance class.
// Present on both server and client to do initial matching.
// Resource listener so it can be reloaded when recipes are.
public class RightClickBlockRecipeInputs extends SimplePreparableReloadListener<Void> {
// Only one instance
public static final RightClickBlockRecipeInputs INSTANCE = new RightClickBlockRecipeInputs();
public interface RightClickBlockRecipeInputs {
Set<BlockState> inputStates();
Set<Holder<Item>> inputItems();
default boolean test(BlockState state, ItemStack stack) {
return this.inputStates().contains(state) && this.inputItems().contains(stack.getItemHolder());
}
}
// Server resource listener so it can be reloaded when recipes are.
public class ServerRightClickBlockRecipeInputs implements ResourceManagerReloadListener, RightClickBlockRecipeInputs {
private final RecipeManager recipeManager;
private Set<BlockState> inputStates;
private Set<Holder<Item>> inputItems;
private RightClickBlockRecipeInputs() {}
@Override
protected Void prepare(ResourceManager manager, ProfilerFiller filler) {}
public RightClickBlockRecipeInputs(RecipeManager recipeManager) {
this.recipeManager = recipeManager;
}
// Set inputs here as #apply is fired synchronously based on listener registration order.
// Recipes are always applied first.
@Override
protected abstract void apply(Void dummy, ResourceManager manager, ProfilerFiller filler) {
public void onResourceManagerReload(ResourceManager manager) {
MinecraftServer server = ServerLifecycleHooks.getCurrentServer();
if (server != null) { // Should never be null
// Populate inputs
Set<BlockState> inputStates = new HashSet<>();
Set<Holder<Item>> inputItems = new HashSet<>();
server.getRecipeManager().recipeMap().byType(RIGHT_CLICK_BLOCK_TYPE.get())
this.recipeManager.recipeMap().byType(RIGHT_CLICK_BLOCK_TYPE.get())
.forEach(holder -> {
var recipe = holder.value();
inputStates.add(recipe.getInputState());
inputItems.addAll(recipe.getInputItem().items());
});
this.inputStates = Set.copyOf(inputStates);
this.inputItems = Set.copyOf(inputItems);
this.inputStates = Set.unmodifiableSet(inputStates);
this.inputItems = Set.unmodifiableSet(inputItems);
}
}
// Should be called within the handler for the payload
public void setInputs(Set<BlockState> inputStates, Set<Holder<Item>> inputItems) {
this.inputStates = inputStates;
this.inputItems = inputItems;
}
public void syncToClient(Stream<ServerPlayer> players) {
ClientboundRightClickBlockRecipesPayload payload =
new ClientboundRightClickBlockRecipesPayload(this.inputStates, this.inputItems);
players.forEach(player -> PacketDistributor.sendToPlayer(player, payload));
}
public boolean test(BlockState state, ItemStack stack) {
return this.inputStates.contains(state) && this.inputItems.contains(stack.getItemHolder());
@Override
public Set<BlockState> inputStates() {
return this.inputStates;
}
@Override
public Set<Holder<Item>> inputItems() {
return this.inputItems;
}
}
// On the game event bus
@Override
public static void addListener(AddReloadListenerEvent event) {
// Register server reload listener
event.addListener(RightClickBlockRecipeInputs.INSTANCE);
// Client implementation to hold the inputs.
public record ClientRightClickBlockRecipeInputs(
Set<BlockState> inputStates, Set<Holder<Item>> inputItems
) implements RightClickBlockRecipeInputs {
public ClientRightClickBlockRecipeInputs(Set<BlockState> inputStates, Set<Holder<Item>> inputItems) {
this.inputStates = Set.unmodifiableSet(inputStates);
this.inputItems = Set.unmodifiableSet(inputItems);
}
}
// On the game event bus
@SubscribeEvent
public static void datapackSync(OnDatapackSyncEvent event) {
// Send to client
RightClickBlockRecipeInputs.INSTANCE.syncToClient(event.getRelevantPlayers());
// Handling the recipe instance depending on side.
public class ServerRightClickBlockRecipes {
private static ServerRightClickBlockRecipeInputs inputs;
public static RightClickBlockRecipeInputs inputs() {
return ServerRightClickBlockRecipes.inputs;
}
// On the game event bus
@SubscribeEvent
public static void addListener(AddReloadListenerEvent event) {
// Register server reload listener
ServerRightClickBlockRecipes.inputs = new ServerRightClickBlockRecipeInputs(
event.getServerResources().getRecipeManager()
);
event.addListener(ServerRightClickBlockRecipes.inputs);
}
// On the game event bus
@SubscribeEvent
public static void datapackSync(OnDatapackSyncEvent event) {
// Send to client
ServerRightClickBlockRecipes.inputs.syncToClient(event.getRelevantPlayers());
}
}
public class ClientRightClickBlockRecipes {
private static ClientRightClickBlockRecipeInputs inputs;
public static RightClickBlockRecipeInputs inputs() {
return ClientRightClickBlockRecipes.inputs;
}
// Handling the sent packet
public static void handle(final ClientboundRightClickBlockRecipesPayload data, final IPayloadContext context) {
// Do something with the data, on the main thread
ClientRightClickBlockRecipes.inputs = new ClientRightClickBlockRecipeInputs(
data.inputStates(), data.inputItems()
);
}
}
public class RightClickBlockRecipes {
// Make proxy method to access properly
public static RightClickBlockRecipeInputs inputs() {
return FMLEnvironment.dist == Dist.CLIENT
? ClientRightClickBlockRecipes.inputs()
: ServerRightClickBlockRecipes.inputs();
}
}
```
Expand All @@ -555,7 +612,7 @@ public static void useItemOnBlock(UseItemOnBlockEvent event) {
ItemStack itemStack = event.getItemStack();

// Check if the input can result in a recipe on both sides
if (!RightClickBlockRecipeInputs.INSTANCE.test(blockState, itemStack)) return;
if (!RightClickBlockRecipes.inputs().test(blockState, itemStack)) return;

// If so, make sure on server before checking recipe
if (!level.isClientSide() && level instanceof ServerLevel serverLevel) {
Expand Down
4 changes: 2 additions & 2 deletions docs/resources/server/recipes/index.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Recipes

Recipes are a datapack registry used as a way to transform a set of objects into other objects within a Minecraft world. Although Minecraft uses this system purely for item transformations, the system is built in a way that allows any kind of objects - blocks, entities, etc. - to be transformed. Almost all recipes use recipe data files; a "recipe" is assumed to be a data-driven recipe in this article unless explicitly stated otherwise.
Recipes are a way to transform a set of objects into other objects within a Minecraft world. Although Minecraft uses this system purely for item transformations, the system is built in a way that allows any kind of objects - blocks, entities, etc. - to be transformed. Almost all recipes use recipe data files; a "recipe" is assumed to be a data-driven recipe in this article unless explicitly stated otherwise.

Recipe data files are located at `data/<namespace>/recipe/<path>.json`. For example, the recipe `minecraft:diamond_block` is located at `data/minecraft/recipe/diamond_block.json`.

Expand Down Expand Up @@ -39,7 +39,7 @@ A full list of types provided by Minecraft can be found in the [Built-In Recipe

## Using Recipes

Recipes are loaded, stored and obtained via the `RecipeManager` class, which is in turn obtained via `ServerLevel#recipeAccess` or - if you don't have a `ServerLevel` available - `ServerLifecycleHooks.getCurrentServer()#getRecipeManager`. The server does not sync the recipes to the client in their entirety, instead it only sends the `RecipePropertySet`s for restricting inputs on menu slots and the `RecipeDisplayEntry`s for the recipe book. All recipe logic should always run on the server.
Recipes are loaded, stored and obtained via the `RecipeManager` class, which is in turn obtained via `ServerLevel#recipeAccess` or - if you don't have a `ServerLevel` available - `ServerLifecycleHooks.getCurrentServer()#getRecipeManager`. The server does not sync the recipes to the client in their entirety instead it only sends the `RecipePropertySet`s for restricting inputs on menu slots and `RecipeDisplay`s via `RecipeDisplayEntry`s for the recipe book (excluding all recipes where `Recipe#isSpecial` returns true). All recipe logic should always run on the server.

The easiest way to get a recipe is by its resource key:

Expand Down

1 comment on commit 1cd2cd9

@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: 1cd2cd9dd5c8bc651872bb8303d216578a63537d
Status: ✅ Deploy successful!
Preview URL: https://6646ac84.neoforged-docs-previews.pages.dev
PR Preview URL: https://pr-178.neoforged-docs-previews.pages.dev

Please sign in to comment.