From 29b82b7b36675005642c0b41c721843b77eabd7b Mon Sep 17 00:00:00 2001 From: corkscrew Date: Sat, 13 Apr 2024 17:41:15 +0300 Subject: [PATCH] Basic networking --- src/main/java/com/youpe/test/Testing.java | 52 ++++++++++------- .../java/com/youpe/test/TestingClient.java | 12 ++++ .../com/youpe/test/server/PlayerData.java | 5 ++ .../test/server/StateSaverAndLoader.java | 57 +++++++++++++++++++ 4 files changed, 107 insertions(+), 19 deletions(-) create mode 100644 src/main/java/com/youpe/test/server/PlayerData.java create mode 100644 src/main/java/com/youpe/test/server/StateSaverAndLoader.java diff --git a/src/main/java/com/youpe/test/Testing.java b/src/main/java/com/youpe/test/Testing.java index 15fdd87..d652a3d 100644 --- a/src/main/java/com/youpe/test/Testing.java +++ b/src/main/java/com/youpe/test/Testing.java @@ -2,9 +2,13 @@ package com.youpe.test; import net.fabricmc.api.ModInitializer; import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback; +import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents; import net.fabricmc.fabric.api.item.v1.FabricItemSettings; import net.fabricmc.fabric.api.itemgroup.v1.ItemGroupEvents; +import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; +import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; import net.fabricmc.fabric.api.registry.FuelRegistry; +import net.minecraft.block.Blocks; import net.minecraft.client.render.BufferBuilder; import net.minecraft.client.render.GameRenderer; import net.minecraft.client.render.Tessellator; @@ -12,16 +16,19 @@ import net.minecraft.client.render.VertexFormat; import net.minecraft.client.render.VertexFormats; import net.minecraft.item.Item; import net.minecraft.item.ItemGroups; +import net.minecraft.network.PacketByteBuf; import net.minecraft.registry.Registries; import net.minecraft.registry.Registry; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.util.Identifier; import net.minecraft.util.Rarity; -import org.joml.Matrix4f; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import com.mojang.blaze3d.systems.RenderSystem; +import com.youpe.test.server.PlayerData; +import com.youpe.test.server.StateSaverAndLoader; public class Testing implements ModInitializer { // This logger is used to write text to the console and the log file. @@ -30,6 +37,8 @@ public class Testing implements ModInitializer { public static final Logger LOGGER = LoggerFactory.getLogger("testing"); public static final Item CUSTOM_ITEM = new CustomItem(new FabricItemSettings().rarity(Rarity.COMMON)); + public static final Identifier DIRT_BROKEN = new Identifier("testing", "dirt_broken"); + @Override public void onInitialize() { // This code runs as soon as Minecraft is in a mod-load-ready state. @@ -42,22 +51,27 @@ public class Testing implements ModInitializer { ItemGroupEvents.modifyEntriesEvent(ItemGroups.BUILDING_BLOCKS).register(content -> { content.add(CUSTOM_ITEM); }); - HudRenderCallback.EVENT.register((drawContext, tickDelta) -> { - Matrix4f positionMatrix = drawContext.getMatrices().peek().getPositionMatrix(); - Tessellator tessellator = Tessellator.getInstance(); - BufferBuilder buffer = tessellator.getBuffer(); - - buffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_COLOR_TEXTURE); - buffer.vertex(positionMatrix, 20, 20, 0).color(1f, 1f, 1f, 1f).texture(0f, 0f).next(); - buffer.vertex(positionMatrix, 20, 60, 0).color(1f, 0f, 0f, 1f).texture(0f, 1f).next(); - buffer.vertex(positionMatrix, 60, 60, 0).color(0f, 1f, 0f, 1f).texture(1f, 1f).next(); - buffer.vertex(positionMatrix, 60, 20, 0).color(0f, 0f, 1f, 1f).texture(1f, 0f).next(); - - RenderSystem.setShader(GameRenderer::getPositionColorTexProgram); - RenderSystem.setShaderTexture(0, new Identifier("testing", "icon.png")); - RenderSystem.setShaderColor(1f, 1f, 1f, 1f); - - tessellator.draw(); - }); + PlayerBlockBreakEvents.AFTER.register((world, player, pos, state, entity) -> { + if (state.getBlock() == Blocks.GRASS_BLOCK || state.getBlock() == Blocks.DIRT) { + StateSaverAndLoader serverState = StateSaverAndLoader.getServerState(world.getServer()); + // Increment the amount of dirt blocks that have been broken + serverState.totalDirtBlocksBroken += 1; + + PlayerData playerState = StateSaverAndLoader.getPlayerState(player); + playerState.dirtBlocksBroken += 1; + + // Send a packet to the client + MinecraftServer server = world.getServer(); + + PacketByteBuf data = PacketByteBufs.create(); + data.writeInt(serverState.totalDirtBlocksBroken); + data.writeInt(playerState.dirtBlocksBroken); + + ServerPlayerEntity playerEntity = server.getPlayerManager().getPlayer(player.getUuid()); + server.execute(() -> { + ServerPlayNetworking.send(playerEntity, DIRT_BROKEN, data); + }); + } + }); } } \ No newline at end of file diff --git a/src/main/java/com/youpe/test/TestingClient.java b/src/main/java/com/youpe/test/TestingClient.java index 1dffd69..66ce486 100644 --- a/src/main/java/com/youpe/test/TestingClient.java +++ b/src/main/java/com/youpe/test/TestingClient.java @@ -3,6 +3,8 @@ package com.youpe.test; import com.youpe.test.event.KeyInputHandler; import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; +import net.minecraft.text.Text; public class TestingClient implements ClientModInitializer{ @@ -10,6 +12,16 @@ public class TestingClient implements ClientModInitializer{ public void onInitializeClient() { KeyInputHandler.register(); KeyInputHandler.registerKeyInputs(); + + ClientPlayNetworking.registerGlobalReceiver(Testing.DIRT_BROKEN, (client, handler, buf, responseSender) -> { + int totalDirtBlocksBroken = buf.readInt(); + int playerSpecificDirtBlocksBroken = buf.readInt(); + + client.execute(() -> { + client.player.sendMessage(Text.literal("Total dirt blocks broken: " + totalDirtBlocksBroken)); + client.player.sendMessage(Text.literal("Player specific dirt blocks broken: " + playerSpecificDirtBlocksBroken)); + }); + }); } } diff --git a/src/main/java/com/youpe/test/server/PlayerData.java b/src/main/java/com/youpe/test/server/PlayerData.java new file mode 100644 index 0000000..fd05989 --- /dev/null +++ b/src/main/java/com/youpe/test/server/PlayerData.java @@ -0,0 +1,5 @@ +package com.youpe.test.server; + +public class PlayerData { + public int dirtBlocksBroken = 0; +} diff --git a/src/main/java/com/youpe/test/server/StateSaverAndLoader.java b/src/main/java/com/youpe/test/server/StateSaverAndLoader.java new file mode 100644 index 0000000..8c9e67b --- /dev/null +++ b/src/main/java/com/youpe/test/server/StateSaverAndLoader.java @@ -0,0 +1,57 @@ +package com.youpe.test.server; + +import java.util.HashMap; +import java.util.UUID; + +import net.minecraft.entity.LivingEntity; +import net.minecraft.nbt.NbtCompound; +import net.minecraft.server.MinecraftServer; +import net.minecraft.world.PersistentState; +import net.minecraft.world.PersistentStateManager; +import net.minecraft.world.World; + +public class StateSaverAndLoader extends PersistentState { + + public Integer totalDirtBlocksBroken = 0; + public HashMap players = new HashMap<>(); + + @Override + public NbtCompound writeNbt(NbtCompound nbt) { + nbt.putInt("totalDirtBlocksBroken", totalDirtBlocksBroken); + return nbt; + } + + public static StateSaverAndLoader createFromNbt(NbtCompound tag) { + StateSaverAndLoader state = new StateSaverAndLoader(); + state.totalDirtBlocksBroken = tag.getInt("totalDirtBlocksBroken"); + return state; + } + + public static StateSaverAndLoader getServerState(MinecraftServer server) { + // (Note: arbitrary choice to use 'World.OVERWORLD' instead of 'World.END' or 'World.NETHER'. Any work) + PersistentStateManager persistentStateManager = server.getWorld(World.OVERWORLD).getPersistentStateManager(); + + // The first time the following 'getOrCreate' function is called, it creates a brand new 'StateSaverAndLoader' and + // stores it inside the 'PersistentStateManager'. The subsequent calls to 'getOrCreate' pass in the saved + // 'StateSaverAndLoader' NBT on disk to our function 'StateSaverAndLoader::createFromNbt'. + StateSaverAndLoader state = persistentStateManager.getOrCreate(StateSaverAndLoader::createFromNbt, StateSaverAndLoader::new, "testing"); + + // If state is not marked dirty, when Minecraft closes, 'writeNbt' won't be called and therefore nothing will be saved. + // Technically it's 'cleaner' if you only mark state as dirty when there was actually a change, but the vast majority + // of mod writers are just going to be confused when their data isn't being saved, and so it's best just to 'markDirty' for them. + // Besides, it's literally just setting a bool to true, and the only time there's a 'cost' is when the file is written to disk when + // there were no actual change to any of the mods state (INCREDIBLY RARE). + state.markDirty(); + + return state; + } + + public static PlayerData getPlayerState(LivingEntity player) { + StateSaverAndLoader serverState = getServerState(player.getWorld().getServer()); + + // Either get the player by the uuid, or we don't have data for him yet, make a new player state + PlayerData playerState = serverState.players.computeIfAbsent(player.getUuid(), uuid -> new PlayerData()); + + return playerState; + } +} \ No newline at end of file