diff --git a/.vitepress/sidebars/develop.ts b/.vitepress/sidebars/develop.ts index 96d1f0870..a43128da1 100644 --- a/.vitepress/sidebars/develop.ts +++ b/.vitepress/sidebars/develop.ts @@ -254,6 +254,10 @@ export default [ text: "develop.misc.events", link: "/develop/events", }, + { + text: "develop.misc.networking", + link: "/develop/networking", + }, { text: "develop.misc.text-and-translations", link: "/develop/text-and-translations", diff --git a/develop/data-generation/advancements.md b/develop/data-generation/advancements.md index f8fa6cab2..c78714c11 100644 --- a/develop/data-generation/advancements.md +++ b/develop/data-generation/advancements.md @@ -22,7 +22,7 @@ First, we need to make our provider. Create a class that `extends FabricAdvancem To finish setup, add this provider to your `DataGeneratorEntrypoint` within the `onInitializeDataGenerator` method. -@[code lang=java transclude={25-25}](@/reference/latest/src/client/java/com/example/docs/datagen/FabricDocsReferenceDataGenerator.java) +@[code lang=java transclude={26-26}](@/reference/latest/src/client/java/com/example/docs/datagen/FabricDocsReferenceDataGenerator.java) ## Advancement Structure {#advancement-structure} diff --git a/develop/data-generation/loot-tables.md b/develop/data-generation/loot-tables.md index 7cc350fad..4a9195430 100644 --- a/develop/data-generation/loot-tables.md +++ b/develop/data-generation/loot-tables.md @@ -18,7 +18,7 @@ Make sure you've completed the [datagen setup](./setup) process first. You will need different providers (classes) for blocks, chests, and entities. Remember to add them all to your pack in your `DataGeneratorEntrypoint` within the `onInitializeDataGenerator` method. -@[code lang=java transclude={32-33}](@/reference/latest/src/client/java/com/example/docs/datagen/FabricDocsReferenceDataGenerator.java) +@[code lang=java transclude={34-35}](@/reference/latest/src/client/java/com/example/docs/datagen/FabricDocsReferenceDataGenerator.java) ## Loot Tables Explained {#loot-tables-explained} diff --git a/develop/data-generation/recipes.md b/develop/data-generation/recipes.md index 8f9020146..175edc7c9 100644 --- a/develop/data-generation/recipes.md +++ b/develop/data-generation/recipes.md @@ -21,7 +21,7 @@ First, we'll need our provider. Make a class that `extends FabricRecipeProvider` To finish setup, add this provider to your `DataGeneratorEntrypoint` within the `onInitializeDataGenerator` method. -@[code lang=java transclude={31-31}](@/reference/latest/src/client/java/com/example/docs/datagen/FabricDocsReferenceDataGenerator.java) +@[code lang=java transclude={32-32}](@/reference/latest/src/client/java/com/example/docs/datagen/FabricDocsReferenceDataGenerator.java) ## Shapeless Recipes {#shapeless-recipes} diff --git a/develop/data-generation/tags.md b/develop/data-generation/tags.md index f6bd77dc2..03c7142ef 100644 --- a/develop/data-generation/tags.md +++ b/develop/data-generation/tags.md @@ -25,7 +25,7 @@ You will need a different provider for each type of tag (eg. one `FabricTagProvi To finish setup, add this provider to your `DataGeneratorEntrypoint` within the `onInitializeDataGenerator` method. -@[code lang=java transclude={29-29}](@/reference/latest/src/client/java/com/example/docs/datagen/FabricDocsReferenceDataGenerator.java) +@[code lang=java transclude={30-30}](@/reference/latest/src/client/java/com/example/docs/datagen/FabricDocsReferenceDataGenerator.java) ## Creating a Tag {#creating-a-tag} diff --git a/develop/data-generation/translations.md b/develop/data-generation/translations.md index 99266881e..f5874abeb 100644 --- a/develop/data-generation/translations.md +++ b/develop/data-generation/translations.md @@ -28,7 +28,7 @@ You will need a different provider for each language you want to generate (eg. o To finish setup, add this provider to your `DataGeneratorEntrypoint` within the `onInitializeDataGenerator` method. -@[code lang=java transclude={27-27}](@/reference/latest/src/client/java/com/example/docs/datagen/FabricDocsReferenceDataGenerator.java) +@[code lang=java transclude={28-28}](@/reference/latest/src/client/java/com/example/docs/datagen/FabricDocsReferenceDataGenerator.java) ## Creating Translations {#creating-translations} diff --git a/develop/networking.md b/develop/networking.md new file mode 100644 index 000000000..ef1697336 --- /dev/null +++ b/develop/networking.md @@ -0,0 +1,252 @@ +--- +title: Networking +description: A general guide on networking using Fabric API. +authors: + - Daomephsta + - dicedpixels + - Earthcomputer + - FlooferLand + - FxMorin + - i509VCB + - modmuss50 + - natanfudge + - NetUserGet + - NShak + - parzivail + - skycatminepokie + - SolidBlock-cn + - Voleil + - Wxffel + - YTG123-Mods + - zulrang +--- + +Networking in Minecraft is used so the client and server can communicate with each other. Networking is a broad topic, +so this page is split up into a few categories. + +## Why Is Networking Important? {#why-is-networking-important} + +The importance of networking can be shown by a simple code example. + +::: warning +Below code is for demonstration purposes only. +::: + +Say you had a Wand which highlights the block you're looking, which will be visible to all +nearby players: + +```java +public class HighlightingWandItem extends Item { + public HighlightingWandItem(Item.Settings settings) { + super(settings); + } + + public TypedActionResult<ItemStack> use(World world, PlayerEntity user, Hand hand) { + BlockPos target = ... + + // BAD CODE: DON'T EVER DO THIS! // [!code error] + ClientBlockHighlighting.highlightBlock(MinecraftClient.getInstance(), target); // [!code error] + return super.use(world, user, hand); + } +} +``` + +Upon testing, you will see a lightning bolt being summoned and nothing crashes. Now you want to show the mod to your +friend, you boot up a dedicated server and invite your friend on with the mod installed. + +You use the item and the server crashes. You will probably notice in the crash log an error similar to this: + +```log +[Server thread/FATAL]: Error executing task on Server +java.lang.RuntimeException: Cannot load class net.minecraft.client.MinecraftClient in environment type SERVER +``` + +### Why Does the Server Crash? {#why-does-the-server-crash} + +The code calls logic only present on the client distribution of the Minecraft. The reason for Mojang distributing the +game in this way is to cut down on the size of the Minecraft Server JAR file. There isn't really a reason to include an +entire rendering engine when your own machine will render the world. + +In a development environment, client-only classes are indicated by the `@Environment(EnvType.CLIENT)` annotation. + +### How Do We Fix the Crash? {#how-do-we-fix-the-crash} + +To fix this issue, you need to understand how the game client and dedicated server communicate: + + + +The diagram above shows that the game client and dedicated server are separate systems, bridged together using +_packets_. Packets can contain data which we refer to as the _payload_. + +This packet bridge does not only exist between a game client and dedicated server, but also between your client and +another client connected over LAN. The packet bridge is also present even in singleplayer. This is because the game +client will spin up a special integrated server instance to run the game on. + +Connection to a server over LAN or singleplayer can also be treated like the server is a remote, dedicated server; so +your game client can't directly access the server instance. + +## An Introduction to Networking {#an-introduction-to-networking} + +### Defining a Payload {#defining-a-payload} + +This can be done by creating a Java `Record` with a `BlockPos` parameter that implements `CustomPayload`. + +@[code lang=java transcludeWith=:::summon_Lightning_payload](@/reference/latest/src/main/java/com/example/docs/networking/basic/SummonLightningS2CPayload.java) + +At the same time, we've defined: + +- An `Identifier` used to identify our packet's payload. For this example our identifier will be + `fabric-docs-reference:summon_lightning`. + +@[code lang=java transclude={13-13}](@/reference/latest/src/main/java/com/example/docs/networking/basic/SummonLightningS2CPayload.java) + +- A public static instance of `CustomPayload.Id` to uniquely identify this custom payload. We will be referencing this + ID in both our common and client code. + +@[code lang=java transclude={14-14}](@/reference/latest/src/main/java/com/example/docs/networking/basic/SummonLightningS2CPayload.java) + +- A public static instance of a `PacketCodec` so that the game knows how to serialize/deserialize the contents of the + packet. + +@[code lang=java transclude={15-15}](@/reference/latest/src/main/java/com/example/docs/networking/basic/SummonLightningS2CPayload.java) + +We have also overridden `getId` to return our payload ID. + +@[code lang=java transclude={17-20}](@/reference/latest/src/main/java/com/example/docs/networking/basic/SummonLightningS2CPayload.java) + +### Registering a Payload {#registering-a-payload} + +Before we send a packet with our custom payload, we need to register it. + +::: info +`S2C` and `C2S` are two common suffixes that mean _Server-to-Client_ and _Client-to-Server_ respectively. +::: + +This can be done in our **common initializer** by using `PayloadTypeRegistry.playS2C().register` which takes in a +`CustomPayload.Id` and a `PacketCodec`. + +@[code lang=java transclude={25-25}](@/reference/latest/src/main/java/com/example/docs/networking/basic/FabricDocsReferenceNetworkingBasic.java) + +A similar method exists to register client-to-server payloads: `PayloadTypeRegistry.playC2S().register`. + +### Sending a Packet to the Client {#sending-a-packet-to-the-client} + +To send a packet with our custom payload, we can use `ServerPlayNetworking.send` which takes in a `ServerPlayerEntity` +and a `CustomPayload`. + +Let's start by creating our Lightning Tater item. You can override `use` to trigger an action when the item is used. +In this case, let's send packets to the players in the server world. + +@[code lang=java transcludeWith=:::lightning_tater_item](@/reference/latest/src/main/java/com/example/docs/networking/basic/LightningTaterItem.java) + +Let's examine the code above. + +We only send packets when the action is initiated on the server, by returning early with a `isClient` check: + +@[code lang=java transclude={22-24}](@/reference/latest/src/main/java/com/example/docs/networking/basic/LightningTaterItem.java) + +We create an instance of the payload with the user's position: + +@[code lang=java transclude={26-26}](@/reference/latest/src/main/java/com/example/docs/networking/basic/LightningTaterItem.java) + +Finally, we get the players in the server world through `PlayerLookup` and send a packet to each player. + +@[code lang=java transclude={28-30}](@/reference/latest/src/main/java/com/example/docs/networking/basic/LightningTaterItem.java) + +::: info +Fabric API provides `PlayerLookup`, a collection of helper functions that will look up players in a server. + +A term frequently used to describe the functionality of these methods is "_tracking_". It means that an entity or a +chunk +on the server is known to a player's client (within their view distance) and the entity or block entity should notify +tracking clients of changes. + +Tracking is an important concept for efficient networking, so that only the necessary players are notified of changes by +sending packets. +::: + +### Receiving a Packet on the Client {#receiving-a-packet-on-the-client} + +To receive a packet sent from a server on the client, you need to specify how you will handle the incoming packet. + +This can be done in the **client initializer**, by calling `ClientPlayNetworking.registerGlobalReceiver` and passing a +`CustomPayload.Id` and a `PlayPayloadHandler`, which is a Functional Interface. + +In this case, we'll define the action to trigger within the implementation of `PlayPayloadHandler` implementation (as a +lambda expression). + +@[code lang=java transcludeWith=:::client_global_receiver](@/reference/latest/src/client/java/com/example/docs/network/basic/FabricDocsReferenceNetworkingBasicClient.java) + +Let's examine the code above. + +We can access the data from our payload by calling the Record's getter methods. In this case `payload.pos()`. Which then +can be used to get the `x`, `y` and `z` positions. + +@[code lang=java transclude={32-32}](@/reference/latest/src/client/java/com/example/docs/network/basic/FabricDocsReferenceNetworkingBasicClient.java) + +Finally, we create a `LightningEntity` and add it to the world. + +@[code lang=java transclude={33-38}](@/reference/latest/src/client/java/com/example/docs/network/basic/FabricDocsReferenceNetworkingBasicClient.java) + +Now, if you add this mod to a server and when a player uses our Lightning Tater item, every player will see lightning +striking at the user's position. + +<VideoPlayer src="/assets/develop/networking/summon-lightning.webm" title="Summon lightning using Lightning Tater" /> + +### Sending a Packet to the Server {#sending-a-packet-to-the-server} + +Just like sending a packet to the client, we start by creating a custom payload. This time, when a player uses a +Poisonous Potato on a living entity, we request the server to apply the Glowing effect to it. + +@[code lang=java transcludeWith=:::give_glowing_effect_payload](@/reference/latest/src/main/java/com/example/docs/networking/basic/GiveGlowingEffectC2SPayload.java) + +We pass in the appropriate codec along with a method reference to get the value from the Record to build this codec. + +Then we register our payload in our **common initializer**. However, this time as _Client-to-Server_ payload by using +`PayloadTypeRegistry.playC2S().register`. + +@[code lang=java transclude={26-26}](@/reference/latest/src/main/java/com/example/docs/networking/basic/FabricDocsReferenceNetworkingBasic.java) + +To send a packet, let's add an action when the player uses a Poisonous Potato. We'll be using the `UseEntityCallback` +event to +keep things concise. + +We register the event in our **client initializer**, and we use `isClient()` to ensure that the action is only triggered +on the logical client. + +@[code lang=java transcludeWith=:::use_entity_callback](@/reference/latest/src/client/java/com/example/docs/network/basic/FabricDocsReferenceNetworkingBasicClient.java) + +We create an instance of our `GiveGlowingEffectC2SPayload` with the necessary arguments. In this case, the network ID +of +the targeted entity. + +@[code lang=java transclude={51-51}](@/reference/latest/src/client/java/com/example/docs/network/basic/FabricDocsReferenceNetworkingBasicClient.java) + +Finally, we send a packet to the server by calling `ClientPlayNetworking.send` with the instance of our +`GiveGlowingEffectC2SPayload`. + +@[code lang=java transclude={52-52}](@/reference/latest/src/client/java/com/example/docs/network/basic/FabricDocsReferenceNetworkingBasicClient.java) + +### Receiving a Packet on the Server {#receiving-a-packet-on-the-server} + +This can be done in the **common initializer**, by calling `ServerPlayNetworking.registerGlobalReceiver` and passing a +`CustomPayload.Id` and a `PlayPayloadHandler`. + +@[code lang=java transcludeWith=:::server_global_receiver](@/reference/latest/src/main/java/com/example/docs/networking/basic/FabricDocsReferenceNetworkingBasic.java) + +::: info +It is important that you validate the content of the packet on the server side. + +In this case, we validate if the entity exists based on its network ID. + +@[code lang=java transclude={30-30}](@/reference/latest/src/main/java/com/example/docs/networking/basic/FabricDocsReferenceNetworkingBasic.java) + +Additionally, the targeted entity has to be a living entity, and we restrict the range of the target entity from the +player to 5. + +@[code lang=java transclude={32-32}](@/reference/latest/src/main/java/com/example/docs/networking/basic/FabricDocsReferenceNetworkingBasic.java) +::: + +Now when any player tries to use a Poisonous Potato on a living entity, the glowing effect will be applied to it. + +<VideoPlayer src="/assets/develop/networking/use-poisonous-potato.webm" title="Glowing effect is applied when a Poisonous Potato is used on a living entity." /> diff --git a/public/assets/develop/networking/sides.png b/public/assets/develop/networking/sides.png new file mode 100644 index 000000000..118425043 Binary files /dev/null and b/public/assets/develop/networking/sides.png differ diff --git a/public/assets/develop/networking/summon-lightning.webm b/public/assets/develop/networking/summon-lightning.webm new file mode 100644 index 000000000..a3afc8ccc Binary files /dev/null and b/public/assets/develop/networking/summon-lightning.webm differ diff --git a/public/assets/develop/networking/use-poisonous-potato.webm b/public/assets/develop/networking/use-poisonous-potato.webm new file mode 100644 index 000000000..f5de84d33 Binary files /dev/null and b/public/assets/develop/networking/use-poisonous-potato.webm differ diff --git a/reference/latest/src/client/java/com/example/docs/datagen/FabricDocsReferenceDataGenerator.java b/reference/latest/src/client/java/com/example/docs/datagen/FabricDocsReferenceDataGenerator.java index a43fa1f3e..fee425909 100644 --- a/reference/latest/src/client/java/com/example/docs/datagen/FabricDocsReferenceDataGenerator.java +++ b/reference/latest/src/client/java/com/example/docs/datagen/FabricDocsReferenceDataGenerator.java @@ -10,6 +10,7 @@ import com.example.docs.damage.FabricDocsReferenceDamageTypes; import com.example.docs.datagen.internal.FabricDocsReferenceInternalModelProvider; +import com.example.docs.network.basic.FabricDocsReferenceNetworkingBasicModelProvider; // :::datagen-setup:generator public class FabricDocsReferenceDataGenerator implements DataGeneratorEntrypoint { @@ -41,6 +42,8 @@ public void onInitializeDataGenerator(FabricDataGenerator fabricDataGenerator) { pack.addProvider(FabricDocsReferenceModelProvider::new); // :::datagen-setup:generator + + pack.addProvider(FabricDocsReferenceNetworkingBasicModelProvider::new); } // :::datagen-setup:generator diff --git a/reference/latest/src/client/java/com/example/docs/network/basic/FabricDocsReferenceNetworkingBasicClient.java b/reference/latest/src/client/java/com/example/docs/network/basic/FabricDocsReferenceNetworkingBasicClient.java new file mode 100644 index 000000000..21e8d5887 --- /dev/null +++ b/reference/latest/src/client/java/com/example/docs/network/basic/FabricDocsReferenceNetworkingBasicClient.java @@ -0,0 +1,61 @@ +package com.example.docs.network.basic; + +import net.minecraft.client.world.ClientWorld; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.LightningEntity; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.SpawnReason; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import net.minecraft.util.math.BlockPos; + +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; +import net.fabricmc.fabric.api.event.player.UseEntityCallback; + +import com.example.docs.networking.basic.GiveGlowingEffectC2SPayload; +import com.example.docs.networking.basic.SummonLightningS2CPayload; + +public class FabricDocsReferenceNetworkingBasicClient implements ClientModInitializer { + @Override + public void onInitializeClient() { + // :::client_global_receiver + ClientPlayNetworking.registerGlobalReceiver(SummonLightningS2CPayload.ID, (payload, context) -> { + ClientWorld world = context.client().world; + + if (world == null) { + return; + } + + BlockPos lightningPos = payload.pos(); + LightningEntity entity = EntityType.LIGHTNING_BOLT.create(world, SpawnReason.TRIGGERED); + + if (entity != null) { + entity.setPosition(lightningPos.getX(), lightningPos.getY(), lightningPos.getZ()); + world.addEntity(entity); + } + }); + // :::client_global_receiver + + // :::use_entity_callback + UseEntityCallback.EVENT.register((player, world, hand, entity, hitResult) -> { + if (!world.isClient()) { + return ActionResult.PASS; + } + + ItemStack usedItemStack = player.getStackInHand(hand); + + if (entity instanceof LivingEntity && usedItemStack.isOf(Items.POISONOUS_POTATO) && hand == Hand.MAIN_HAND) { + GiveGlowingEffectC2SPayload payload = new GiveGlowingEffectC2SPayload(hitResult.getEntity().getId()); + ClientPlayNetworking.send(payload); + + return ActionResult.SUCCESS; + } + + return ActionResult.PASS; + }); + // :::use_entity_callback + } +} diff --git a/reference/latest/src/client/java/com/example/docs/network/basic/FabricDocsReferenceNetworkingBasicModelProvider.java b/reference/latest/src/client/java/com/example/docs/network/basic/FabricDocsReferenceNetworkingBasicModelProvider.java new file mode 100644 index 000000000..bf12229de --- /dev/null +++ b/reference/latest/src/client/java/com/example/docs/network/basic/FabricDocsReferenceNetworkingBasicModelProvider.java @@ -0,0 +1,25 @@ +package com.example.docs.network.basic; + +import net.minecraft.client.data.BlockStateModelGenerator; +import net.minecraft.client.data.ItemModelGenerator; +import net.minecraft.client.data.Models; + +import net.fabricmc.fabric.api.client.datagen.v1.provider.FabricModelProvider; +import net.fabricmc.fabric.api.datagen.v1.FabricDataOutput; + +import com.example.docs.networking.basic.FabricDocsReferenceNetworkingBasic; + +public class FabricDocsReferenceNetworkingBasicModelProvider extends FabricModelProvider { + public FabricDocsReferenceNetworkingBasicModelProvider(FabricDataOutput output) { + super(output); + } + + @Override + public void generateBlockStateModels(BlockStateModelGenerator blockStateModelGenerator) { + } + + @Override + public void generateItemModels(ItemModelGenerator itemModelGenerator) { + itemModelGenerator.register(FabricDocsReferenceNetworkingBasic.LIGHTNING_TATER, Models.HANDHELD); + } +} diff --git a/reference/latest/src/main/generated/assets/fabric-docs-reference/items/lightning_tater.json b/reference/latest/src/main/generated/assets/fabric-docs-reference/items/lightning_tater.json new file mode 100644 index 000000000..e2c46e49f --- /dev/null +++ b/reference/latest/src/main/generated/assets/fabric-docs-reference/items/lightning_tater.json @@ -0,0 +1,6 @@ +{ + "model": { + "type": "minecraft:model", + "model": "fabric-docs-reference:item/lightning_tater" + } +} \ No newline at end of file diff --git a/reference/latest/src/main/generated/assets/fabric-docs-reference/items/ruby_door.json b/reference/latest/src/main/generated/assets/fabric-docs-reference/items/ruby_door.json index 07d2240a9..a1c490bdf 100644 --- a/reference/latest/src/main/generated/assets/fabric-docs-reference/items/ruby_door.json +++ b/reference/latest/src/main/generated/assets/fabric-docs-reference/items/ruby_door.json @@ -1,6 +1,6 @@ { "model": { "type": "minecraft:model", - "model": "fabric-docs-reference:item/ruby_door" + "model": "fabric-docs-reference:block/ruby_door" } } \ No newline at end of file diff --git a/reference/latest/src/main/generated/assets/fabric-docs-reference/items/ruby_fence.json b/reference/latest/src/main/generated/assets/fabric-docs-reference/items/ruby_fence.json index aee82d9ad..5ed2fdb1c 100644 --- a/reference/latest/src/main/generated/assets/fabric-docs-reference/items/ruby_fence.json +++ b/reference/latest/src/main/generated/assets/fabric-docs-reference/items/ruby_fence.json @@ -1,6 +1,6 @@ { "model": { "type": "minecraft:model", - "model": "fabric-docs-reference:block/ruby_fence_inventory" + "model": "fabric-docs-reference:block/ruby_fence" } } \ No newline at end of file diff --git a/reference/latest/src/main/generated/assets/fabric-docs-reference/items/ruby_trapdoor.json b/reference/latest/src/main/generated/assets/fabric-docs-reference/items/ruby_trapdoor.json index 620b00ddb..d47921fb6 100644 --- a/reference/latest/src/main/generated/assets/fabric-docs-reference/items/ruby_trapdoor.json +++ b/reference/latest/src/main/generated/assets/fabric-docs-reference/items/ruby_trapdoor.json @@ -1,6 +1,6 @@ { "model": { "type": "minecraft:model", - "model": "fabric-docs-reference:block/ruby_trapdoor_bottom" + "model": "fabric-docs-reference:block/ruby_trapdoor" } } \ No newline at end of file diff --git a/reference/latest/src/main/generated/assets/fabric-docs-reference/models/item/lightning_tater.json b/reference/latest/src/main/generated/assets/fabric-docs-reference/models/item/lightning_tater.json new file mode 100644 index 000000000..6b99f07ae --- /dev/null +++ b/reference/latest/src/main/generated/assets/fabric-docs-reference/models/item/lightning_tater.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/handheld", + "textures": { + "layer0": "fabric-docs-reference:item/lightning_tater" + } +} \ No newline at end of file diff --git a/reference/latest/src/main/java/com/example/docs/networking/basic/FabricDocsReferenceNetworkingBasic.java b/reference/latest/src/main/java/com/example/docs/networking/basic/FabricDocsReferenceNetworkingBasic.java new file mode 100644 index 000000000..2436df44a --- /dev/null +++ b/reference/latest/src/main/java/com/example/docs/networking/basic/FabricDocsReferenceNetworkingBasic.java @@ -0,0 +1,38 @@ +package com.example.docs.networking.basic; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.effect.StatusEffectInstance; +import net.minecraft.entity.effect.StatusEffects; +import net.minecraft.item.Item; +import net.minecraft.registry.Registries; +import net.minecraft.registry.Registry; +import net.minecraft.registry.RegistryKey; +import net.minecraft.registry.RegistryKeys; +import net.minecraft.util.Identifier; + +import net.fabricmc.api.ModInitializer; +import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry; +import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; + +import com.example.docs.FabricDocsReference; + +public class FabricDocsReferenceNetworkingBasic implements ModInitializer { + public static final RegistryKey<Item> LIGHTNING_TATER_REGISTRY_KEY = RegistryKey.of(RegistryKeys.ITEM, Identifier.of(FabricDocsReference.MOD_ID, "lightning_tater")); + public static final Item LIGHTNING_TATER = Registry.register(Registries.ITEM, Identifier.of(FabricDocsReference.MOD_ID, "lightning_tater"), new LightningTaterItem(new Item.Settings().registryKey(LIGHTNING_TATER_REGISTRY_KEY))); + + public void onInitialize() { + PayloadTypeRegistry.playS2C().register(SummonLightningS2CPayload.ID, SummonLightningS2CPayload.CODEC); + PayloadTypeRegistry.playC2S().register(GiveGlowingEffectC2SPayload.ID, GiveGlowingEffectC2SPayload.CODEC); + + // :::server_global_receiver + ServerPlayNetworking.registerGlobalReceiver(GiveGlowingEffectC2SPayload.ID, (payload, context) -> { + Entity entity = context.player().getWorld().getEntityById(payload.entityId()); + + if (entity instanceof LivingEntity livingEntity && livingEntity.isInRange(context.player(), 5)) { + livingEntity.addStatusEffect(new StatusEffectInstance(StatusEffects.GLOWING, 100)); + } + }); + // :::server_global_receiver + } +} diff --git a/reference/latest/src/main/java/com/example/docs/networking/basic/GiveGlowingEffectC2SPayload.java b/reference/latest/src/main/java/com/example/docs/networking/basic/GiveGlowingEffectC2SPayload.java new file mode 100644 index 000000000..b081de958 --- /dev/null +++ b/reference/latest/src/main/java/com/example/docs/networking/basic/GiveGlowingEffectC2SPayload.java @@ -0,0 +1,22 @@ +package com.example.docs.networking.basic; + +import net.minecraft.network.RegistryByteBuf; +import net.minecraft.network.codec.PacketCodec; +import net.minecraft.network.codec.PacketCodecs; +import net.minecraft.network.packet.CustomPayload; +import net.minecraft.util.Identifier; + +import com.example.docs.FabricDocsReference; + +// :::give_glowing_effect_payload +public record GiveGlowingEffectC2SPayload(int entityId) implements CustomPayload { + public static final Identifier GIVE_GLOWING_EFFECT_PAYLOAD_ID = Identifier.of(FabricDocsReference.MOD_ID, "give_glowing_effect"); + public static final CustomPayload.Id<GiveGlowingEffectC2SPayload> ID = new CustomPayload.Id<>(GIVE_GLOWING_EFFECT_PAYLOAD_ID); + public static final PacketCodec<RegistryByteBuf, GiveGlowingEffectC2SPayload> CODEC = PacketCodec.tuple(PacketCodecs.INTEGER, GiveGlowingEffectC2SPayload::entityId, GiveGlowingEffectC2SPayload::new); + + @Override + public Id<? extends CustomPayload> getId() { + return ID; + } +} +// :::give_glowing_effect_payload diff --git a/reference/latest/src/main/java/com/example/docs/networking/basic/LightningTaterItem.java b/reference/latest/src/main/java/com/example/docs/networking/basic/LightningTaterItem.java new file mode 100644 index 000000000..50a278d14 --- /dev/null +++ b/reference/latest/src/main/java/com/example/docs/networking/basic/LightningTaterItem.java @@ -0,0 +1,35 @@ +package com.example.docs.networking.basic; + +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.Item; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.ActionResult; +import net.minecraft.util.Hand; +import net.minecraft.world.World; + +import net.fabricmc.fabric.api.networking.v1.PlayerLookup; +import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; + +// :::lightning_tater_item +public class LightningTaterItem extends Item { + public LightningTaterItem(Settings settings) { + super(settings); + } + + @Override + public ActionResult use(World world, PlayerEntity user, Hand hand) { + if (world.isClient()) { + return ActionResult.PASS; + } + + SummonLightningS2CPayload payload = new SummonLightningS2CPayload(user.getBlockPos()); + + for (ServerPlayerEntity player : PlayerLookup.world((ServerWorld) world)) { + ServerPlayNetworking.send(player, payload); + } + + return ActionResult.SUCCESS; + } +} +// :::lightning_tater_item diff --git a/reference/latest/src/main/java/com/example/docs/networking/basic/SummonLightningS2CPayload.java b/reference/latest/src/main/java/com/example/docs/networking/basic/SummonLightningS2CPayload.java new file mode 100644 index 000000000..044c7cc35 --- /dev/null +++ b/reference/latest/src/main/java/com/example/docs/networking/basic/SummonLightningS2CPayload.java @@ -0,0 +1,22 @@ +package com.example.docs.networking.basic; + +import net.minecraft.network.RegistryByteBuf; +import net.minecraft.network.codec.PacketCodec; +import net.minecraft.network.packet.CustomPayload; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; + +import com.example.docs.FabricDocsReference; + +// :::summon_Lightning_payload +public record SummonLightningS2CPayload(BlockPos pos) implements CustomPayload { + public static final Identifier SUMMON_LIGHTNING_PAYLOAD_ID = Identifier.of(FabricDocsReference.MOD_ID, "summon_lightning"); + public static final CustomPayload.Id<SummonLightningS2CPayload> ID = new CustomPayload.Id<>(SUMMON_LIGHTNING_PAYLOAD_ID); + public static final PacketCodec<RegistryByteBuf, SummonLightningS2CPayload> CODEC = PacketCodec.tuple(BlockPos.PACKET_CODEC, SummonLightningS2CPayload::pos, SummonLightningS2CPayload::new); + + @Override + public Id<? extends CustomPayload> getId() { + return ID; + } +} +// :::summon_Lightning_payload diff --git a/reference/latest/src/main/resources/assets/fabric-docs-reference/textures/item/lightning_tater.png b/reference/latest/src/main/resources/assets/fabric-docs-reference/textures/item/lightning_tater.png new file mode 100644 index 000000000..b32c8a9ca Binary files /dev/null and b/reference/latest/src/main/resources/assets/fabric-docs-reference/textures/item/lightning_tater.png differ diff --git a/reference/latest/src/main/resources/fabric.mod.json b/reference/latest/src/main/resources/fabric.mod.json index 3f3baab67..fde14a434 100644 --- a/reference/latest/src/main/resources/fabric.mod.json +++ b/reference/latest/src/main/resources/fabric.mod.json @@ -20,14 +20,16 @@ "com.example.docs.block.entity.FabricDocsReferenceBlockEntities", "com.example.docs.component.FabricDocsReferenceComponents", "com.example.docs.advancement.FabricDocsReferenceDatagenAdvancement", - "com.example.docs.networking.FabricDocsReferenceNetworking" + "com.example.docs.networking.FabricDocsReferenceNetworking", + "com.example.docs.networking.basic.FabricDocsReferenceNetworkingBasic" ], "client": [ "com.example.docs.client.command.FabricDocsReferenceClientCommands", "com.example.docs.FabricDocsBlockEntityRenderer", "com.example.docs.FabricDocsDynamicSound", "com.example.docs.FabricDocsReferenceClient", - "com.example.docs.rendering.HudRenderingEntrypoint" + "com.example.docs.rendering.HudRenderingEntrypoint", + "com.example.docs.network.basic.FabricDocsReferenceNetworkingBasicClient" ], "fabric-datagen": [ "com.example.docs.datagen.FabricDocsReferenceDataGenerator" diff --git a/sidebar_translations.json b/sidebar_translations.json index 9d1fa5acb..a4ca4e92c 100644 --- a/sidebar_translations.json +++ b/sidebar_translations.json @@ -62,6 +62,7 @@ "develop.misc": "Miscellaneous Pages", "develop.misc.codecs": "Codecs", "develop.misc.events": "Events", + "develop.misc.networking": "Networking", "develop.misc.text-and-translations": "Text and Translations", "develop.misc.ideTipsAndTricks": "IDE Tips and Tricks", "develop.misc.automatic-testing": "Automated Testing",