From 3f73d02d2d114843170ccca1b0424feb88c4986d Mon Sep 17 00:00:00 2001 From: "Josiah (Gaming32) Glosson" Date: Thu, 22 Dec 2022 08:48:37 -0600 Subject: [PATCH 1/6] Allow for buffering of inputs --- .../clientcommands/command/SnakeCommand.java | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/src/main/java/net/earthcomputer/clientcommands/command/SnakeCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/SnakeCommand.java index ce90df3bd..2f0abb92e 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/SnakeCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/SnakeCommand.java @@ -17,9 +17,7 @@ import net.minecraft.util.math.Direction; import org.lwjgl.glfw.GLFW; -import java.util.LinkedList; -import java.util.ListIterator; -import java.util.Random; +import java.util.*; import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; @@ -56,6 +54,7 @@ class SnakeGameScreen extends Screen { private Direction direction = Direction.EAST; private Direction lastMoved = Direction.EAST; private final LinkedList snake = new LinkedList<>(); + private final Queue directionQueue = new ArrayDeque<>(); private Vec2i apple; SnakeGameScreen() { @@ -107,18 +106,21 @@ public void renderBackground(MatrixStack matrices) { @Override public boolean keyPressed(int keyCode, int scanCode, int modifiers) { if (client.options.forwardKey.matchesKey(keyCode, scanCode) || keyCode == GLFW.GLFW_KEY_UP) { - return this.setDirection(Direction.NORTH); + return enqueueMove(Direction.NORTH); } else if (client.options.leftKey.matchesKey(keyCode, scanCode) || keyCode == GLFW.GLFW_KEY_LEFT) { - return this.setDirection(Direction.WEST); + return enqueueMove(Direction.WEST); } else if (client.options.backKey.matchesKey(keyCode, scanCode) || keyCode == GLFW.GLFW_KEY_DOWN) { - return this.setDirection(Direction.SOUTH); + return enqueueMove(Direction.SOUTH); } else if (client.options.rightKey.matchesKey(keyCode, scanCode) || keyCode == GLFW.GLFW_KEY_RIGHT) { - return this.setDirection(Direction.EAST); + return enqueueMove(Direction.EAST); } return super.keyPressed(keyCode, scanCode, modifiers); } private void move() { + while (!directionQueue.isEmpty()) { + if (setDirection(directionQueue.remove())) break; + } Vec2i head = this.snake.getFirst(); this.snake.addFirst(new Vec2i(head.x() + this.direction.getOffsetX(), head.z() + this.direction.getOffsetZ())); this.lastMoved = this.direction; @@ -157,12 +159,20 @@ private void checkApple() { } private boolean setDirection(Direction direction) { - if (this.lastMoved == direction.getOpposite()) { + if (this.lastMoved == direction.getOpposite() || this.lastMoved == direction) { return false; } this.direction = direction; return true; } + + private boolean enqueueMove(Direction direction) { + while (directionQueue.size() > 2) { + directionQueue.remove(); + } + directionQueue.add(direction); + return true; + } } record Vec2i(int x, int z) { From 5009fe810bcca13795a064e25656fb1fd43dff7e Mon Sep 17 00:00:00 2001 From: "Josiah (Gaming32) Glosson" Date: Thu, 22 Dec 2022 11:32:35 -0600 Subject: [PATCH 2/6] Base multiplayer snake --- .../clientcommands/c2c/CCNetworkHandler.java | 76 +++- .../clientcommands/c2c/CCPacketHandler.java | 6 +- .../clientcommands/c2c/CCPacketListener.java | 10 +- .../clientcommands/c2c/StringBuf.java | 20 + .../c2c/packets/SnakeAddPlayersC2CPacket.java | 24 ++ .../c2c/packets/SnakeBodyC2CPacket.java | 26 ++ .../c2c/packets/SnakeInviteC2CPacket.java | 21 + .../c2c/packets/SnakeJoinC2CPacket.java | 21 + .../clientcommands/command/SnakeCommand.java | 368 ++++++++++++------ .../command/WhisperEncryptedCommand.java | 13 +- 10 files changed, 462 insertions(+), 123 deletions(-) create mode 100644 src/main/java/net/earthcomputer/clientcommands/c2c/packets/SnakeAddPlayersC2CPacket.java create mode 100644 src/main/java/net/earthcomputer/clientcommands/c2c/packets/SnakeBodyC2CPacket.java create mode 100644 src/main/java/net/earthcomputer/clientcommands/c2c/packets/SnakeInviteC2CPacket.java create mode 100644 src/main/java/net/earthcomputer/clientcommands/c2c/packets/SnakeJoinC2CPacket.java diff --git a/src/main/java/net/earthcomputer/clientcommands/c2c/CCNetworkHandler.java b/src/main/java/net/earthcomputer/clientcommands/c2c/CCNetworkHandler.java index 14fe4a280..40d006191 100644 --- a/src/main/java/net/earthcomputer/clientcommands/c2c/CCNetworkHandler.java +++ b/src/main/java/net/earthcomputer/clientcommands/c2c/CCNetworkHandler.java @@ -3,17 +3,22 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; import com.mojang.logging.LogUtils; -import net.earthcomputer.clientcommands.c2c.packets.MessageC2CPacket; +import net.earthcomputer.clientcommands.c2c.packets.*; +import net.earthcomputer.clientcommands.command.SnakeCommand; import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.PlayerListEntry; import net.minecraft.network.encryption.PlayerPublicKey; import net.minecraft.network.encryption.PublicPlayerSession; +import net.minecraft.text.ClickEvent; +import net.minecraft.text.HoverEvent; import net.minecraft.text.MutableText; import net.minecraft.text.Text; import net.minecraft.util.Formatting; import org.slf4j.Logger; import java.security.PublicKey; +import java.util.List; +import java.util.Optional; public class CCNetworkHandler implements CCPacketListener { @@ -32,6 +37,16 @@ public static CCNetworkHandler getInstance() { return instance; } + public static Optional getPlayerByName(String name) { + assert MinecraftClient.getInstance().getNetworkHandler() != null; + return MinecraftClient.getInstance() + .getNetworkHandler() + .getPlayerList() + .stream() + .filter(p -> p.getProfile().getName().equalsIgnoreCase(name)) + .findFirst(); + } + public void sendPacket(C2CPacket packet, PlayerListEntry recipient) throws CommandSyntaxException { Integer id = CCPacketHandler.getId(packet.getClass()); if (id == null) { @@ -79,4 +94,63 @@ public void onMessageC2CPacket(MessageC2CPacket packet) { Text text = prefix.append(Text.translatable("ccpacket.messageC2CPacket.incoming", sender, message).formatted(Formatting.GRAY)); MinecraftClient.getInstance().inGameHud.getChatHud().addMessage(text); } + + @Override + public void onSnakeInviteC2CPacket(SnakeInviteC2CPacket packet) { + // TODO: Use Text.translatable + MinecraftClient.getInstance().inGameHud.getChatHud().addMessage( + Text.literal(packet.sender()) + .append(" invited you to a game of snake. ") + .append( + Text.literal("[Accept]") + .styled(style -> + style.withColor(Formatting.GREEN) + .withHoverEvent(new HoverEvent( + HoverEvent.Action.SHOW_TEXT, Text.literal("Join game") + )) + .withClickEvent(new ClickEvent( + ClickEvent.Action.RUN_COMMAND, "/csnake join " + packet.sender() + )) + ) + ) + .styled(style -> style.withColor(Formatting.GRAY)) + ); + } + + @Override + public void onSnakeJoinC2CPacket(SnakeJoinC2CPacket packet) { + if (!(MinecraftClient.getInstance().currentScreen instanceof SnakeCommand.SnakeGameScreen snakeScreen)) return; + final SnakeAddPlayersC2CPacket addPlayersPacket = new SnakeAddPlayersC2CPacket(List.of(packet.sender())); + for (final String otherPlayer : snakeScreen.getOtherSnakes().keySet()) { + getPlayerByName(otherPlayer).ifPresent(actualPlayer -> { + try { + sendPacket(addPlayersPacket, actualPlayer); + } catch (CommandSyntaxException e) { + LOGGER.warn("Failed to recast snake player join", e); + } + }); + } + getPlayerByName(packet.sender()).ifPresent(sender -> { + try { + sendPacket(new SnakeAddPlayersC2CPacket(snakeScreen.getOtherSnakes().keySet()), sender); + } catch (CommandSyntaxException e) { + LOGGER.warn("Failed to reply to snake player join", e); + } + }); + snakeScreen.getOtherSnakes().put(packet.sender(), List.of()); // Reserve entry in player list + } + + @Override + public void onSnakeAddPlayersC2CPacket(SnakeAddPlayersC2CPacket packet) { + if (!(MinecraftClient.getInstance().currentScreen instanceof SnakeCommand.SnakeGameScreen snakeScreen)) return; + for (final String player : packet.players()) { + snakeScreen.getOtherSnakes().put(player, List.of()); + } + } + + @Override + public void onSnakeBodyC2CPacket(SnakeBodyC2CPacket packet) { + if (!(MinecraftClient.getInstance().currentScreen instanceof SnakeCommand.SnakeGameScreen snakeScreen)) return; + snakeScreen.getOtherSnakes().put(packet.sender(), packet.segments()); + } } diff --git a/src/main/java/net/earthcomputer/clientcommands/c2c/CCPacketHandler.java b/src/main/java/net/earthcomputer/clientcommands/c2c/CCPacketHandler.java index f3903b6cf..9f72a963f 100644 --- a/src/main/java/net/earthcomputer/clientcommands/c2c/CCPacketHandler.java +++ b/src/main/java/net/earthcomputer/clientcommands/c2c/CCPacketHandler.java @@ -2,7 +2,7 @@ import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; -import net.earthcomputer.clientcommands.c2c.packets.MessageC2CPacket; +import net.earthcomputer.clientcommands.c2c.packets.*; import net.minecraft.util.Util; import org.jetbrains.annotations.Nullable; @@ -17,6 +17,10 @@ public class CCPacketHandler { static { CCPacketHandler.register(MessageC2CPacket.class, MessageC2CPacket::new); + CCPacketHandler.register(SnakeInviteC2CPacket.class, SnakeInviteC2CPacket::new); + CCPacketHandler.register(SnakeJoinC2CPacket.class, SnakeJoinC2CPacket::new); + CCPacketHandler.register(SnakeAddPlayersC2CPacket.class, SnakeAddPlayersC2CPacket::new); + CCPacketHandler.register(SnakeBodyC2CPacket.class, SnakeBodyC2CPacket::new); } public static

void register(Class

packet, Function packetFactory) { diff --git a/src/main/java/net/earthcomputer/clientcommands/c2c/CCPacketListener.java b/src/main/java/net/earthcomputer/clientcommands/c2c/CCPacketListener.java index 734cb6e6c..a84268880 100644 --- a/src/main/java/net/earthcomputer/clientcommands/c2c/CCPacketListener.java +++ b/src/main/java/net/earthcomputer/clientcommands/c2c/CCPacketListener.java @@ -1,7 +1,15 @@ package net.earthcomputer.clientcommands.c2c; -import net.earthcomputer.clientcommands.c2c.packets.MessageC2CPacket; +import net.earthcomputer.clientcommands.c2c.packets.*; public interface CCPacketListener { void onMessageC2CPacket(MessageC2CPacket packet); + + void onSnakeInviteC2CPacket(SnakeInviteC2CPacket packet); + + void onSnakeJoinC2CPacket(SnakeJoinC2CPacket packet); + + void onSnakeAddPlayersC2CPacket(SnakeAddPlayersC2CPacket packet); + + void onSnakeBodyC2CPacket(SnakeBodyC2CPacket packet); } diff --git a/src/main/java/net/earthcomputer/clientcommands/c2c/StringBuf.java b/src/main/java/net/earthcomputer/clientcommands/c2c/StringBuf.java index 23909f489..b2d7c420d 100644 --- a/src/main/java/net/earthcomputer/clientcommands/c2c/StringBuf.java +++ b/src/main/java/net/earthcomputer/clientcommands/c2c/StringBuf.java @@ -1,6 +1,10 @@ package net.earthcomputer.clientcommands.c2c; import java.nio.charset.StandardCharsets; +import java.util.Collection; +import java.util.function.BiConsumer; +import java.util.function.Function; +import java.util.function.IntFunction; public class StringBuf { @@ -35,6 +39,15 @@ public int readInt() { return Integer.parseInt(this.readString()); } + public > C readCollection(IntFunction collectionCreator, Function elementReader) { + final int count = readInt(); + final C result = collectionCreator.apply(count); + for (int i = 0; i < count; i++) { + result.add(elementReader.apply(this)); + } + return result; + } + public void writeString(String string) { this.buffer.append(string).append('\0'); } @@ -42,4 +55,11 @@ public void writeString(String string) { public void writeInt(int integer) { this.buffer.append(integer).append('\0'); } + + public void writeCollection(Collection collection, BiConsumer elementWriter) { + writeInt(collection.size()); + for (final E element : collection) { + elementWriter.accept(this, element); + } + } } diff --git a/src/main/java/net/earthcomputer/clientcommands/c2c/packets/SnakeAddPlayersC2CPacket.java b/src/main/java/net/earthcomputer/clientcommands/c2c/packets/SnakeAddPlayersC2CPacket.java new file mode 100644 index 000000000..51861b11f --- /dev/null +++ b/src/main/java/net/earthcomputer/clientcommands/c2c/packets/SnakeAddPlayersC2CPacket.java @@ -0,0 +1,24 @@ +package net.earthcomputer.clientcommands.c2c.packets; + +import net.earthcomputer.clientcommands.c2c.C2CPacket; +import net.earthcomputer.clientcommands.c2c.CCPacketListener; +import net.earthcomputer.clientcommands.c2c.StringBuf; + +import java.util.ArrayList; +import java.util.Collection; + +public record SnakeAddPlayersC2CPacket(Collection players) implements C2CPacket { + public SnakeAddPlayersC2CPacket(StringBuf buf) { + this((Collection)buf.readCollection(ArrayList::new, StringBuf::readString)); + } + + @Override + public void write(StringBuf buf) { + buf.writeCollection(players, StringBuf::writeString); + } + + @Override + public void apply(CCPacketListener listener) { + listener.onSnakeAddPlayersC2CPacket(this); + } +} diff --git a/src/main/java/net/earthcomputer/clientcommands/c2c/packets/SnakeBodyC2CPacket.java b/src/main/java/net/earthcomputer/clientcommands/c2c/packets/SnakeBodyC2CPacket.java new file mode 100644 index 000000000..8c449f9a0 --- /dev/null +++ b/src/main/java/net/earthcomputer/clientcommands/c2c/packets/SnakeBodyC2CPacket.java @@ -0,0 +1,26 @@ +package net.earthcomputer.clientcommands.c2c.packets; + +import net.earthcomputer.clientcommands.c2c.C2CPacket; +import net.earthcomputer.clientcommands.c2c.CCPacketListener; +import net.earthcomputer.clientcommands.c2c.StringBuf; +import net.earthcomputer.clientcommands.command.SnakeCommand; + +import java.util.ArrayList; +import java.util.List; + +public record SnakeBodyC2CPacket(String sender, List segments) implements C2CPacket { + public SnakeBodyC2CPacket(StringBuf buf) { + this(buf.readString(), buf.readCollection(ArrayList::new, SnakeCommand.Vec2i::new)); + } + + @Override + public void write(StringBuf buf) { + buf.writeString(sender); + buf.writeCollection(segments, SnakeCommand.Vec2i::write); + } + + @Override + public void apply(CCPacketListener listener) { + listener.onSnakeBodyC2CPacket(this); + } +} diff --git a/src/main/java/net/earthcomputer/clientcommands/c2c/packets/SnakeInviteC2CPacket.java b/src/main/java/net/earthcomputer/clientcommands/c2c/packets/SnakeInviteC2CPacket.java new file mode 100644 index 000000000..c80a29519 --- /dev/null +++ b/src/main/java/net/earthcomputer/clientcommands/c2c/packets/SnakeInviteC2CPacket.java @@ -0,0 +1,21 @@ +package net.earthcomputer.clientcommands.c2c.packets; + +import net.earthcomputer.clientcommands.c2c.C2CPacket; +import net.earthcomputer.clientcommands.c2c.CCPacketListener; +import net.earthcomputer.clientcommands.c2c.StringBuf; + +public record SnakeInviteC2CPacket(String sender) implements C2CPacket { + public SnakeInviteC2CPacket(StringBuf buf) { + this(buf.readString()); + } + + @Override + public void write(StringBuf buf) { + buf.writeString(sender); + } + + @Override + public void apply(CCPacketListener listener) { + listener.onSnakeInviteC2CPacket(this); + } +} diff --git a/src/main/java/net/earthcomputer/clientcommands/c2c/packets/SnakeJoinC2CPacket.java b/src/main/java/net/earthcomputer/clientcommands/c2c/packets/SnakeJoinC2CPacket.java new file mode 100644 index 000000000..a189d72ff --- /dev/null +++ b/src/main/java/net/earthcomputer/clientcommands/c2c/packets/SnakeJoinC2CPacket.java @@ -0,0 +1,21 @@ +package net.earthcomputer.clientcommands.c2c.packets; + +import net.earthcomputer.clientcommands.c2c.C2CPacket; +import net.earthcomputer.clientcommands.c2c.CCPacketListener; +import net.earthcomputer.clientcommands.c2c.StringBuf; + +public record SnakeJoinC2CPacket(String sender) implements C2CPacket { + public SnakeJoinC2CPacket(StringBuf buf) { + this(buf.readString()); + } + + @Override + public void write(StringBuf buf) { + buf.writeString(sender); + } + + @Override + public void apply(CCPacketListener listener) { + listener.onSnakeJoinC2CPacket(this); + } +} diff --git a/src/main/java/net/earthcomputer/clientcommands/command/SnakeCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/SnakeCommand.java index 2f0abb92e..0c323ddf5 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/SnakeCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/SnakeCommand.java @@ -1,179 +1,319 @@ package net.earthcomputer.clientcommands.command; +import com.mojang.authlib.GameProfile; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.brigadier.Command; import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; +import com.mojang.logging.LogUtils; +import net.earthcomputer.clientcommands.c2c.CCNetworkHandler; +import net.earthcomputer.clientcommands.c2c.StringBuf; +import net.earthcomputer.clientcommands.c2c.packets.SnakeBodyC2CPacket; +import net.earthcomputer.clientcommands.c2c.packets.SnakeInviteC2CPacket; +import net.earthcomputer.clientcommands.c2c.packets.SnakeJoinC2CPacket; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.DrawableHelper; import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.network.PlayerListEntry; import net.minecraft.client.render.GameRenderer; import net.minecraft.client.sound.PositionedSoundInstance; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.sound.SoundEvents; import net.minecraft.text.MutableText; import net.minecraft.text.Text; +import net.minecraft.util.Formatting; import net.minecraft.util.Identifier; import net.minecraft.util.math.Direction; +import org.jetbrains.annotations.Nullable; import org.lwjgl.glfw.GLFW; +import org.slf4j.Logger; import java.util.*; +import java.util.stream.Collectors; -import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; +import static dev.xpple.clientarguments.arguments.CGameProfileArgumentType.gameProfile; +import static dev.xpple.clientarguments.arguments.CGameProfileArgumentType.getCProfileArgument; +import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.argument; +import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal; public class SnakeCommand { + private static final Logger LOGGER = LogUtils.getLogger(); + private static final SimpleCommandExceptionType PLAYER_NOT_FOUND_EXCEPTION = new SimpleCommandExceptionType(Text.translatable("commands.cwe.playerNotFound")); + public static void register(CommandDispatcher dispatcher) { dispatcher.register(literal("csnake") - .executes(ctx -> snake(ctx.getSource()))); + .executes(ctx -> snake(ctx.getSource(), null)) + .then(literal("invite") + .then(argument("player", gameProfile()) + .executes(ctx -> invite(ctx.getSource(), getCProfileArgument(ctx, "player"))) + ) + ) + .then(literal("join") + .then(argument("player", gameProfile()) + .executes(ctx -> joinGame(ctx.getSource(), getCProfileArgument(ctx, "player"))) + ) + ) + ); + } + + private static int joinGame(FabricClientCommandSource source, Collection profiles) throws CommandSyntaxException { + if (profiles.size() != 1) { + throw PLAYER_NOT_FOUND_EXCEPTION.create(); + } + assert source.getClient().getNetworkHandler() != null; + final PlayerListEntry gameToJoin = CCNetworkHandler.getPlayerByName(profiles.iterator().next().getName()) + .orElseThrow(PLAYER_NOT_FOUND_EXCEPTION::create); + return snake(source, gameToJoin); } - private static int snake(FabricClientCommandSource source) { + private static int snake(FabricClientCommandSource source, @Nullable PlayerListEntry gameToJoin) throws CommandSyntaxException { + final String otherSnake; + if (gameToJoin != null) { + otherSnake = gameToJoin.getProfile().getName(); + assert source.getClient().getNetworkHandler() != null; + CCNetworkHandler.getInstance().sendPacket(new SnakeJoinC2CPacket( + source.getClient().getNetworkHandler().getProfile().getName() + ), gameToJoin); + } else { + otherSnake = null; + } /* After executing a command, the current screen will be closed (the chat hud). And if you open a new screen in a command, that new screen will be closed instantly along with the chat hud. Slightly delaying the opening of the screen fixes this issue. */ - source.getClient().send(() -> source.getClient().setScreen(new SnakeGameScreen())); + source.getClient().send(() -> source.getClient().setScreen(new SnakeGameScreen(otherSnake))); return Command.SINGLE_SUCCESS; } -} -class SnakeGameScreen extends Screen { + private static int invite(FabricClientCommandSource source, Collection profiles) throws CommandSyntaxException { + assert source.getClient().getNetworkHandler() != null; + final String sender = source.getClient().getNetworkHandler().getProfile().getName(); + final Set names = profiles.stream() + .map(GameProfile::getName) + .collect(Collectors.toCollection(() -> new TreeSet<>(String.CASE_INSENSITIVE_ORDER))); + try { + final long inviteCount = source.getClient().getNetworkHandler().getPlayerList().stream() + .filter(entry -> names.contains(entry.getProfile().getName())) + .peek(player -> { + final SnakeInviteC2CPacket packet = new SnakeInviteC2CPacket(sender); + try { + CCNetworkHandler.getInstance().sendPacket(packet, player); + } catch (CommandSyntaxException e) { + throw new RuntimeException(e); + } + // TODO: Text.translatable + source.sendFeedback( + Text.literal("Invited ") + .append(Objects.requireNonNullElseGet( + player.getDisplayName(), () -> Text.literal(player.getProfile().getName()) + )) + .append(" to a game of snake.") + .styled(style -> style.withColor(Formatting.GRAY)) + ); + }) + .count(); + if (inviteCount == 0L) { + source.sendFeedback(Text.literal("Couldn't find any players.")); + } + snake(source, null); + return (int)inviteCount; + } catch (RuntimeException e) { + if (e.getCause() instanceof CommandSyntaxException syntaxException) { + throw syntaxException; + } + throw e; + } + } - private static final MinecraftClient client = MinecraftClient.getInstance(); + public static class SnakeGameScreen extends Screen { - private static final Identifier GRID_TEXTURE = new Identifier("clientcommands:textures/snake_grid.png"); + private static final MinecraftClient client = MinecraftClient.getInstance(); - private static final Random random = new Random(); + private static final Identifier GRID_TEXTURE = new Identifier("clientcommands:textures/snake_grid.png"); - private static final int MAX_X = 16; - private static final int MAX_Z = 16; + private static final Random random = new Random(); - private int tickCounter = 10; - private Direction direction = Direction.EAST; - private Direction lastMoved = Direction.EAST; - private final LinkedList snake = new LinkedList<>(); - private final Queue directionQueue = new ArrayDeque<>(); - private Vec2i apple; + private static final int MAX_X = 16; + private static final int MAX_Z = 16; - SnakeGameScreen() { - super(Text.translatable("snakeGame.title")); - this.snake.add(new Vec2i(6, 8)); - this.snake.add(new Vec2i(5, 8)); - this.snake.add(new Vec2i(4, 8)); - do { - this.apple = new Vec2i(random.nextInt(MAX_X + 1), random.nextInt(MAX_Z + 1)); - } while (this.snake.contains(this.apple)); - } + private int tickCounter = 10; + private Direction direction = Direction.EAST; + private Direction lastMoved = Direction.EAST; + private final LinkedList snake = new LinkedList<>(); + private final Map> otherSnakes = new HashMap<>(); + private final Queue directionQueue = new ArrayDeque<>(); + private Vec2i apple; - @Override - public void tick() { - if (--this.tickCounter < 0) { - this.tickCounter = 3; - this.move(); + SnakeGameScreen(@Nullable String otherSnake) { + super(Text.translatable("snakeGame.title")); + if (otherSnake != null) { + otherSnakes.put(otherSnake, List.of()); + } + this.snake.add(new Vec2i(6, 8)); + this.snake.add(new Vec2i(5, 8)); + this.snake.add(new Vec2i(4, 8)); + do { + this.apple = new Vec2i(random.nextInt(MAX_X + 1), random.nextInt(MAX_Z + 1)); + } while (this.snake.contains(this.apple)); } - } - @Override - public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) { - this.renderBackground(matrices); - super.render(matrices, mouseX, mouseY, delta); - } + @Override + public void tick() { + if (--this.tickCounter < 0) { + this.tickCounter = 3; + this.move(); + } + } - @Override - public void renderBackground(MatrixStack matrices) { - super.renderBackground(matrices); - int startX = (this.width - 289) / 2; - int startY = (this.height - 289) / 2; - - drawTextWithShadow(matrices, client.textRenderer, this.title, startX, startY - 10, 0xff_ffffff); - MutableText score = Text.translatable("snakeGame.score", this.snake.size()); - drawCenteredText(matrices, client.textRenderer, score, this.width / 2, startY - 10, 0xff_ffffff); - - RenderSystem.setShader(GameRenderer::getPositionTexProgram); - RenderSystem.setShaderColor(1.0f, 1.0f, 1.0f, 1.0f); - RenderSystem.setShaderTexture(0, GRID_TEXTURE); - drawTexture(matrices, startX, startY, 0, 0, 289, 289, 289, 289); - int scaleX = MAX_X + 1; - int scaleZ = MAX_Z + 1; - DrawableHelper.fill(matrices, startX + this.apple.x() * scaleX, startY + this.apple.z() * scaleZ, startX + this.apple.x() * scaleX + scaleX, startY + this.apple.z() * scaleZ + scaleZ, 0xff_f52559); - for (Vec2i vec : this.snake) { - DrawableHelper.fill(matrices, startX + vec.x() * scaleX, startY + vec.z() * scaleZ, startX + vec.x() * scaleX + scaleX, startY + vec.z() * scaleZ + scaleZ, 0xff_1f2df6); + @Override + public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) { + this.renderBackground(matrices); + super.render(matrices, mouseX, mouseY, delta); } - } - @Override - public boolean keyPressed(int keyCode, int scanCode, int modifiers) { - if (client.options.forwardKey.matchesKey(keyCode, scanCode) || keyCode == GLFW.GLFW_KEY_UP) { - return enqueueMove(Direction.NORTH); - } else if (client.options.leftKey.matchesKey(keyCode, scanCode) || keyCode == GLFW.GLFW_KEY_LEFT) { - return enqueueMove(Direction.WEST); - } else if (client.options.backKey.matchesKey(keyCode, scanCode) || keyCode == GLFW.GLFW_KEY_DOWN) { - return enqueueMove(Direction.SOUTH); - } else if (client.options.rightKey.matchesKey(keyCode, scanCode) || keyCode == GLFW.GLFW_KEY_RIGHT) { - return enqueueMove(Direction.EAST); - } - return super.keyPressed(keyCode, scanCode, modifiers); - } + @Override + public void renderBackground(MatrixStack matrices) { + super.renderBackground(matrices); + int startX = (this.width - 289) / 2; + int startY = (this.height - 289) / 2; - private void move() { - while (!directionQueue.isEmpty()) { - if (setDirection(directionQueue.remove())) break; + drawTextWithShadow(matrices, client.textRenderer, this.title, startX, startY - 10, 0xff_ffffff); + MutableText score = Text.translatable("snakeGame.score", this.snake.size()); + drawCenteredText(matrices, client.textRenderer, score, this.width / 2, startY - 10, 0xff_ffffff); + + RenderSystem.setShader(GameRenderer::getPositionTexProgram); + RenderSystem.setShaderColor(1.0f, 1.0f, 1.0f, 1.0f); + RenderSystem.setShaderTexture(0, GRID_TEXTURE); + drawTexture(matrices, startX, startY, 0, 0, 289, 289, 289, 289); + int scaleX = MAX_X + 1; + int scaleZ = MAX_Z + 1; + DrawableHelper.fill(matrices, startX + this.apple.x() * scaleX, startY + this.apple.z() * scaleZ, startX + this.apple.x() * scaleX + scaleX, startY + this.apple.z() * scaleZ + scaleZ, 0xff_f52559); + for (Vec2i vec : this.snake) { + DrawableHelper.fill(matrices, startX + vec.x() * scaleX, startY + vec.z() * scaleZ, startX + vec.x() * scaleX + scaleX, startY + vec.z() * scaleZ + scaleZ, 0xff_1f2df6); + } + for (final Map.Entry> otherSnake : otherSnakes.entrySet()) { + for (final Vec2i vec : otherSnake.getValue()) { + DrawableHelper.fill(matrices, startX + vec.x() * scaleX, startY + vec.z() * scaleZ, startX + vec.x() * scaleX + scaleX, startY + vec.z() * scaleZ + scaleZ, 0xffffa500); + } + if (!otherSnake.getValue().isEmpty()) { + final Vec2i head = otherSnake.getValue().get(0); + DrawableHelper.drawCenteredText( + matrices, textRenderer, + otherSnake.getKey(), + startX + head.x() * scaleX + scaleX / 2, + startY + head.z() * scaleZ - scaleZ, + 0xffffffff + ); + } + } } - Vec2i head = this.snake.getFirst(); - this.snake.addFirst(new Vec2i(head.x() + this.direction.getOffsetX(), head.z() + this.direction.getOffsetZ())); - this.lastMoved = this.direction; - if (this.checkGameOver()) { - client.getSoundManager().play(PositionedSoundInstance.master(SoundEvents.ENTITY_OCELOT_DEATH, 1)); - this.close(); - return; + + @Override + public boolean keyPressed(int keyCode, int scanCode, int modifiers) { + if (client.options.forwardKey.matchesKey(keyCode, scanCode) || keyCode == GLFW.GLFW_KEY_UP) { + return enqueueMove(Direction.NORTH); + } else if (client.options.leftKey.matchesKey(keyCode, scanCode) || keyCode == GLFW.GLFW_KEY_LEFT) { + return enqueueMove(Direction.WEST); + } else if (client.options.backKey.matchesKey(keyCode, scanCode) || keyCode == GLFW.GLFW_KEY_DOWN) { + return enqueueMove(Direction.SOUTH); + } else if (client.options.rightKey.matchesKey(keyCode, scanCode) || keyCode == GLFW.GLFW_KEY_RIGHT) { + return enqueueMove(Direction.EAST); + } + return super.keyPressed(keyCode, scanCode, modifiers); } - this.checkApple(); - } - private boolean checkGameOver() { - Vec2i head = this.snake.getFirst(); - if (head.x() < 0 || head.x() > MAX_X || head.z() < 0 || head.z() > MAX_Z) { - return true; + private void move() { + while (!directionQueue.isEmpty()) { + if (setDirection(directionQueue.remove())) break; + } + Vec2i head = this.snake.getFirst(); + this.snake.addFirst(new Vec2i(head.x() + this.direction.getOffsetX(), head.z() + this.direction.getOffsetZ())); + this.lastMoved = this.direction; + if (this.checkGameOver()) { + client.getSoundManager().play(PositionedSoundInstance.master(SoundEvents.ENTITY_OCELOT_DEATH, 1)); + this.close(); + return; + } + this.checkApple(); + syncPlayerData(); } - ListIterator it = this.snake.listIterator(1); - while (it.hasNext()) { - if (it.next().equals(head)) { + + private boolean checkGameOver() { + Vec2i head = this.snake.getFirst(); + if (head.x() < 0 || head.x() > MAX_X || head.z() < 0 || head.z() > MAX_Z) { return true; } + ListIterator it = this.snake.listIterator(1); + while (it.hasNext()) { + if (it.next().equals(head)) { + return true; + } + } + return false; } - return false; - } - private void checkApple() { - Vec2i head = this.snake.getFirst(); - if (head.equals(this.apple)) { - client.getSoundManager().play(PositionedSoundInstance.master(SoundEvents.ENTITY_GENERIC_EAT, 1)); - do { - this.apple = new Vec2i(random.nextInt(MAX_X + 1), random.nextInt(MAX_Z + 1)); - } while (this.snake.contains(this.apple)); - } else { - this.snake.removeLast(); + private void checkApple() { + Vec2i head = this.snake.getFirst(); + if (head.equals(this.apple)) { + client.getSoundManager().play(PositionedSoundInstance.master(SoundEvents.ENTITY_GENERIC_EAT, 1)); + do { + this.apple = new Vec2i(random.nextInt(MAX_X + 1), random.nextInt(MAX_Z + 1)); + } while (this.snake.contains(this.apple)); + } else { + this.snake.removeLast(); + } } - } - private boolean setDirection(Direction direction) { - if (this.lastMoved == direction.getOpposite() || this.lastMoved == direction) { - return false; + private void syncPlayerData() { + if (otherSnakes.isEmpty()) return; + assert client.getNetworkHandler() != null; + final SnakeBodyC2CPacket packet = new SnakeBodyC2CPacket( + client.getNetworkHandler().getProfile().getName(), snake + ); + for (final String otherSnake : otherSnakes.keySet()) { + CCNetworkHandler.getPlayerByName(otherSnake).ifPresent(player -> { + try { + CCNetworkHandler.getInstance().sendPacket(packet, player); + } catch (CommandSyntaxException e) { + LOGGER.warn("Failed to sync snake data to " + otherSnake, e); + } + }); + } + } + + private boolean setDirection(Direction direction) { + if (this.lastMoved == direction.getOpposite() || this.lastMoved == direction) { + return false; + } + this.direction = direction; + return true; + } + + private boolean enqueueMove(Direction direction) { + while (directionQueue.size() > 2) { + directionQueue.remove(); + } + directionQueue.add(direction); + return true; } - this.direction = direction; - return true; - } - private boolean enqueueMove(Direction direction) { - while (directionQueue.size() > 2) { - directionQueue.remove(); + public Map> getOtherSnakes() { + return otherSnakes; } - directionQueue.add(direction); - return true; } -} -record Vec2i(int x, int z) { + public record Vec2i(int x, int z) { + public Vec2i(StringBuf buf) { + this(buf.readInt(), buf.readInt()); + } + + public static void write(StringBuf buf, Vec2i vec) { + buf.writeInt(vec.x); + buf.writeInt(vec.z); + } + } } diff --git a/src/main/java/net/earthcomputer/clientcommands/command/WhisperEncryptedCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/WhisperEncryptedCommand.java index 5218063c0..26a8f2489 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/WhisperEncryptedCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/WhisperEncryptedCommand.java @@ -15,9 +15,12 @@ import java.util.Collection; -import static com.mojang.brigadier.arguments.StringArgumentType.*; -import static dev.xpple.clientarguments.arguments.CGameProfileArgumentType.*; -import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*; +import static com.mojang.brigadier.arguments.StringArgumentType.getString; +import static com.mojang.brigadier.arguments.StringArgumentType.greedyString; +import static dev.xpple.clientarguments.arguments.CGameProfileArgumentType.gameProfile; +import static dev.xpple.clientarguments.arguments.CGameProfileArgumentType.getCProfileArgument; +import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.argument; +import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.literal; public class WhisperEncryptedCommand { @@ -35,9 +38,7 @@ private static int whisper(FabricClientCommandSource source, Collection p.getProfile().getName().equalsIgnoreCase(profiles.iterator().next().getName())) - .findFirst() + PlayerListEntry recipient = CCNetworkHandler.getPlayerByName(profiles.iterator().next().getName()) .orElseThrow(PLAYER_NOT_FOUND_EXCEPTION::create); MessageC2CPacket packet = new MessageC2CPacket(source.getClient().getNetworkHandler().getProfile().getName(), message); From 82a7a8d8119fcbd5b3855111fd4bd532760207f8 Mon Sep 17 00:00:00 2001 From: "Josiah (Gaming32) Glosson" Date: Thu, 22 Dec 2022 12:25:39 -0600 Subject: [PATCH 3/6] Make grid bigger --- .../clientcommands/command/SnakeCommand.java | 14 +++++++------- .../clientcommands/textures/snake_grid.png | Bin 1288 -> 282 bytes 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/earthcomputer/clientcommands/command/SnakeCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/SnakeCommand.java index 0c323ddf5..645185134 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/SnakeCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/SnakeCommand.java @@ -137,8 +137,8 @@ public static class SnakeGameScreen extends Screen { private static final Random random = new Random(); - private static final int MAX_X = 16; - private static final int MAX_Z = 16; + private static final int MAX_X = 24; + private static final int MAX_Z = 24; private int tickCounter = 10; private Direction direction = Direction.EAST; @@ -178,8 +178,8 @@ public void render(MatrixStack matrices, int mouseX, int mouseY, float delta) { @Override public void renderBackground(MatrixStack matrices) { super.renderBackground(matrices); - int startX = (this.width - 289) / 2; - int startY = (this.height - 289) / 2; + int startX = (this.width - 425) / 2; + int startY = (this.height - 425) / 2; drawTextWithShadow(matrices, client.textRenderer, this.title, startX, startY - 10, 0xff_ffffff); MutableText score = Text.translatable("snakeGame.score", this.snake.size()); @@ -188,9 +188,9 @@ public void renderBackground(MatrixStack matrices) { RenderSystem.setShader(GameRenderer::getPositionTexProgram); RenderSystem.setShaderColor(1.0f, 1.0f, 1.0f, 1.0f); RenderSystem.setShaderTexture(0, GRID_TEXTURE); - drawTexture(matrices, startX, startY, 0, 0, 289, 289, 289, 289); - int scaleX = MAX_X + 1; - int scaleZ = MAX_Z + 1; + drawTexture(matrices, startX, startY, 0, 0, 425, 425, 425, 425); + int scaleX = 17; + int scaleZ = 17; DrawableHelper.fill(matrices, startX + this.apple.x() * scaleX, startY + this.apple.z() * scaleZ, startX + this.apple.x() * scaleX + scaleX, startY + this.apple.z() * scaleZ + scaleZ, 0xff_f52559); for (Vec2i vec : this.snake) { DrawableHelper.fill(matrices, startX + vec.x() * scaleX, startY + vec.z() * scaleZ, startX + vec.x() * scaleX + scaleX, startY + vec.z() * scaleZ + scaleZ, 0xff_1f2df6); diff --git a/src/main/resources/assets/clientcommands/textures/snake_grid.png b/src/main/resources/assets/clientcommands/textures/snake_grid.png index 52014adc074febcc87e6d0adb721c12125fe0ca5..43ab2249bb89197e2ee95747d376153f6c950b9c 100644 GIT binary patch literal 282 zcmeAS@N?(olHy`uVBq!ia0y~yU|b2pjLaaBN6`{Mim^Dz-HBn{IhmJ04okYDuOkD) z#(wTUiL5|ATYyi9tI|fF#8Z|g$!9r$BAf*tk;M!QLSW3;qqLJ9C^*;C#WAGf*4wKK z1)CWdoG%u1oa#HE(Zja5bV^@?OF@gM!Gd{b?{5C-IqjcO>FMR}ZQsrb@Endyc{^+8 z|C-(DZz{_V7`(B4w$s4J*N`VT7>L^26SKM!XUs|hB8g?ofM_1~;VUAC4NQ+3n3h(6 ogxXeWf)Geu)+=0EzlnJ3wQX12AelS87wAw1Pgg&ebxsLQ0OT=dasU7T literal 1288 zcmeAS@N?(olHy`uVBq!ia0y~yU{nNQ4mO~OfvlJ`kYX$ja(7}_cTVOdki(Mh=+ApjwX~il zd%Evy{mqZpzl$p=FhNLBK^3cn)xG)GA1_Yhv(*Kg z@>G?e4s@e{PU`QUW%*^xn}{+U>uJ;RSca|@;yl^+=U;yeG;W^<@;b_J-AU<3hz-GjK4ou z)m%BVa5=~UH#hDfAr2v~cxCaP(qQ~sX=|7D?80S`1Kvc$VNEb_FJnn!rE|Vh7>pnP z?U9o=Eq;*+azL4mKCWbo(-l_dep48X74>lxrswCi6b9oSdrH&u=Y13fqrk6Uzj#-9 X9Y}Xyb=wnIdNFvq`njxgN@xNAMWUc| From 0ddf1cf7ebd15d79c752841487894c31a250339b Mon Sep 17 00:00:00 2001 From: "Josiah (Gaming32) Glosson" Date: Thu, 22 Dec 2022 13:27:51 -0600 Subject: [PATCH 4/6] Sync player deaths --- .../clientcommands/c2c/CCNetworkHandler.java | 8 +++++- .../clientcommands/c2c/CCPacketHandler.java | 1 + .../clientcommands/c2c/CCPacketListener.java | 2 ++ .../packets/SnakeRemovePlayerC2CPacket.java | 21 ++++++++++++++ .../clientcommands/command/SnakeCommand.java | 28 ++++++++++++++++--- 5 files changed, 55 insertions(+), 5 deletions(-) create mode 100644 src/main/java/net/earthcomputer/clientcommands/c2c/packets/SnakeRemovePlayerC2CPacket.java diff --git a/src/main/java/net/earthcomputer/clientcommands/c2c/CCNetworkHandler.java b/src/main/java/net/earthcomputer/clientcommands/c2c/CCNetworkHandler.java index 40d006191..89890f761 100644 --- a/src/main/java/net/earthcomputer/clientcommands/c2c/CCNetworkHandler.java +++ b/src/main/java/net/earthcomputer/clientcommands/c2c/CCNetworkHandler.java @@ -102,7 +102,7 @@ public void onSnakeInviteC2CPacket(SnakeInviteC2CPacket packet) { Text.literal(packet.sender()) .append(" invited you to a game of snake. ") .append( - Text.literal("[Accept]") + Text.literal("[Join]") .styled(style -> style.withColor(Formatting.GREEN) .withHoverEvent(new HoverEvent( @@ -153,4 +153,10 @@ public void onSnakeBodyC2CPacket(SnakeBodyC2CPacket packet) { if (!(MinecraftClient.getInstance().currentScreen instanceof SnakeCommand.SnakeGameScreen snakeScreen)) return; snakeScreen.getOtherSnakes().put(packet.sender(), packet.segments()); } + + @Override + public void onSnakeRemovePlayerC2CPacket(SnakeRemovePlayerC2CPacket packet) { + if (!(MinecraftClient.getInstance().currentScreen instanceof SnakeCommand.SnakeGameScreen snakeScreen)) return; + snakeScreen.getOtherSnakes().remove(packet.player()); + } } diff --git a/src/main/java/net/earthcomputer/clientcommands/c2c/CCPacketHandler.java b/src/main/java/net/earthcomputer/clientcommands/c2c/CCPacketHandler.java index 9f72a963f..308101a21 100644 --- a/src/main/java/net/earthcomputer/clientcommands/c2c/CCPacketHandler.java +++ b/src/main/java/net/earthcomputer/clientcommands/c2c/CCPacketHandler.java @@ -21,6 +21,7 @@ public class CCPacketHandler { CCPacketHandler.register(SnakeJoinC2CPacket.class, SnakeJoinC2CPacket::new); CCPacketHandler.register(SnakeAddPlayersC2CPacket.class, SnakeAddPlayersC2CPacket::new); CCPacketHandler.register(SnakeBodyC2CPacket.class, SnakeBodyC2CPacket::new); + CCPacketHandler.register(SnakeRemovePlayerC2CPacket.class, SnakeRemovePlayerC2CPacket::new); } public static

void register(Class

packet, Function packetFactory) { diff --git a/src/main/java/net/earthcomputer/clientcommands/c2c/CCPacketListener.java b/src/main/java/net/earthcomputer/clientcommands/c2c/CCPacketListener.java index a84268880..9d70e481d 100644 --- a/src/main/java/net/earthcomputer/clientcommands/c2c/CCPacketListener.java +++ b/src/main/java/net/earthcomputer/clientcommands/c2c/CCPacketListener.java @@ -12,4 +12,6 @@ public interface CCPacketListener { void onSnakeAddPlayersC2CPacket(SnakeAddPlayersC2CPacket packet); void onSnakeBodyC2CPacket(SnakeBodyC2CPacket packet); + + void onSnakeRemovePlayerC2CPacket(SnakeRemovePlayerC2CPacket packet); } diff --git a/src/main/java/net/earthcomputer/clientcommands/c2c/packets/SnakeRemovePlayerC2CPacket.java b/src/main/java/net/earthcomputer/clientcommands/c2c/packets/SnakeRemovePlayerC2CPacket.java new file mode 100644 index 000000000..4abd1a814 --- /dev/null +++ b/src/main/java/net/earthcomputer/clientcommands/c2c/packets/SnakeRemovePlayerC2CPacket.java @@ -0,0 +1,21 @@ +package net.earthcomputer.clientcommands.c2c.packets; + +import net.earthcomputer.clientcommands.c2c.C2CPacket; +import net.earthcomputer.clientcommands.c2c.CCPacketListener; +import net.earthcomputer.clientcommands.c2c.StringBuf; + +public record SnakeRemovePlayerC2CPacket(String player) implements C2CPacket { + public SnakeRemovePlayerC2CPacket(StringBuf buf) { + this(buf.readString()); + } + + @Override + public void write(StringBuf buf) { + buf.writeString(player); + } + + @Override + public void apply(CCPacketListener listener) { + listener.onSnakeRemovePlayerC2CPacket(this); + } +} diff --git a/src/main/java/net/earthcomputer/clientcommands/command/SnakeCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/SnakeCommand.java index 645185134..6bb19ec72 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/SnakeCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/SnakeCommand.java @@ -12,6 +12,7 @@ import net.earthcomputer.clientcommands.c2c.packets.SnakeBodyC2CPacket; import net.earthcomputer.clientcommands.c2c.packets.SnakeInviteC2CPacket; import net.earthcomputer.clientcommands.c2c.packets.SnakeJoinC2CPacket; +import net.earthcomputer.clientcommands.c2c.packets.SnakeRemovePlayerC2CPacket; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.DrawableHelper; @@ -191,10 +192,6 @@ public void renderBackground(MatrixStack matrices) { drawTexture(matrices, startX, startY, 0, 0, 425, 425, 425, 425); int scaleX = 17; int scaleZ = 17; - DrawableHelper.fill(matrices, startX + this.apple.x() * scaleX, startY + this.apple.z() * scaleZ, startX + this.apple.x() * scaleX + scaleX, startY + this.apple.z() * scaleZ + scaleZ, 0xff_f52559); - for (Vec2i vec : this.snake) { - DrawableHelper.fill(matrices, startX + vec.x() * scaleX, startY + vec.z() * scaleZ, startX + vec.x() * scaleX + scaleX, startY + vec.z() * scaleZ + scaleZ, 0xff_1f2df6); - } for (final Map.Entry> otherSnake : otherSnakes.entrySet()) { for (final Vec2i vec : otherSnake.getValue()) { DrawableHelper.fill(matrices, startX + vec.x() * scaleX, startY + vec.z() * scaleZ, startX + vec.x() * scaleX + scaleX, startY + vec.z() * scaleZ + scaleZ, 0xffffa500); @@ -210,6 +207,10 @@ public void renderBackground(MatrixStack matrices) { ); } } + DrawableHelper.fill(matrices, startX + this.apple.x() * scaleX, startY + this.apple.z() * scaleZ, startX + this.apple.x() * scaleX + scaleX, startY + this.apple.z() * scaleZ + scaleZ, 0xff_f52559); + for (Vec2i vec : this.snake) { + DrawableHelper.fill(matrices, startX + vec.x() * scaleX, startY + vec.z() * scaleZ, startX + vec.x() * scaleX + scaleX, startY + vec.z() * scaleZ + scaleZ, 0xff_1f2df6); + } } @Override @@ -242,6 +243,25 @@ private void move() { syncPlayerData(); } + @Override + public void close() { + super.close(); + if (otherSnakes.isEmpty()) return; + assert client.getNetworkHandler() != null; + final SnakeRemovePlayerC2CPacket packet = new SnakeRemovePlayerC2CPacket( + client.getNetworkHandler().getProfile().getName() + ); + for (final String otherSnake : otherSnakes.keySet()) { + CCNetworkHandler.getPlayerByName(otherSnake).ifPresent(player -> { + try { + CCNetworkHandler.getInstance().sendPacket(packet, player); + } catch (CommandSyntaxException e) { + LOGGER.warn("Failed to send removal packet to " + otherSnake, e); + } + }); + } + } + private boolean checkGameOver() { Vec2i head = this.snake.getFirst(); if (head.x() < 0 || head.x() > MAX_X || head.z() < 0 || head.z() > MAX_Z) { From 87e727e43e3eeba7993705ea57631afcb3054e8e Mon Sep 17 00:00:00 2001 From: "Josiah (Gaming32) Glosson" Date: Thu, 22 Dec 2022 13:53:12 -0600 Subject: [PATCH 5/6] Sync apples --- .../clientcommands/c2c/CCNetworkHandler.java | 8 ++++ .../clientcommands/c2c/CCPacketHandler.java | 1 + .../clientcommands/c2c/CCPacketListener.java | 2 + .../c2c/packets/SnakeSyncAppleC2CPacket.java | 22 +++++++++ .../clientcommands/command/SnakeCommand.java | 48 +++++++++++-------- 5 files changed, 60 insertions(+), 21 deletions(-) create mode 100644 src/main/java/net/earthcomputer/clientcommands/c2c/packets/SnakeSyncAppleC2CPacket.java diff --git a/src/main/java/net/earthcomputer/clientcommands/c2c/CCNetworkHandler.java b/src/main/java/net/earthcomputer/clientcommands/c2c/CCNetworkHandler.java index 89890f761..6088e6791 100644 --- a/src/main/java/net/earthcomputer/clientcommands/c2c/CCNetworkHandler.java +++ b/src/main/java/net/earthcomputer/clientcommands/c2c/CCNetworkHandler.java @@ -78,6 +78,7 @@ public void sendPacket(C2CPacket packet, PlayerListEntry recipient) throws Comma if (commandString.length() >= 256) { throw MESSAGE_TOO_LONG_EXCEPTION.create(); } + assert MinecraftClient.getInstance().getNetworkHandler() != null; MinecraftClient.getInstance().getNetworkHandler().sendChatCommand(commandString); OutgoingPacketFilter.addPacket(packetString); } @@ -133,6 +134,7 @@ public void onSnakeJoinC2CPacket(SnakeJoinC2CPacket packet) { getPlayerByName(packet.sender()).ifPresent(sender -> { try { sendPacket(new SnakeAddPlayersC2CPacket(snakeScreen.getOtherSnakes().keySet()), sender); + sendPacket(new SnakeSyncAppleC2CPacket(snakeScreen.getApple()), sender); } catch (CommandSyntaxException e) { LOGGER.warn("Failed to reply to snake player join", e); } @@ -159,4 +161,10 @@ public void onSnakeRemovePlayerC2CPacket(SnakeRemovePlayerC2CPacket packet) { if (!(MinecraftClient.getInstance().currentScreen instanceof SnakeCommand.SnakeGameScreen snakeScreen)) return; snakeScreen.getOtherSnakes().remove(packet.player()); } + + @Override + public void onSnakeSyncAppleC2CPacket(SnakeSyncAppleC2CPacket packet) { + if (!(MinecraftClient.getInstance().currentScreen instanceof SnakeCommand.SnakeGameScreen snakeScreen)) return; + snakeScreen.setApple(packet.applePos()); + } } diff --git a/src/main/java/net/earthcomputer/clientcommands/c2c/CCPacketHandler.java b/src/main/java/net/earthcomputer/clientcommands/c2c/CCPacketHandler.java index 308101a21..5842330e8 100644 --- a/src/main/java/net/earthcomputer/clientcommands/c2c/CCPacketHandler.java +++ b/src/main/java/net/earthcomputer/clientcommands/c2c/CCPacketHandler.java @@ -22,6 +22,7 @@ public class CCPacketHandler { CCPacketHandler.register(SnakeAddPlayersC2CPacket.class, SnakeAddPlayersC2CPacket::new); CCPacketHandler.register(SnakeBodyC2CPacket.class, SnakeBodyC2CPacket::new); CCPacketHandler.register(SnakeRemovePlayerC2CPacket.class, SnakeRemovePlayerC2CPacket::new); + CCPacketHandler.register(SnakeSyncAppleC2CPacket.class, SnakeSyncAppleC2CPacket::new); } public static

void register(Class

packet, Function packetFactory) { diff --git a/src/main/java/net/earthcomputer/clientcommands/c2c/CCPacketListener.java b/src/main/java/net/earthcomputer/clientcommands/c2c/CCPacketListener.java index 9d70e481d..4d52f6b5f 100644 --- a/src/main/java/net/earthcomputer/clientcommands/c2c/CCPacketListener.java +++ b/src/main/java/net/earthcomputer/clientcommands/c2c/CCPacketListener.java @@ -14,4 +14,6 @@ public interface CCPacketListener { void onSnakeBodyC2CPacket(SnakeBodyC2CPacket packet); void onSnakeRemovePlayerC2CPacket(SnakeRemovePlayerC2CPacket packet); + + void onSnakeSyncAppleC2CPacket(SnakeSyncAppleC2CPacket packet); } diff --git a/src/main/java/net/earthcomputer/clientcommands/c2c/packets/SnakeSyncAppleC2CPacket.java b/src/main/java/net/earthcomputer/clientcommands/c2c/packets/SnakeSyncAppleC2CPacket.java new file mode 100644 index 000000000..1f66587a5 --- /dev/null +++ b/src/main/java/net/earthcomputer/clientcommands/c2c/packets/SnakeSyncAppleC2CPacket.java @@ -0,0 +1,22 @@ +package net.earthcomputer.clientcommands.c2c.packets; + +import net.earthcomputer.clientcommands.c2c.C2CPacket; +import net.earthcomputer.clientcommands.c2c.CCPacketListener; +import net.earthcomputer.clientcommands.c2c.StringBuf; +import net.earthcomputer.clientcommands.command.SnakeCommand; + +public record SnakeSyncAppleC2CPacket(SnakeCommand.Vec2i applePos) implements C2CPacket { + public SnakeSyncAppleC2CPacket(StringBuf buf) { + this(new SnakeCommand.Vec2i(buf)); + } + + @Override + public void write(StringBuf buf) { + SnakeCommand.Vec2i.write(buf, applePos); + } + + @Override + public void apply(CCPacketListener listener) { + listener.onSnakeSyncAppleC2CPacket(this); + } +} diff --git a/src/main/java/net/earthcomputer/clientcommands/command/SnakeCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/SnakeCommand.java index 6bb19ec72..83d4b5b59 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/SnakeCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/SnakeCommand.java @@ -7,12 +7,10 @@ import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; import com.mojang.logging.LogUtils; +import net.earthcomputer.clientcommands.c2c.C2CPacket; import net.earthcomputer.clientcommands.c2c.CCNetworkHandler; import net.earthcomputer.clientcommands.c2c.StringBuf; -import net.earthcomputer.clientcommands.c2c.packets.SnakeBodyC2CPacket; -import net.earthcomputer.clientcommands.c2c.packets.SnakeInviteC2CPacket; -import net.earthcomputer.clientcommands.c2c.packets.SnakeJoinC2CPacket; -import net.earthcomputer.clientcommands.c2c.packets.SnakeRemovePlayerC2CPacket; +import net.earthcomputer.clientcommands.c2c.packets.*; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.DrawableHelper; @@ -193,6 +191,7 @@ public void renderBackground(MatrixStack matrices) { int scaleX = 17; int scaleZ = 17; for (final Map.Entry> otherSnake : otherSnakes.entrySet()) { + if (otherSnake.getKey().equals(client.getSession().getUsername())) continue; for (final Vec2i vec : otherSnake.getValue()) { DrawableHelper.fill(matrices, startX + vec.x() * scaleX, startY + vec.z() * scaleZ, startX + vec.x() * scaleX + scaleX, startY + vec.z() * scaleZ + scaleZ, 0xffffa500); } @@ -248,18 +247,7 @@ public void close() { super.close(); if (otherSnakes.isEmpty()) return; assert client.getNetworkHandler() != null; - final SnakeRemovePlayerC2CPacket packet = new SnakeRemovePlayerC2CPacket( - client.getNetworkHandler().getProfile().getName() - ); - for (final String otherSnake : otherSnakes.keySet()) { - CCNetworkHandler.getPlayerByName(otherSnake).ifPresent(player -> { - try { - CCNetworkHandler.getInstance().sendPacket(packet, player); - } catch (CommandSyntaxException e) { - LOGGER.warn("Failed to send removal packet to " + otherSnake, e); - } - }); - } + broadcastPacket(new SnakeRemovePlayerC2CPacket(client.getNetworkHandler().getProfile().getName())); } private boolean checkGameOver() { @@ -282,24 +270,34 @@ private void checkApple() { client.getSoundManager().play(PositionedSoundInstance.master(SoundEvents.ENTITY_GENERIC_EAT, 1)); do { this.apple = new Vec2i(random.nextInt(MAX_X + 1), random.nextInt(MAX_Z + 1)); - } while (this.snake.contains(this.apple)); + } while (anyContainsPos(this.apple)); + broadcastPacket(new SnakeSyncAppleC2CPacket(apple)); } else { this.snake.removeLast(); } } + private boolean anyContainsPos(Vec2i pos) { + if (snake.contains(pos)) return true; + for (final List otherSnake : otherSnakes.values()) { + if (otherSnake.contains(pos)) return true; + } + return false; + } + private void syncPlayerData() { if (otherSnakes.isEmpty()) return; assert client.getNetworkHandler() != null; - final SnakeBodyC2CPacket packet = new SnakeBodyC2CPacket( - client.getNetworkHandler().getProfile().getName(), snake - ); + broadcastPacket(new SnakeBodyC2CPacket(client.getNetworkHandler().getProfile().getName(), snake)); + } + + private void broadcastPacket(C2CPacket packet) { for (final String otherSnake : otherSnakes.keySet()) { CCNetworkHandler.getPlayerByName(otherSnake).ifPresent(player -> { try { CCNetworkHandler.getInstance().sendPacket(packet, player); } catch (CommandSyntaxException e) { - LOGGER.warn("Failed to sync snake data to " + otherSnake, e); + LOGGER.warn("Failed to broadcast packet to " + otherSnake, e); } }); } @@ -324,6 +322,14 @@ private boolean enqueueMove(Direction direction) { public Map> getOtherSnakes() { return otherSnakes; } + + public Vec2i getApple() { + return apple; + } + + public void setApple(Vec2i apple) { + this.apple = apple; + } } public record Vec2i(int x, int z) { From 68d0cd4ea8e390f2b68bbd6091a0174ed9a30b07 Mon Sep 17 00:00:00 2001 From: "Josiah (Gaming32) Glosson" Date: Thu, 22 Dec 2022 14:10:44 -0600 Subject: [PATCH 6/6] Update rendering --- .../clientcommands/command/SnakeCommand.java | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/main/java/net/earthcomputer/clientcommands/command/SnakeCommand.java b/src/main/java/net/earthcomputer/clientcommands/command/SnakeCommand.java index 83d4b5b59..4f5067f1c 100644 --- a/src/main/java/net/earthcomputer/clientcommands/command/SnakeCommand.java +++ b/src/main/java/net/earthcomputer/clientcommands/command/SnakeCommand.java @@ -195,6 +195,13 @@ public void renderBackground(MatrixStack matrices) { for (final Vec2i vec : otherSnake.getValue()) { DrawableHelper.fill(matrices, startX + vec.x() * scaleX, startY + vec.z() * scaleZ, startX + vec.x() * scaleX + scaleX, startY + vec.z() * scaleZ + scaleZ, 0xffffa500); } + } + DrawableHelper.fill(matrices, startX + this.apple.x() * scaleX, startY + this.apple.z() * scaleZ, startX + this.apple.x() * scaleX + scaleX, startY + this.apple.z() * scaleZ + scaleZ, 0xff_f52559); + for (Vec2i vec : this.snake) { + DrawableHelper.fill(matrices, startX + vec.x() * scaleX, startY + vec.z() * scaleZ, startX + vec.x() * scaleX + scaleX, startY + vec.z() * scaleZ + scaleZ, 0xff_1f2df6); + } + for (final Map.Entry> otherSnake : otherSnakes.entrySet()) { + if (otherSnake.getKey().equals(client.getSession().getUsername())) continue; if (!otherSnake.getValue().isEmpty()) { final Vec2i head = otherSnake.getValue().get(0); DrawableHelper.drawCenteredText( @@ -206,10 +213,6 @@ public void renderBackground(MatrixStack matrices) { ); } } - DrawableHelper.fill(matrices, startX + this.apple.x() * scaleX, startY + this.apple.z() * scaleZ, startX + this.apple.x() * scaleX + scaleX, startY + this.apple.z() * scaleZ + scaleZ, 0xff_f52559); - for (Vec2i vec : this.snake) { - DrawableHelper.fill(matrices, startX + vec.x() * scaleX, startY + vec.z() * scaleZ, startX + vec.x() * scaleX + scaleX, startY + vec.z() * scaleZ + scaleZ, 0xff_1f2df6); - } } @Override