Basic networking
build / build (17) (push) Waiting to run Details
build / build (21) (push) Waiting to run Details

This commit is contained in:
corkscrew 2024-04-13 17:41:15 +03:00
parent f7c54d756a
commit 29b82b7b36
4 changed files with 107 additions and 19 deletions

View File

@ -2,9 +2,13 @@ package com.youpe.test;
import net.fabricmc.api.ModInitializer; import net.fabricmc.api.ModInitializer;
import net.fabricmc.fabric.api.client.rendering.v1.HudRenderCallback; 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.item.v1.FabricItemSettings;
import net.fabricmc.fabric.api.itemgroup.v1.ItemGroupEvents; 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.fabricmc.fabric.api.registry.FuelRegistry;
import net.minecraft.block.Blocks;
import net.minecraft.client.render.BufferBuilder; import net.minecraft.client.render.BufferBuilder;
import net.minecraft.client.render.GameRenderer; import net.minecraft.client.render.GameRenderer;
import net.minecraft.client.render.Tessellator; 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.client.render.VertexFormats;
import net.minecraft.item.Item; import net.minecraft.item.Item;
import net.minecraft.item.ItemGroups; import net.minecraft.item.ItemGroups;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.registry.Registries; import net.minecraft.registry.Registries;
import net.minecraft.registry.Registry; 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.Identifier;
import net.minecraft.util.Rarity; import net.minecraft.util.Rarity;
import org.joml.Matrix4f;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; 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 { public class Testing implements ModInitializer {
// This logger is used to write text to the console and the log file. // 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 Logger LOGGER = LoggerFactory.getLogger("testing");
public static final Item CUSTOM_ITEM = new CustomItem(new FabricItemSettings().rarity(Rarity.COMMON)); 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 @Override
public void onInitialize() { public void onInitialize() {
// This code runs as soon as Minecraft is in a mod-load-ready state. // 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 -> { ItemGroupEvents.modifyEntriesEvent(ItemGroups.BUILDING_BLOCKS).register(content -> {
content.add(CUSTOM_ITEM); content.add(CUSTOM_ITEM);
}); });
HudRenderCallback.EVENT.register((drawContext, tickDelta) -> { PlayerBlockBreakEvents.AFTER.register((world, player, pos, state, entity) -> {
Matrix4f positionMatrix = drawContext.getMatrices().peek().getPositionMatrix(); if (state.getBlock() == Blocks.GRASS_BLOCK || state.getBlock() == Blocks.DIRT) {
Tessellator tessellator = Tessellator.getInstance(); StateSaverAndLoader serverState = StateSaverAndLoader.getServerState(world.getServer());
BufferBuilder buffer = tessellator.getBuffer(); // Increment the amount of dirt blocks that have been broken
serverState.totalDirtBlocksBroken += 1;
buffer.begin(VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_COLOR_TEXTURE); PlayerData playerState = StateSaverAndLoader.getPlayerState(player);
buffer.vertex(positionMatrix, 20, 20, 0).color(1f, 1f, 1f, 1f).texture(0f, 0f).next(); playerState.dirtBlocksBroken += 1;
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); // Send a packet to the client
RenderSystem.setShaderTexture(0, new Identifier("testing", "icon.png")); MinecraftServer server = world.getServer();
RenderSystem.setShaderColor(1f, 1f, 1f, 1f);
tessellator.draw(); 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);
});
}
}); });
} }
} }

View File

@ -3,6 +3,8 @@ package com.youpe.test;
import com.youpe.test.event.KeyInputHandler; import com.youpe.test.event.KeyInputHandler;
import net.fabricmc.api.ClientModInitializer; import net.fabricmc.api.ClientModInitializer;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.minecraft.text.Text;
public class TestingClient implements ClientModInitializer{ public class TestingClient implements ClientModInitializer{
@ -10,6 +12,16 @@ public class TestingClient implements ClientModInitializer{
public void onInitializeClient() { public void onInitializeClient() {
KeyInputHandler.register(); KeyInputHandler.register();
KeyInputHandler.registerKeyInputs(); 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));
});
});
} }
} }

View File

@ -0,0 +1,5 @@
package com.youpe.test.server;
public class PlayerData {
public int dirtBlocksBroken = 0;
}

View File

@ -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<UUID, PlayerData> 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;
}
}