From b18733e0539152e3e51dd4439264f5a1b9863a0f Mon Sep 17 00:00:00 2001 From: alpha Date: Sat, 16 Aug 2025 00:30:55 -0500 Subject: [PATCH 1/7] Initial Payload Networking API --- .../api/item/StationComponentItemStack.java | 4 + .../station-item-components-v0.mixins.json | 18 ++ station-networking-v0/build.gradle.kts | 7 +- .../api/network/ConnectionHelper.java | 16 ++ .../stationapi/api/network/NetworkPlugin.java | 15 ++ .../stationapi/api/network/PacketByteBuf.java | 182 ++++++++++++++++++ .../api/network/StationConnection.java | 26 +++ .../api/network/StationMinecraftServer.java | 9 + .../StationServerConnectionListener.java | 110 +++++++++++ .../stationapi/api/network/VarInt.java | 83 ++++++++ .../api/network/codec/StreamCodec.java | 10 + .../api/network/codec/StreamCodecs.java | 18 ++ .../api/network/codec/StreamDecoder.java | 10 + .../api/network/codec/StreamEncoder.java | 10 + .../api/network/nio/NioNetworkPlugin.java | 32 +++ .../api/network/packet/PacketHelper.java | 5 + .../api/network/packet/PayloadPacket.java | 5 + .../api/network/packet/PayloadType.java | 19 ++ .../StationServerLoginNetworkHandler.java | 16 ++ .../mixin/network/ConnectionHelperMixin.java | 8 + .../mixin/network/HandshakePacketMixin.java | 14 ++ .../mixin/network/MinecraftServerMixin.java | 36 ++++ .../ServerConnectionListenerMixin.java | 19 ++ .../ServerLoginNetworkHandlerMixin.java | 38 ++++ .../src/main/resources/fabric.mod.json | 8 +- .../station-networking-v0.accesswidener | 6 + .../station-networking-v0.mixins.json | 9 +- 27 files changed, 730 insertions(+), 3 deletions(-) create mode 100644 station-item-components-v0/src/main/java/net/modificationstation/stationapi/api/item/StationComponentItemStack.java create mode 100644 station-item-components-v0/src/main/resources/station-item-components-v0.mixins.json create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/ConnectionHelper.java create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/NetworkPlugin.java create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/PacketByteBuf.java create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationConnection.java create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationMinecraftServer.java create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationServerConnectionListener.java create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/VarInt.java create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/codec/StreamCodec.java create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/codec/StreamCodecs.java create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/codec/StreamDecoder.java create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/codec/StreamEncoder.java create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/nio/NioNetworkPlugin.java create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/packet/PayloadPacket.java create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/packet/PayloadType.java create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/impl/network/server/StationServerLoginNetworkHandler.java create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/ConnectionHelperMixin.java create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/HandshakePacketMixin.java create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/MinecraftServerMixin.java create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/ServerConnectionListenerMixin.java create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/ServerLoginNetworkHandlerMixin.java create mode 100644 station-networking-v0/src/main/resources/station-networking-v0.accesswidener diff --git a/station-item-components-v0/src/main/java/net/modificationstation/stationapi/api/item/StationComponentItemStack.java b/station-item-components-v0/src/main/java/net/modificationstation/stationapi/api/item/StationComponentItemStack.java new file mode 100644 index 000000000..9192a68fa --- /dev/null +++ b/station-item-components-v0/src/main/java/net/modificationstation/stationapi/api/item/StationComponentItemStack.java @@ -0,0 +1,4 @@ +package net.modificationstation.stationapi.api.item; + +public interface StationComponentItemStack { +} diff --git a/station-item-components-v0/src/main/resources/station-item-components-v0.mixins.json b/station-item-components-v0/src/main/resources/station-item-components-v0.mixins.json new file mode 100644 index 000000000..952c213c3 --- /dev/null +++ b/station-item-components-v0/src/main/resources/station-item-components-v0.mixins.json @@ -0,0 +1,18 @@ +{ + "required": true, + "minVersion": "0.8", + "package": "net.modificationstation.stationapi.mixin.item", + "compatibilityLevel": "JAVA_17", + "mixins": [ + + ], + "server": [ + + ], + "client": [ + + ], + "injectors": { + "defaultRequire": 1 + } +} diff --git a/station-networking-v0/build.gradle.kts b/station-networking-v0/build.gradle.kts index 0d08afbcc..23e2a3f20 100644 --- a/station-networking-v0/build.gradle.kts +++ b/station-networking-v0/build.gradle.kts @@ -5,7 +5,12 @@ version = getSubprojectVersion(project, "1.0.0") addModuleDependencies(project, "station-api-base", + "station-maths-v0", "station-registry-api-v0", "station-player-api-v0", "station-lifecycle-events-v0" -) \ No newline at end of file +) + +loom { + accessWidenerPath = file("src/main/resources/station-networking-v0.accesswidener") +} \ No newline at end of file diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/ConnectionHelper.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/ConnectionHelper.java new file mode 100644 index 000000000..26fa7b09a --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/ConnectionHelper.java @@ -0,0 +1,16 @@ +package net.modificationstation.stationapi.api.network; + +import net.minecraft.network.Connection; +import net.minecraft.network.NetworkHandler; +import org.jetbrains.annotations.NotNull; + +import java.nio.channels.SocketChannel; + +public final class ConnectionHelper { + private ConnectionHelper() {} + + @NotNull + public static Connection createConnection(SocketChannel socket, String name, NetworkHandler listener) { + return null; // Replaced by asm + } +} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/NetworkPlugin.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/NetworkPlugin.java new file mode 100644 index 000000000..364fc1a92 --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/NetworkPlugin.java @@ -0,0 +1,15 @@ +package net.modificationstation.stationapi.api.network; + +import org.jetbrains.annotations.Nullable; + +import java.io.IOException; +import java.net.ProtocolFamily; +import java.net.SocketAddress; +import java.nio.channels.NetworkChannel; + +public interface NetworkPlugin { + + NetworkChannel openServer(SocketAddress address, @Nullable ProtocolFamily family) throws IOException; + + NetworkChannel open() throws IOException; +} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/PacketByteBuf.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/PacketByteBuf.java new file mode 100644 index 000000000..3407b4ac3 --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/PacketByteBuf.java @@ -0,0 +1,182 @@ +package net.modificationstation.stationapi.api.network; + +import net.minecraft.util.math.BlockPos; +import net.modificationstation.stationapi.api.util.math.StationBlockPos; + +import java.io.*; +import java.nio.ByteBuffer; +import java.nio.charset.StandardCharsets; + +public class PacketByteBuf { + public static final short MAX_STRING_LENGTH = 32767; + + protected final ByteBuffer source; + + public PacketByteBuf(ByteBuffer source) { + this.source = source; + } + + public DataInputStream getInputStream() { + return new DataInputStream(new InputStream() { + @Override + public int read() { + if (!source.hasRemaining()) { + return -1; + } + return source.get() & 0xFF; + } + + @Override + public int read(byte[] bytes, int off, int len) { + if (!source.hasRemaining()) { + return -1; + } + + len = Math.min(len, source.remaining()); + source.get(bytes, off, len); + return len; + } + }); + } + + public DataOutputStream getOutputStream() { + return new DataOutputStream(new OutputStream() { + @Override + public void write(int b) { + source.put((byte) b); + } + + @Override + public void write(byte[] bytes, int off, int len) { + source.put(bytes, off, len); + } + }); + } + + public ByteBuffer getSource() { + return source; + } + + public void writeBoolean(boolean value) { + source.put((byte) (value ? 1 : 0)); + } + + public boolean readBoolean() { + return source.get() != 0; + } + + public void writeChar(int value) { + source.putChar((char) value); + } + + public char readChar() { + return source.getChar(); + } + + public void writeByte(int value) { + source.put((byte) value); + } + + public byte readByte() { + return source.get(); + } + + public void writeShort(int value) { + source.putShort((short) value); + } + + public short readShort() { + return source.getShort(); + } + + public void writeInt(int value) { + source.putInt(value); + } + + public int readInt() { + return source.getInt(); + } + + public void writeLong(long value) { + source.putLong(value); + } + + public long readLong() { + return source.getLong(); + } + + public void writeFloat(float value) { + source.putFloat(value); + } + public float readFloat() { + return source.getFloat(); + } + + public void writeDouble(double value) { + source.putDouble(value); + } + + public double readDouble() { + return source.getDouble(); + } + + public void writeVarInt(int value) { + VarInt.writeVarInt(value, source); + } + public int readVarInt() { + return VarInt.readVarInt(source); + } + + public void writeVarLong(long value) { + VarInt.writeVarLong(value, source); + } + + public long readVarLong() { + return VarInt.readVarLong(source); + } + + public > T readEnum(Class enumClass) { + return enumClass.getEnumConstants()[readVarInt()]; + } + + public void writeEnum(Enum instance) { + writeVarInt(instance.ordinal()); + } + + public void writeString(String string) { + writeString(string, MAX_STRING_LENGTH); + } + + public String readString() { + return readString(MAX_STRING_LENGTH); + } + + public void writeString(String string, int maxLength) { + if (string.length() > maxLength) { + throw new RuntimeException("String too big"); + } else { + writeVarInt(string.length()); + source.put(string.getBytes(StandardCharsets.UTF_8)); + } + } + + public String readString(int maxLength) { + int size = readVarInt(); + if (size > maxLength) { + throw new RuntimeException("Received string length longer than maximum allowed (" + size + " > " + maxLength + ")"); + } else if (size < 0) { + throw new RuntimeException("Received string length is less than zero! Weird string!"); + } else { + // TODO: I don't know how this would work for different buffer backends; make a better solution later + return new String(source.slice(source.position(), size).array(), StandardCharsets.UTF_8); + } + } + + public void writeBlockPos(BlockPos pos) { + writeLong(pos.asLong()); + } + + public BlockPos readBlockPos() { + return StationBlockPos.fromLong(readLong()); + } +} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationConnection.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationConnection.java new file mode 100644 index 000000000..1fb1250a5 --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationConnection.java @@ -0,0 +1,26 @@ +package net.modificationstation.stationapi.api.network; + +import net.minecraft.network.Connection; +import net.minecraft.network.NetworkHandler; +import net.modificationstation.stationapi.api.network.packet.PayloadType; + +import java.nio.channels.SocketChannel; + +public class StationConnection extends Connection { + public StationConnection(SocketChannel socket, String string, NetworkHandler networkHandler) { + super(socket.socket(), string, networkHandler); + } + + public

void sendPayload(PayloadType type, P payload) { + + } + + @Override + public void method_1122() { + } + + @Override + protected boolean method_1137() { // Write Tick + return super.method_1137(); + } +} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationMinecraftServer.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationMinecraftServer.java new file mode 100644 index 000000000..fa2d5fe67 --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationMinecraftServer.java @@ -0,0 +1,9 @@ +package net.modificationstation.stationapi.api.network; + +import net.modificationstation.stationapi.api.util.Util; + +public interface StationMinecraftServer { + default StationServerConnectionListener getStationConnectionListener() { + return Util.assertImpl(); + } +} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationServerConnectionListener.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationServerConnectionListener.java new file mode 100644 index 000000000..5e2d6b54c --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationServerConnectionListener.java @@ -0,0 +1,110 @@ +package net.modificationstation.stationapi.api.network; + +import net.minecraft.class_9; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.network.ServerLoginNetworkHandler; +import net.minecraft.server.network.ServerPlayNetworkHandler; +import net.modificationstation.stationapi.api.network.nio.NioNetworkPlugin; +import net.modificationstation.stationapi.impl.network.server.StationServerLoginNetworkHandler; + +import java.io.IOException; +import java.net.*; +import java.nio.channels.ServerSocketChannel; +import java.nio.channels.SocketChannel; +import java.util.ArrayList; +import java.util.List; +import java.util.logging.Level; + +/** + * Replaces {@link class_9} in accepting network connections + */ +public class StationServerConnectionListener { + private ServerSocketChannel socketChannel; + private final MinecraftServer server; + private final Thread listenerThread; + private volatile boolean listening; + private int connectionCounter = 0; + private final List connections; + private final List pendingConnections; + + public StationServerConnectionListener(MinecraftServer server, InetAddress address, int port) throws IOException { + this.server = server; + ProtocolFamily family; + // TODO: Unix addresses? + if (address instanceof Inet4Address) + family = StandardProtocolFamily.INET; + else if (address instanceof Inet6Address) + family = StandardProtocolFamily.INET6; + else if (address == null) + family = null; + else + throw new RuntimeException("Unsupported address family: " + address.getHostAddress()); + int maxPlayers = server.field_2840.method_1246("max-players", 20); + this.pendingConnections = new ArrayList<>(maxPlayers); + this.connections = new ArrayList<>(maxPlayers); + this.socketChannel = NioNetworkPlugin.INSTANCE.openServer(new InetSocketAddress(address, port), family); + this.listening = true; + this.listenerThread = new Thread(this::listen, "Station-Listener-Thread"); + this.listenerThread.start(); + } + + public void listen() { + while (listening) { + try { + SocketChannel socket = this.socketChannel.accept(); + + addPendingConnection(new StationServerLoginNetworkHandler(this.server, socket, "Connection #" + this.connectionCounter++)); + } catch (IOException e) { + e.printStackTrace(); + } + } + } + + public void addConnection(ServerPlayNetworkHandler listener) { + this.connections.add(listener); + } + + private void addPendingConnection(StationServerLoginNetworkHandler listener) { + if (listener == null) { + throw new IllegalArgumentException("Got null pendingconnection!"); + } else { + this.pendingConnections.add(listener); + } + } + + public void tick() { + for (int i = 0; i < this.pendingConnections.size(); i++) { + ServerLoginNetworkHandler listener = this.pendingConnections.get(i); + + try { + listener.tick(); + } catch (Exception var5) { + listener.disconnect("Internal server error"); + class_9.LOGGER.log(Level.WARNING, "Failed to handle packet: " + var5, var5); + } + + if (listener.closed) { + this.pendingConnections.remove(i--); + } + + listener.connection.method_1122(); // interrupt + } + + for (int i = 0; i < this.connections.size(); i++) { + ServerPlayNetworkHandler listener = this.connections.get(i); + + try { + listener.method_831(); // tick + } catch (Exception e) { + class_9.LOGGER.log(Level.WARNING, "Failed to handle packet: " + e, e); + listener.method_833("Internal server error"); // disconnect + } + + if (listener.field_918) { // stopped + this.connections.remove(i--); + } + + listener.field_917.method_1122(); // interrupt + } + } +} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/VarInt.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/VarInt.java new file mode 100644 index 000000000..ce64ab99a --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/VarInt.java @@ -0,0 +1,83 @@ +package net.modificationstation.stationapi.api.network; + +import java.nio.ByteBuffer; + +public class VarInt { + private static final int MAX_VARINT_SIZE = 5; + private static final int MAX_VARLONG_SIZE = 10; + private static final int DATA_BITS_MASK = 127; + private static final int CONTINUATION_BIT_MASK = 128; + private static final int DATA_BITS_PER_BYTE = 7; + + public static boolean hasContinuationBit(byte part) { + return (part & CONTINUATION_BIT_MASK) == CONTINUATION_BIT_MASK; + } + + public static int getVarIntSize(int value) { + int result = 0; + do { + result++; + value >>>= DATA_BITS_PER_BYTE; + } while (value != 0); + return result; + } + + public static void writeVarInt(int value, ByteBuffer buf) { + while ((value & -CONTINUATION_BIT_MASK) != 0) { + buf.put((byte) (value & DATA_BITS_MASK | CONTINUATION_BIT_MASK)); + value >>>= DATA_BITS_PER_BYTE; + } + + buf.put((byte) value); + } + + public static int readVarInt(ByteBuffer buf) { + int result = 0; + int offset = 0; + + byte bit; + do { + bit = buf.get(); + result |= (bit & DATA_BITS_MASK) << offset++ * DATA_BITS_PER_BYTE; + if (offset > MAX_VARINT_SIZE) { + throw new RuntimeException("VarInt too big"); + } + } while (hasContinuationBit(bit)); + + return result; + } + + public static int getVarLongSize(long value) { + int result = 0; + do { + result++; + value >>>= DATA_BITS_PER_BYTE; + } while (value != 0); + return result; + } + + public static void writeVarLong(long value, ByteBuffer buf) { + while ((value & -CONTINUATION_BIT_MASK) != 0L) { + buf.put((byte) ((value & DATA_BITS_MASK) | CONTINUATION_BIT_MASK)); + value >>>= DATA_BITS_PER_BYTE; + } + + buf.put((byte) value); + } + + public static long readVarLong(ByteBuffer buf) { + long result = 0L; + int offset = 0; + + byte bit; + do { + bit = buf.get(); + result |= (long) (bit & DATA_BITS_MASK) << offset++ * DATA_BITS_PER_BYTE; + if (offset > MAX_VARLONG_SIZE) { + throw new RuntimeException("VarLong too big"); + } + } while (hasContinuationBit(bit)); + + return result; + } +} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/codec/StreamCodec.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/codec/StreamCodec.java new file mode 100644 index 000000000..641016e81 --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/codec/StreamCodec.java @@ -0,0 +1,10 @@ +package net.modificationstation.stationapi.api.network.codec; + +/** + * Similar to a {@link com.mojang.serialization.Codec}, but all values are encoded into a "stream" with no assigning identifier attached to them. + * A stream codec represents a stream of memory. + * @param + * @param + */ +public interface StreamCodec extends StreamDecoder, StreamEncoder { +} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/codec/StreamCodecs.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/codec/StreamCodecs.java new file mode 100644 index 000000000..562da50ff --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/codec/StreamCodecs.java @@ -0,0 +1,18 @@ +package net.modificationstation.stationapi.api.network.codec; + +import net.modificationstation.stationapi.api.network.PacketByteBuf; + +/** + * Provides {@link StreamCodec} for common types. + */ +public interface StreamCodecs { + StreamCodec BOOL = new StreamCodec<>() { + public Boolean decode(PacketByteBuf buf) { + return buf.readBoolean(); + } + + public void encode(PacketByteBuf buf, Boolean bool) { + buf.writeBoolean(bool); + } + }; +} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/codec/StreamDecoder.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/codec/StreamDecoder.java new file mode 100644 index 000000000..8b9a07ce3 --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/codec/StreamDecoder.java @@ -0,0 +1,10 @@ +package net.modificationstation.stationapi.api.network.codec; + +/** + * Represents a generic decoder. + * @param The deserializer that decodes the object. + * @param The object that will get deserialized. + */ +public interface StreamDecoder { + T decode(I decoder); +} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/codec/StreamEncoder.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/codec/StreamEncoder.java new file mode 100644 index 000000000..4d66c56fc --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/codec/StreamEncoder.java @@ -0,0 +1,10 @@ +package net.modificationstation.stationapi.api.network.codec; + +/** + * Represents a generic encoder. + * @param The serializer use to serialize the object. + * @param The object to serialize. + */ +public interface StreamEncoder { + void encode(S encoder, T obj); +} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/nio/NioNetworkPlugin.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/nio/NioNetworkPlugin.java new file mode 100644 index 000000000..ee8daa6b0 --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/nio/NioNetworkPlugin.java @@ -0,0 +1,32 @@ +package net.modificationstation.stationapi.api.network.nio; + +import net.modificationstation.stationapi.api.network.NetworkPlugin; + +import java.io.IOException; +import java.net.ProtocolFamily; +import java.net.SocketAddress; +import java.nio.channels.NetworkChannel; +import java.nio.channels.ServerSocketChannel; + +/** + * Represents a TCP server + */ +public class NioNetworkPlugin implements NetworkPlugin { + public static final NioNetworkPlugin INSTANCE = new NioNetworkPlugin(); + + public NioNetworkPlugin() { + + } + + @Override + public ServerSocketChannel openServer(SocketAddress address, ProtocolFamily family) throws IOException { + ServerSocketChannel serverSocket = family == null ? ServerSocketChannel.open() : ServerSocketChannel.open(family); + serverSocket.bind(address); + return serverSocket; + } + + @Override + public NetworkChannel open() { + return null; + } +} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/packet/PacketHelper.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/packet/PacketHelper.java index 48fb37671..e7e1aec07 100644 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/packet/PacketHelper.java +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/packet/PacketHelper.java @@ -3,6 +3,7 @@ import net.minecraft.entity.Entity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.network.packet.Packet; +import net.modificationstation.stationapi.api.network.PacketByteBuf; import net.modificationstation.stationapi.api.util.API; import net.modificationstation.stationapi.api.util.Identifier; import net.modificationstation.stationapi.api.util.SideUtil; @@ -23,6 +24,10 @@ public final class PacketHelper { @SuppressWarnings("Convert2MethodRef") // Method references load their target classes on both sides, causing crashes. private static final PacketHelperImpl INSTANCE = SideUtil.get(() -> new PacketHelperClientImpl(), () -> new PacketHelperServerImpl()); + public static

Packet createPayloadPacket(PayloadType type, P payload) { + return null; + } + /** * On client, sends the packet to the server if the current game is multiplayer, or handles the packet locally if the current game is singleplayer. * On server, handles the packet locally. diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/packet/PayloadPacket.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/packet/PayloadPacket.java new file mode 100644 index 000000000..4cccde7d2 --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/packet/PayloadPacket.java @@ -0,0 +1,5 @@ +package net.modificationstation.stationapi.api.network.packet; + +public class PayloadPacket { + +} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/packet/PayloadType.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/packet/PayloadType.java new file mode 100644 index 000000000..1a4ecd533 --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/packet/PayloadType.java @@ -0,0 +1,19 @@ +package net.modificationstation.stationapi.api.network.packet; + +import net.modificationstation.stationapi.api.network.PacketByteBuf; +import net.modificationstation.stationapi.api.network.codec.StreamCodec; +import net.modificationstation.stationapi.api.util.Identifier; + +/** + * Represents a payload type sent over the network. + * @param id The identifier of the payload. + * @param codec Represents how your payload should be encoded/decoded over the network. + * @param delayed An option to be handled last after all other packets, this is equivalent to {@link net.minecraft.network.packet.Packet#worldPacket}. + * @param The context buffer used to serialize and deserialize the payload. + * @param The actual payload that will be sent over the network. + */ +public record PayloadType(Identifier id, StreamCodec codec, boolean delayed) { + public PayloadType(Identifier id, StreamCodec codec) { + this(id, codec, false); + } +} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/impl/network/server/StationServerLoginNetworkHandler.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/impl/network/server/StationServerLoginNetworkHandler.java new file mode 100644 index 000000000..843512111 --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/impl/network/server/StationServerLoginNetworkHandler.java @@ -0,0 +1,16 @@ +package net.modificationstation.stationapi.impl.network.server; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.network.ServerLoginNetworkHandler; +import net.modificationstation.stationapi.api.network.StationConnection; + +import java.net.Socket; +import java.nio.channels.SocketChannel; + +public class StationServerLoginNetworkHandler extends ServerLoginNetworkHandler { + public StationServerLoginNetworkHandler(MinecraftServer server, SocketChannel socket, String string) { + super(server, null, string); // We patch vanilla's class to allow passing null here + this.connection = new StationConnection(socket, string, this); + this.connection.field_1279 = 0; + } +} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/ConnectionHelperMixin.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/ConnectionHelperMixin.java new file mode 100644 index 000000000..5f6ca3d95 --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/ConnectionHelperMixin.java @@ -0,0 +1,8 @@ +package net.modificationstation.stationapi.mixin.network; + +import net.modificationstation.stationapi.api.network.ConnectionHelper; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(ConnectionHelper.class) +public class ConnectionHelperMixin { +} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/HandshakePacketMixin.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/HandshakePacketMixin.java new file mode 100644 index 000000000..4bc4d3bbd --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/HandshakePacketMixin.java @@ -0,0 +1,14 @@ +package net.modificationstation.stationapi.mixin.network; + +import net.minecraft.network.packet.handshake.HandshakePacket; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyArg; + +@Mixin(HandshakePacket.class) +public class HandshakePacketMixin { + @ModifyArg(method = "read", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/packet/handshake/HandshakePacket;readString(Ljava/io/DataInputStream;I)Ljava/lang/String;"), index = 1) + private int modifyMaxStringForGlassNetworking(int par2) { + return Short.MAX_VALUE; + } +} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/MinecraftServerMixin.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/MinecraftServerMixin.java new file mode 100644 index 000000000..a482e5fc6 --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/MinecraftServerMixin.java @@ -0,0 +1,36 @@ +package net.modificationstation.stationapi.mixin.network; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Local; +import net.minecraft.class_9; +import net.minecraft.server.MinecraftServer; +import net.modificationstation.stationapi.api.network.StationMinecraftServer; +import net.modificationstation.stationapi.api.network.StationServerConnectionListener; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +import java.io.IOException; +import java.net.InetAddress; + +@Mixin(MinecraftServer.class) +public class MinecraftServerMixin implements StationMinecraftServer { + + private StationServerConnectionListener stationapi_listener; + + @WrapOperation(method = "method_2166", at = @At(value = "NEW", target = "(Lnet/minecraft/server/MinecraftServer;Ljava/net/InetAddress;I)Lnet/minecraft/class_9;")) + private class_9 createStationConnectionListener(MinecraftServer inetAddress, InetAddress address, int port, Operation original) throws IOException { + this.stationapi_listener = new StationServerConnectionListener((MinecraftServer) (Object) this, address, port); + return null; + } + + @WrapOperation(method = "method_2171", at = @At(value = "INVOKE", target = "Lnet/minecraft/class_9;method_34()V")) + private void tickListener(class_9 instance, Operation original) { + this.stationapi_listener.tick(); + } + + @Override + public StationServerConnectionListener getStationConnectionListener() { + return stationapi_listener; + } +} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/ServerConnectionListenerMixin.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/ServerConnectionListenerMixin.java new file mode 100644 index 000000000..d46e60f2c --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/ServerConnectionListenerMixin.java @@ -0,0 +1,19 @@ +package net.modificationstation.stationapi.mixin.network; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import net.minecraft.class_9; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.ServerSocket; + +@Mixin(class_9.class) +public class ServerConnectionListenerMixin { + @WrapOperation(method = "", at = @At(value = "NEW", target = "(IILjava/net/InetAddress;)Ljava/net/ServerSocket;")) + private ServerSocket createServerSocket(int port, int backlog, InetAddress address, Operation original) throws IOException { + return null; + } +} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/ServerLoginNetworkHandlerMixin.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/ServerLoginNetworkHandlerMixin.java new file mode 100644 index 000000000..eb5fb96b4 --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/ServerLoginNetworkHandlerMixin.java @@ -0,0 +1,38 @@ +package net.modificationstation.stationapi.mixin.network; + +import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import net.minecraft.class_9; +import net.minecraft.network.Connection; +import net.minecraft.network.NetworkHandler; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.network.ServerLoginNetworkHandler; +import net.minecraft.server.network.ServerPlayNetworkHandler; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; + +import java.net.Socket; + +@Mixin(ServerLoginNetworkHandler.class) +public class ServerLoginNetworkHandlerMixin { + @Shadow private MinecraftServer server; + + @WrapOperation(method = "", at = @At(value = "NEW", target = "(Ljava/net/Socket;Ljava/lang/String;Lnet/minecraft/network/NetworkHandler;)Lnet/minecraft/network/Connection;")) + private Connection replaceVanillaConnection(Socket socket, String name, NetworkHandler networkHandler, Operation original) { + if (socket == null) + return null; + return original.call(socket, name, networkHandler); + } + + @WrapWithCondition(method = "", at = @At(value = "FIELD", target = "Lnet/minecraft/network/Connection;field_1279:I")) + private boolean checkIfConnectionIsNull(Connection instance, int value) { + return instance != null; + } + + @WrapOperation(method = "accept", at = @At(value = "INVOKE", target = "Lnet/minecraft/class_9;method_38(Lnet/minecraft/server/network/ServerPlayNetworkHandler;)V")) + private void useStationApiListener(class_9 instance, ServerPlayNetworkHandler listener, Operation original) { + this.server.getStationConnectionListener().addConnection(listener); + } +} diff --git a/station-networking-v0/src/main/resources/fabric.mod.json b/station-networking-v0/src/main/resources/fabric.mod.json index 151fd3951..c61cf0a67 100644 --- a/station-networking-v0/src/main/resources/fabric.mod.json +++ b/station-networking-v0/src/main/resources/fabric.mod.json @@ -29,6 +29,7 @@ "mixins": [ "station-networking-v0.mixins.json" ], + "accessWidener" : "station-networking-v0.accesswidener", "depends": { "fabricloader": "*", @@ -36,6 +37,11 @@ }, "custom": { - "modmenu:api": true + "modmenu:api": true, + "loom:injected_interfaces": { + "net/minecraft/server/MinecraftServer": [ + "net/modificationstation/stationapi/api/network/StationMinecraftServer" + ] + } } } diff --git a/station-networking-v0/src/main/resources/station-networking-v0.accesswidener b/station-networking-v0/src/main/resources/station-networking-v0.accesswidener new file mode 100644 index 000000000..52d8a2c04 --- /dev/null +++ b/station-networking-v0/src/main/resources/station-networking-v0.accesswidener @@ -0,0 +1,6 @@ +accessWidener v2 named + +transitive-extendable method net/minecraft/network/Connection method_1137 ()Z #writeTick +transitive-extendable method net/minecraft/network/Connection method_1139 ()Z #readTick + +accessible field net/minecraft/class_166 field_581 I \ No newline at end of file diff --git a/station-networking-v0/src/main/resources/station-networking-v0.mixins.json b/station-networking-v0/src/main/resources/station-networking-v0.mixins.json index 8dee1849b..56247c5ea 100644 --- a/station-networking-v0/src/main/resources/station-networking-v0.mixins.json +++ b/station-networking-v0/src/main/resources/station-networking-v0.mixins.json @@ -5,10 +5,17 @@ "compatibilityLevel": "JAVA_17", "mixins": [ "AbstractPacketAccessor", + "ConnectionHelperMixin", "ConnectionMixin", + "HandshakePacketMixin", "PacketMixin" ], "injectors": { "defaultRequire": 1 - } + }, + "server": [ + "MinecraftServerMixin", + "ServerConnectionListenerMixin", + "ServerLoginNetworkHandlerMixin" + ] } From 35c0d72586ff5037c762dc9b8b9d1e084df214ce Mon Sep 17 00:00:00 2001 From: alpha Date: Sat, 16 Aug 2025 14:40:13 -0500 Subject: [PATCH 2/7] Remove accidental component stuff --- .../api/item/StationComponentItemStack.java | 4 ---- .../station-item-components-v0.mixins.json | 18 ------------------ 2 files changed, 22 deletions(-) delete mode 100644 station-item-components-v0/src/main/java/net/modificationstation/stationapi/api/item/StationComponentItemStack.java delete mode 100644 station-item-components-v0/src/main/resources/station-item-components-v0.mixins.json diff --git a/station-item-components-v0/src/main/java/net/modificationstation/stationapi/api/item/StationComponentItemStack.java b/station-item-components-v0/src/main/java/net/modificationstation/stationapi/api/item/StationComponentItemStack.java deleted file mode 100644 index 9192a68fa..000000000 --- a/station-item-components-v0/src/main/java/net/modificationstation/stationapi/api/item/StationComponentItemStack.java +++ /dev/null @@ -1,4 +0,0 @@ -package net.modificationstation.stationapi.api.item; - -public interface StationComponentItemStack { -} diff --git a/station-item-components-v0/src/main/resources/station-item-components-v0.mixins.json b/station-item-components-v0/src/main/resources/station-item-components-v0.mixins.json deleted file mode 100644 index 952c213c3..000000000 --- a/station-item-components-v0/src/main/resources/station-item-components-v0.mixins.json +++ /dev/null @@ -1,18 +0,0 @@ -{ - "required": true, - "minVersion": "0.8", - "package": "net.modificationstation.stationapi.mixin.item", - "compatibilityLevel": "JAVA_17", - "mixins": [ - - ], - "server": [ - - ], - "client": [ - - ], - "injectors": { - "defaultRequire": 1 - } -} From d0ca0984a908a64783ab20d45b7c9b195483c052 Mon Sep 17 00:00:00 2001 From: alpha Date: Sat, 16 Aug 2025 15:54:12 -0500 Subject: [PATCH 3/7] More WIP things for sending payloads --- .../api/network/ConnectionHelper.java | 16 ----- .../stationapi/api/network/PacketByteBuf.java | 48 +++----------- .../api/network/StationConnection.java | 64 ++++++++++++++++++- .../StationServerConnectionListener.java | 6 +- .../api/network/packet/PayloadPacket.java | 2 +- .../api/registry/PayloadTypeRegistry.java | 19 ++++++ .../mixin/network/ConnectionHelperMixin.java | 8 --- .../station-networking-v0.accesswidener | 5 +- .../station-networking-v0.mixins.json | 1 - 9 files changed, 99 insertions(+), 70 deletions(-) delete mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/ConnectionHelper.java create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/api/registry/PayloadTypeRegistry.java delete mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/ConnectionHelperMixin.java diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/ConnectionHelper.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/ConnectionHelper.java deleted file mode 100644 index 26fa7b09a..000000000 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/ConnectionHelper.java +++ /dev/null @@ -1,16 +0,0 @@ -package net.modificationstation.stationapi.api.network; - -import net.minecraft.network.Connection; -import net.minecraft.network.NetworkHandler; -import org.jetbrains.annotations.NotNull; - -import java.nio.channels.SocketChannel; - -public final class ConnectionHelper { - private ConnectionHelper() {} - - @NotNull - public static Connection createConnection(SocketChannel socket, String name, NetworkHandler listener) { - return null; // Replaced by asm - } -} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/PacketByteBuf.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/PacketByteBuf.java index 3407b4ac3..e3e9463ba 100644 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/PacketByteBuf.java +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/PacketByteBuf.java @@ -1,9 +1,10 @@ package net.modificationstation.stationapi.api.network; import net.minecraft.util.math.BlockPos; +import net.modificationstation.stationapi.api.network.codec.StreamDecoder; +import net.modificationstation.stationapi.api.network.codec.StreamEncoder; import net.modificationstation.stationapi.api.util.math.StationBlockPos; -import java.io.*; import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; @@ -16,47 +17,18 @@ public PacketByteBuf(ByteBuffer source) { this.source = source; } - public DataInputStream getInputStream() { - return new DataInputStream(new InputStream() { - @Override - public int read() { - if (!source.hasRemaining()) { - return -1; - } - return source.get() & 0xFF; - } - - @Override - public int read(byte[] bytes, int off, int len) { - if (!source.hasRemaining()) { - return -1; - } - - len = Math.min(len, source.remaining()); - source.get(bytes, off, len); - return len; - } - }); - } - - public DataOutputStream getOutputStream() { - return new DataOutputStream(new OutputStream() { - @Override - public void write(int b) { - source.put((byte) b); - } - - @Override - public void write(byte[] bytes, int off, int len) { - source.put(bytes, off, len); - } - }); - } - public ByteBuffer getSource() { return source; } + public void write(StreamEncoder encoder, T value) { + encoder.encode(this, value); + } + + public T read(StreamDecoder decoder) { + return decoder.decode(this); + } + public void writeBoolean(boolean value) { source.put((byte) (value ? 1 : 0)); } diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationConnection.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationConnection.java index 1fb1250a5..760a58e74 100644 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationConnection.java +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationConnection.java @@ -2,17 +2,47 @@ import net.minecraft.network.Connection; import net.minecraft.network.NetworkHandler; +import net.modificationstation.stationapi.api.network.packet.PacketType; +import net.modificationstation.stationapi.api.network.packet.PayloadPacket; import net.modificationstation.stationapi.api.network.packet.PayloadType; +import net.modificationstation.stationapi.api.registry.PayloadTypeRegistry; +import java.nio.ByteBuffer; import java.nio.channels.SocketChannel; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.function.Consumer; public class StationConnection extends Connection { + protected List> incomingPayloads = Collections.synchronizedList(new ArrayList<>()); + protected List> outgoingPayloads = Collections.synchronizedList(new ArrayList<>()); + protected List> outgoingPayloadsDelayed = Collections.synchronizedList(new ArrayList<>()); + protected SocketChannel socketChannel; + protected PacketByteBuf outgoingBuffer; + public StationConnection(SocketChannel socket, String string, NetworkHandler networkHandler) { super(socket.socket(), string, networkHandler); + this.socketChannel = socket; + this.outgoingBuffer = new PacketByteBuf(ByteBuffer.allocate(5120)); } - public

void sendPayload(PayloadType type, P payload) { + protected

Consumer getPacketEncoder(PayloadType type, P payload) { + return buf -> { + buf.writeInt(PayloadPacket.PACKET_ID); + buf.writeVarInt(PayloadTypeRegistry.INSTANCE.getRawId(type)); + type.codec().encode(buf, payload); + }; + } + public

void sendPayload(PayloadType type, P payload) { + if (!this.field_1290) { // this.closed + Consumer encoder = getPacketEncoder(type, payload); + if (type.delayed()) + outgoingPayloadsDelayed.add(encoder); + else + outgoingPayloads.add(encoder); + } } @Override @@ -21,6 +51,36 @@ public void method_1122() { @Override protected boolean method_1137() { // Write Tick - return super.method_1137(); + boolean handled = super.method_1137(); + + // Vanilla only decodes one packet a tick, TODO possibly change this? + if (!this.outgoingPayloads.isEmpty()) { + Consumer type; + synchronized (this.field_1280) { + type = this.outgoingPayloads.remove(0); + } + + type.accept(this.outgoingBuffer); + handled = true; + } + + if (!this.outgoingPayloadsDelayed.isEmpty()) { + Consumer type; + synchronized (this.field_1280) { + type = this.outgoingPayloadsDelayed.remove(0); + } + + type.accept(this.outgoingBuffer); + handled = true; + } + + return handled; } + + @Override + protected boolean method_1139() { // Read Tick + return super.method_1139(); + } + + protected record TypeWithPayload

(PayloadType type, Consumer payload) {} } diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationServerConnectionListener.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationServerConnectionListener.java index 5e2d6b54c..4d30c21cf 100644 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationServerConnectionListener.java +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationServerConnectionListener.java @@ -78,9 +78,9 @@ public void tick() { try { listener.tick(); - } catch (Exception var5) { + } catch (Exception e) { listener.disconnect("Internal server error"); - class_9.LOGGER.log(Level.WARNING, "Failed to handle packet: " + var5, var5); + class_9.LOGGER.log(Level.WARNING, "Failed to handle packet: " + e, e); } if (listener.closed) { @@ -100,7 +100,7 @@ public void tick() { listener.method_833("Internal server error"); // disconnect } - if (listener.field_918) { // stopped + if (listener.field_918) { // closed this.connections.remove(i--); } diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/packet/PayloadPacket.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/packet/PayloadPacket.java index 4cccde7d2..f7fe8a5f0 100644 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/packet/PayloadPacket.java +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/packet/PayloadPacket.java @@ -1,5 +1,5 @@ package net.modificationstation.stationapi.api.network.packet; public class PayloadPacket { - + public static final int PACKET_ID = 253; } diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/registry/PayloadTypeRegistry.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/registry/PayloadTypeRegistry.java new file mode 100644 index 000000000..6e96a89b9 --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/registry/PayloadTypeRegistry.java @@ -0,0 +1,19 @@ +package net.modificationstation.stationapi.api.registry; + +import com.mojang.serialization.Lifecycle; +import net.modificationstation.stationapi.api.event.registry.RegistryAttribute; +import net.modificationstation.stationapi.api.event.registry.RegistryAttributeHolder; +import net.modificationstation.stationapi.api.network.PacketByteBuf; +import net.modificationstation.stationapi.api.network.packet.PayloadType; + +import static net.modificationstation.stationapi.api.StationAPI.NAMESPACE; + +public class PayloadTypeRegistry extends SimpleRegistry> { + public static final RegistryKey>> KEY = RegistryKey.ofRegistry(NAMESPACE.id("payload_types")); + public static final Registry> INSTANCE = Registries.create(KEY, new PayloadTypeRegistry(), Lifecycle.experimental()); + + private PayloadTypeRegistry() { + super(KEY, Lifecycle.experimental(), true); + RegistryAttributeHolder.get(this).addAttribute(RegistryAttribute.SYNCED); + } +} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/ConnectionHelperMixin.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/ConnectionHelperMixin.java deleted file mode 100644 index 5f6ca3d95..000000000 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/ConnectionHelperMixin.java +++ /dev/null @@ -1,8 +0,0 @@ -package net.modificationstation.stationapi.mixin.network; - -import net.modificationstation.stationapi.api.network.ConnectionHelper; -import org.spongepowered.asm.mixin.Mixin; - -@Mixin(ConnectionHelper.class) -public class ConnectionHelperMixin { -} diff --git a/station-networking-v0/src/main/resources/station-networking-v0.accesswidener b/station-networking-v0/src/main/resources/station-networking-v0.accesswidener index 52d8a2c04..613c17b86 100644 --- a/station-networking-v0/src/main/resources/station-networking-v0.accesswidener +++ b/station-networking-v0/src/main/resources/station-networking-v0.accesswidener @@ -3,4 +3,7 @@ accessWidener v2 named transitive-extendable method net/minecraft/network/Connection method_1137 ()Z #writeTick transitive-extendable method net/minecraft/network/Connection method_1139 ()Z #readTick -accessible field net/minecraft/class_166 field_581 I \ No newline at end of file +accessible field net/minecraft/class_166 field_581 I + +accessible field net/minecraft/network/Connection field_1290 Z +accessible field net/minecraft/network/Connection field_1280 Ljava/lang/Object; \ No newline at end of file diff --git a/station-networking-v0/src/main/resources/station-networking-v0.mixins.json b/station-networking-v0/src/main/resources/station-networking-v0.mixins.json index 56247c5ea..3dbe1d865 100644 --- a/station-networking-v0/src/main/resources/station-networking-v0.mixins.json +++ b/station-networking-v0/src/main/resources/station-networking-v0.mixins.json @@ -5,7 +5,6 @@ "compatibilityLevel": "JAVA_17", "mixins": [ "AbstractPacketAccessor", - "ConnectionHelperMixin", "ConnectionMixin", "HandshakePacketMixin", "PacketMixin" From 013565c9eb99f8472df4b4852281e21bf5e60708 Mon Sep 17 00:00:00 2001 From: alpha Date: Wed, 10 Sep 2025 12:12:40 -0500 Subject: [PATCH 4/7] More refactors Introduce Payload class so generics are happy --- build.gradle.kts | 2 +- .../stationapi/api/network/BufferPool.java | 45 ++++ .../stationapi/api/network/PacketByteBuf.java | 74 +++++- .../api/network/StationConnection.java | 237 +++++++++++++++--- .../StationServerConnectionListener.java | 43 +++- .../api/network/packet/Payload.java | 11 + .../api/network/packet/PayloadPacket.java | 5 - .../api/registry/PayloadTypeRegistry.java | 7 +- .../mixin/network/ConnectionMixin.java | 8 + .../ServerConnectionListenerMixin.java | 8 +- .../client/ClientNetworkHandlerMixin.java | 82 ++++++ .../station-networking-v0.accesswidener | 17 +- .../station-networking-v0.mixins.json | 3 + 13 files changed, 493 insertions(+), 49 deletions(-) create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/BufferPool.java create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/packet/Payload.java delete mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/packet/PayloadPacket.java create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/client/ClientNetworkHandlerMixin.java diff --git a/build.gradle.kts b/build.gradle.kts index d3ee4e9d4..a88ca7ec6 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -88,7 +88,7 @@ allprojects { implementation("org.jetbrains:annotations:23.0.0") modLocalRuntime("net.glasslauncher.mods:ModMenu:${project.properties["modmenu_version"]}") - modLocalRuntime("maven.modrinth:retrocommands:${project.properties["rc_version"]}") { + modImplementation("maven.modrinth:retrocommands:${project.properties["rc_version"]}") { isTransitive = false } diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/BufferPool.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/BufferPool.java new file mode 100644 index 000000000..e6c2e0e73 --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/BufferPool.java @@ -0,0 +1,45 @@ +package net.modificationstation.stationapi.api.network; + +import org.jetbrains.annotations.ApiStatus; + +import java.lang.ref.Cleaner; +import java.lang.ref.SoftReference; +import java.nio.ByteBuffer; +import java.util.LinkedList; +import java.util.Queue; + +@ApiStatus.Experimental +public class BufferPool { + public static final int BUFFER_SIZE = 16_383; + private static final Cleaner CLEANER = Cleaner.create(); + private final Queue> buffers = new LinkedList<>(); + + public ByteBuffer get() { + ByteBuffer ret; + SoftReference ref; + // Check if the pool has any buffer's available + while ((ref = buffers.poll()) != null) { + if ((ret = ref.get()) != null) { + return ret; + } + } + // If not buffers are available create one + return ByteBuffer.allocateDirect(BUFFER_SIZE); + } + + public void register(Object ref, ByteBuffer buffer) { + CLEANER.register(ref, () -> buffers.add(new SoftReference<>(buffer))); + } + + public void clear() { + buffers.clear(); + } + + public int size() { + return buffers.size(); + } + + public boolean offer(ByteBuffer buffer) { + return buffers.offer(new SoftReference<>(buffer.clear())); + } +} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/PacketByteBuf.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/PacketByteBuf.java index e3e9463ba..b12b3cde5 100644 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/PacketByteBuf.java +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/PacketByteBuf.java @@ -4,15 +4,26 @@ import net.modificationstation.stationapi.api.network.codec.StreamDecoder; import net.modificationstation.stationapi.api.network.codec.StreamEncoder; import net.modificationstation.stationapi.api.util.math.StationBlockPos; +import org.jetbrains.annotations.ApiStatus; +import org.jetbrains.annotations.NotNull; +import java.io.*; import java.nio.ByteBuffer; +import java.nio.channels.SocketChannel; +import java.nio.channels.WritableByteChannel; import java.nio.charset.StandardCharsets; public class PacketByteBuf { + public static final BufferPool BUFFER_POOL = new BufferPool(); public static final short MAX_STRING_LENGTH = 32767; protected final ByteBuffer source; + @ApiStatus.Experimental + public static PacketByteBuf pooled() { + return new PacketByteBuf(BUFFER_POOL.get()); + } + public PacketByteBuf(ByteBuffer source) { this.source = source; } @@ -21,6 +32,67 @@ public ByteBuffer getSource() { return source; } + public PacketByteBuf flip() { + source.flip(); + return this; + } + +// public PacketByteBuf resize(int newSize) { +// } + + public PacketByteBuf slice(int index, int length) { + return new PacketByteBuf(source.slice(index, length)); + } + + public DataInputStream getInputStream() { + return new DataInputStream(new InputStream() { + @Override + public int read() throws IOException { + if (!source.hasRemaining()) + return -1; + return ((int) source.get()) & 0xff; + } + @Override + public int read(byte[] b, int off, int len) throws IOException { + if (!source.hasRemaining()) + return -1; + len = Math.min(len, source.remaining()); + source.get(b, off, len); + return len; + } + }); + } + + public DataOutputStream getOutputStream() { + return new DataOutputStream(new OutputStream() { + @Override + public void write(int b) throws IOException { + source.put((byte) b); + } + + @Override + public void write(@NotNull byte[] b, int off, int len) throws IOException { + source.put(b, off, len); + } + }); + } + + public int read(SocketChannel channel) throws IOException { + int count = channel.read(source); + if (count == -1) + throw new EOFException("End of stream"); + return count; + } + + public boolean flush(WritableByteChannel channel) throws IOException { + if (source.position() == 0) + return true; + int count = channel.write(source.slice(0, source.position())); + if (count == -1) + throw new EOFException("End of stream"); + return true; + } + public void write(StreamEncoder encoder, T value) { encoder.encode(this, value); } @@ -66,7 +138,7 @@ public void writeInt(int value) { } public int readInt() { - return source.getInt(); + return source.getInt(0); } public void writeLong(long value) { diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationConnection.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationConnection.java index 760a58e74..9c3ae8700 100644 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationConnection.java +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationConnection.java @@ -2,42 +2,77 @@ import net.minecraft.network.Connection; import net.minecraft.network.NetworkHandler; -import net.modificationstation.stationapi.api.network.packet.PacketType; -import net.modificationstation.stationapi.api.network.packet.PayloadPacket; +import net.minecraft.network.packet.Packet; +import net.minecraft.network.packet.play.UpdateSignPacket; +import net.modificationstation.stationapi.api.network.packet.ManagedPacket; +import net.modificationstation.stationapi.api.network.packet.Payload; import net.modificationstation.stationapi.api.network.packet.PayloadType; import net.modificationstation.stationapi.api.registry.PayloadTypeRegistry; +import org.spongepowered.asm.mixin.Unique; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.EOFException; +import java.io.IOException; import java.nio.ByteBuffer; +import java.nio.channels.ClosedChannelException; +import java.nio.channels.Selector; import java.nio.channels.SocketChannel; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; +import java.util.function.Function; public class StationConnection extends Connection { - protected List> incomingPayloads = Collections.synchronizedList(new ArrayList<>()); + private static final Object STATIONAPI$PACKET_READ_LOCK = new Object(); + private static final AtomicBoolean STATIONAPI$BLOCKNG_PACKET = new AtomicBoolean(); + protected List incomingPayloads = Collections.synchronizedList(new ArrayList<>()); protected List> outgoingPayloads = Collections.synchronizedList(new ArrayList<>()); protected List> outgoingPayloadsDelayed = Collections.synchronizedList(new ArrayList<>()); protected SocketChannel socketChannel; - protected PacketByteBuf outgoingBuffer; + protected PacketByteBuf readBuffer; + protected PacketByteBuf writeBuffer; + private DataInputStream inputStream; + private DataOutputStream outputStream; - public StationConnection(SocketChannel socket, String string, NetworkHandler networkHandler) { - super(socket.socket(), string, networkHandler); + public StationConnection(SocketChannel socket, String name, NetworkHandler networkHandler) { + super(socket.socket(), name, networkHandler); this.socketChannel = socket; - this.outgoingBuffer = new PacketByteBuf(ByteBuffer.allocate(5120)); + this.readBuffer = PacketByteBuf.pooled(); + this.writeBuffer = PacketByteBuf.pooled(); + + this.inputStream = readBuffer.getInputStream(); + this.outputStream = writeBuffer.getOutputStream(); + +// this.field_1292 = new Thread(this::packetReadLoop, "Station" + name + " read thread"); +// this.field_1291 = new Thread(this::packetWriteLoop, "Station" + name + " write thread"); +// this.field_1292.start(); +// this.field_1291.start(); + } + + protected Function getPayloadDecoder() { + return buf -> { + int packetId = this.readBuffer.readByte() & 0xFF; // Apply bit mask to make sure range is between 0-255 + if (packetId != Payload.PACKET_ID) + return null; + PayloadType type = (PayloadType) PayloadTypeRegistry.INSTANCE.get(this.readBuffer.readVarInt()); + if (type == null) + return null; + return type.codec().decode(this.readBuffer); + }; } - protected

Consumer getPacketEncoder(PayloadType type, P payload) { + protected

Consumer getPayloadEncoder(PayloadType type, P payload) { return buf -> { - buf.writeInt(PayloadPacket.PACKET_ID); + buf.writeByte(Payload.PACKET_ID); buf.writeVarInt(PayloadTypeRegistry.INSTANCE.getRawId(type)); type.codec().encode(buf, payload); }; } - public

void sendPayload(PayloadType type, P payload) { + public

void sendPayload(PayloadType type, P payload) { if (!this.field_1290) { // this.closed - Consumer encoder = getPacketEncoder(type, payload); + Consumer encoder = getPayloadEncoder(type, payload); if (type.delayed()) outgoingPayloadsDelayed.add(encoder); else @@ -47,40 +82,180 @@ public

void sendPayload(PayloadType type, P payload) { @Override public void method_1122() { +// this.readThread.interrupt(); +// this.writeThread.interrupt(); } @Override - protected boolean method_1137() { // Write Tick - boolean handled = super.method_1137(); + public void method_1129() { // tick +// if (this.field_1297 > 1048576) { +// this.disconnect("disconnect.overflow"); +// } - // Vanilla only decodes one packet a tick, TODO possibly change this? - if (!this.outgoingPayloads.isEmpty()) { - Consumer type; - synchronized (this.field_1280) { - type = this.outgoingPayloads.remove(0); + if (this.field_1286.isEmpty()) { + if (this.field_1296++ == 1200) { + this.disconnect("disconnect.timeout"); + } + } else { + this.field_1296 = 0; + } + + int var1 = 100; + + while (!this.field_1286.isEmpty() && var1-- >= 0) { + Packet packet = (Packet)this.field_1286.remove(0); + if (packet instanceof ManagedPacket managedPacket) { + packet.apply(managedPacket.getType().getHandler().orElse(this.field_1289)); + if (managedPacket.getType().blocking) { + synchronized (STATIONAPI$PACKET_READ_LOCK) { + STATIONAPI$BLOCKNG_PACKET.set(false); + STATIONAPI$PACKET_READ_LOCK.notifyAll(); + } + } + } else { + if (packet instanceof UpdateSignPacket updateSignPacket) { + if (updateSignPacket.x == 0 && updateSignPacket.y == -1 && updateSignPacket.z == 0) { + continue; + } + } + packet.apply(this.field_1289); } - type.accept(this.outgoingBuffer); - handled = true; } - if (!this.outgoingPayloadsDelayed.isEmpty()) { + this.method_1122(); + if (this.field_1293 && this.field_1286.isEmpty()) { + this.field_1289.onDisconnected(this.field_1294, this.field_1295); + } + } + + protected boolean pollPacket(List packets, PacketByteBuf buf) { + if (!packets.isEmpty()) { + Packet packet; + synchronized(this.field_1280) { + packet = packets.remove(0); + } + Packet.write(packet, outputStream); + return true; + } + return false; + } + + protected boolean pollPayload(List> payloads, PacketByteBuf buf) { + if (!payloads.isEmpty()) { Consumer type; synchronized (this.field_1280) { - type = this.outgoingPayloadsDelayed.remove(0); + type = payloads.remove(0); } - type.accept(this.outgoingBuffer); - handled = true; + type.accept(buf); + return true; } + return false; + } + + protected boolean pollWrite(PacketByteBuf buf) { + boolean handled = false; // TODO: do we even need to make vanilla use our buffer pool? + + // Handle normal packets first + handled |= pollPacket(this.field_1287, buf); + handled |= pollPayload(this.outgoingPayloads, buf); + // Then handle delayed packets + handled |= pollPacket(this.field_1288, buf); + handled |= pollPayload(this.outgoingPayloadsDelayed, buf); return handled; } - @Override - protected boolean method_1139() { // Read Tick - return super.method_1139(); + protected boolean pollRead() { + int oldPos = readBuffer.getSource().position(); + Payload payload = getPayloadDecoder().apply(readBuffer); + // Payload being null doesn't mean we are at the end of the stream TODO: make a dummy payload for this? + if (payload != null) { + incomingPayloads.add(payload); + return true; + } + + readBuffer.getSource().position(oldPos); + + Packet packet = Packet.read(inputStream, this.field_1289.isServerSide()); + if (packet != null) { + field_1286.add(packet); + if (packet instanceof ManagedPacket managedPacket && managedPacket.getType().blocking) { + synchronized (STATIONAPI$PACKET_READ_LOCK) { + STATIONAPI$BLOCKNG_PACKET.set(true); + while (STATIONAPI$BLOCKNG_PACKET.get()) try { + STATIONAPI$PACKET_READ_LOCK.wait(); + } catch (InterruptedException ignored) { + } + } + } + return true; + } else { + disconnect("disconnect.endOfStream"); + } + return false; } - protected record TypeWithPayload

(PayloadType type, Consumer payload) {} + public void packetWrite() { + try { + if(this.field_1285) { + // Poll packets til there is none left + PacketByteBuf buf = writeBuffer;//PacketByteBuf.pooled(); + while(pollWrite(buf)) { + } + + try { + Thread.sleep(100L); + } catch (InterruptedException e) { + } + + try { + buf.flush(socketChannel); +// if (buf.flush(socketChannel)) +// PacketByteBuf.BUFFER_POOL.offer(buf.getSource()); + } catch (ClosedChannelException ignored) { + } catch (EOFException e) { + disconnect("disconnect.endOfStream"); + } catch (IOException e) { + if (!this.field_1293) { + disconnect(e); + } + + e.printStackTrace(); + } + } + } catch (Exception e) { + disconnect(e); + } + } + + public void packetRead() { + try { + if(this.field_1285 && !this.field_1290) { + try { + readBuffer.read(socketChannel); + readBuffer.flip(); + } catch (EOFException e) { + disconnect("disconnect.endOfStream"); + } catch (IOException e) { + if (!this.field_1293) { + this.disconnect(e); + } + } + // Poll packets til there is none left + while(readBuffer.getSource().hasRemaining()) { + pollRead(); + } + readBuffer.getSource().compact(); + + try { + Thread.sleep(100L); + } catch (InterruptedException var13) { + } + } + } catch (Exception e) { + disconnect(e); + } + } } diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationServerConnectionListener.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationServerConnectionListener.java index 4d30c21cf..5034a8de2 100644 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationServerConnectionListener.java +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationServerConnectionListener.java @@ -1,5 +1,7 @@ package net.modificationstation.stationapi.api.network; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; import net.minecraft.class_9; import net.minecraft.server.MinecraftServer; import net.minecraft.server.network.ServerLoginNetworkHandler; @@ -9,17 +11,23 @@ import java.io.IOException; import java.net.*; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.ArrayList; +import java.util.Iterator; import java.util.List; +import java.util.Set; import java.util.logging.Level; /** * Replaces {@link class_9} in accepting network connections */ +@Environment(EnvType.SERVER) public class StationServerConnectionListener { private ServerSocketChannel socketChannel; + private Selector selector; private final MinecraftServer server; private final Thread listenerThread; private volatile boolean listening; @@ -43,6 +51,9 @@ else if (address == null) this.pendingConnections = new ArrayList<>(maxPlayers); this.connections = new ArrayList<>(maxPlayers); this.socketChannel = NioNetworkPlugin.INSTANCE.openServer(new InetSocketAddress(address, port), family); + this.socketChannel.configureBlocking(false); + this.selector = Selector.open(); + this.socketChannel.register(selector, SelectionKey.OP_ACCEPT); this.listening = true; this.listenerThread = new Thread(this::listen, "Station-Listener-Thread"); this.listenerThread.start(); @@ -51,9 +62,37 @@ else if (address == null) public void listen() { while (listening) { try { - SocketChannel socket = this.socketChannel.accept(); + int readyKeys = selector.select(); + if (readyKeys == 0) continue; + Set keys = selector.selectedKeys(); + Iterator iterator = keys.iterator(); + while (iterator.hasNext()) { + SelectionKey key = iterator.next(); + if (key.isValid() && key.isAcceptable()) { + SocketChannel socket = this.socketChannel.accept(); + socket.setOption(StandardSocketOptions.TCP_NODELAY, true); // Vanilla doesn't do this, but I think it can improve networking performance, Probably look into RFC1122 which the java doc refers to + + var pendingConnection = new StationServerLoginNetworkHandler(this.server, socket, "Connection #" + this.connectionCounter++); + socket.configureBlocking(false); + socket.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE, pendingConnection.connection); + addPendingConnection(pendingConnection); + } + + if (key.isValid() && key.isReadable()) { + if (key.attachment() instanceof StationConnection connection) { + connection.packetRead(); + } + } + + if (key.isValid() && key.isWritable()) { + if (key.attachment() instanceof StationConnection connection) { + connection.packetWrite(); + } + } + + iterator.remove(); + } - addPendingConnection(new StationServerLoginNetworkHandler(this.server, socket, "Connection #" + this.connectionCounter++)); } catch (IOException e) { e.printStackTrace(); } diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/packet/Payload.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/packet/Payload.java new file mode 100644 index 000000000..9fe6fcdb1 --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/packet/Payload.java @@ -0,0 +1,11 @@ +package net.modificationstation.stationapi.api.network.packet; + +import net.modificationstation.stationapi.api.network.PacketByteBuf; + +public interface Payload { + int PACKET_ID = 253; + + PayloadType type(); + + void apply(HANDLER handler); +} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/packet/PayloadPacket.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/packet/PayloadPacket.java deleted file mode 100644 index f7fe8a5f0..000000000 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/packet/PayloadPacket.java +++ /dev/null @@ -1,5 +0,0 @@ -package net.modificationstation.stationapi.api.network.packet; - -public class PayloadPacket { - public static final int PACKET_ID = 253; -} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/registry/PayloadTypeRegistry.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/registry/PayloadTypeRegistry.java index 6e96a89b9..35ae08020 100644 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/registry/PayloadTypeRegistry.java +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/registry/PayloadTypeRegistry.java @@ -4,13 +4,14 @@ import net.modificationstation.stationapi.api.event.registry.RegistryAttribute; import net.modificationstation.stationapi.api.event.registry.RegistryAttributeHolder; import net.modificationstation.stationapi.api.network.PacketByteBuf; +import net.modificationstation.stationapi.api.network.packet.Payload; import net.modificationstation.stationapi.api.network.packet.PayloadType; import static net.modificationstation.stationapi.api.StationAPI.NAMESPACE; -public class PayloadTypeRegistry extends SimpleRegistry> { - public static final RegistryKey>> KEY = RegistryKey.ofRegistry(NAMESPACE.id("payload_types")); - public static final Registry> INSTANCE = Registries.create(KEY, new PayloadTypeRegistry(), Lifecycle.experimental()); +public class PayloadTypeRegistry extends SimpleRegistry> { + public static final RegistryKey>> KEY = RegistryKey.ofRegistry(NAMESPACE.id("payload_types")); + public static final Registry> INSTANCE = Registries.create(KEY, new PayloadTypeRegistry(), Lifecycle.experimental()); private PayloadTypeRegistry() { super(KEY, Lifecycle.experimental(), true); diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/ConnectionMixin.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/ConnectionMixin.java index 81dd7aab1..beb0cbce1 100644 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/ConnectionMixin.java +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/ConnectionMixin.java @@ -6,6 +6,7 @@ import net.minecraft.network.Connection; import net.minecraft.network.NetworkHandler; import net.minecraft.network.packet.Packet; +import net.modificationstation.stationapi.api.network.StationConnection; import net.modificationstation.stationapi.api.network.packet.ManagedPacket; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; @@ -23,6 +24,13 @@ class ConnectionMixin { @Unique private static final AtomicBoolean STATIONAPI$BLOCKNG_PACKET = new AtomicBoolean(); + @WrapOperation(method = "", at = @At(value = "INVOKE", target = "Ljava/lang/Thread;start()V")) + private void stationapi_preventVanillaThreadFromStarting(Thread instance, Operation original) { + if (!((Object) this instanceof StationConnection)) { + original.call(instance); + } + } + @WrapOperation( method = "method_1129", at = @At( diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/ServerConnectionListenerMixin.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/ServerConnectionListenerMixin.java index d46e60f2c..8736923bd 100644 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/ServerConnectionListenerMixin.java +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/ServerConnectionListenerMixin.java @@ -12,8 +12,8 @@ @Mixin(class_9.class) public class ServerConnectionListenerMixin { - @WrapOperation(method = "", at = @At(value = "NEW", target = "(IILjava/net/InetAddress;)Ljava/net/ServerSocket;")) - private ServerSocket createServerSocket(int port, int backlog, InetAddress address, Operation original) throws IOException { - return null; - } +// @WrapOperation(method = "", at = @At(value = "NEW", target = "(IILjava/net/InetAddress;)Ljava/net/ServerSocket;")) +// private ServerSocket createServerSocket(int port, int backlog, InetAddress address, Operation original) throws IOException { +// return null; +// } } diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/client/ClientNetworkHandlerMixin.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/client/ClientNetworkHandlerMixin.java new file mode 100644 index 000000000..7d57e2271 --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/client/ClientNetworkHandlerMixin.java @@ -0,0 +1,82 @@ +package net.modificationstation.stationapi.mixin.network.client; + +import com.llamalad7.mixinextras.injector.wrapoperation.Operation; +import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; +import com.llamalad7.mixinextras.sugar.Share; +import com.llamalad7.mixinextras.sugar.ref.LocalRef; +import net.minecraft.client.network.ClientNetworkHandler; +import net.minecraft.network.Connection; +import net.minecraft.network.NetworkHandler; +import net.modificationstation.stationapi.api.network.StationConnection; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; + +import java.io.IOException; +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.nio.channels.SelectionKey; +import java.nio.channels.Selector; +import java.nio.channels.SocketChannel; +import java.util.Iterator; +import java.util.Set; + +@Mixin(ClientNetworkHandler.class) +public abstract class ClientNetworkHandlerMixin { + @Shadow private boolean disconnected; + + @Shadow private Connection connection; + private Selector stationapi_selector; + private Thread stationapi_listenThread; + + @WrapOperation(method = "", at = @At(value = "NEW", target = "(Ljava/net/InetAddress;I)Ljava/net/Socket;")) + private Socket wrapSocket(InetAddress address, int port, Operation original, @Share("nio_socket") LocalRef socketRef) throws IOException { + SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress(address, port)); + socketRef.set(socketChannel); + return socketChannel.socket(); + } + + @WrapOperation(method = "", at = @At(value = "NEW", target = "(Ljava/net/Socket;Ljava/lang/String;Lnet/minecraft/network/NetworkHandler;)Lnet/minecraft/network/Connection;")) + private Connection useStationConnection(Socket oldSocket, String name, NetworkHandler networkHandler, Operation original, @Share("nio_socket") LocalRef socketRef) throws IOException { + SocketChannel socket = socketRef.get(); + if (socket == null) + return original.call(oldSocket, name, networkHandler); + + this.stationapi_selector = Selector.open(); + socket.configureBlocking(false); + socket.register(this.stationapi_selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE); + this.stationapi_listenThread = new Thread(this::stationapi_listen, "Station" + name + " read thread"); + this.stationapi_listenThread.start(); + + return new StationConnection(socket, "Station " + name, networkHandler); + } + + private void stationapi_listen() { + while (!this.disconnected) { + if (connection == null) + continue; + try { + int readyKeys = stationapi_selector.select(); + if (readyKeys == 0) continue; + Set keys = stationapi_selector.selectedKeys(); + Iterator iterator = keys.iterator(); + while (iterator.hasNext()) { + SelectionKey key = iterator.next(); + + if (key.isValid() && key.isReadable()) { + ((StationConnection) this.connection).packetRead(); + } + + if (key.isValid() && key.isWritable()) { + ((StationConnection) this.connection).packetWrite(); + } + iterator.remove(); + } + } catch (IOException e) { + this.connection.disconnect(e); + break; + } + } + } +} diff --git a/station-networking-v0/src/main/resources/station-networking-v0.accesswidener b/station-networking-v0/src/main/resources/station-networking-v0.accesswidener index 613c17b86..060a8d329 100644 --- a/station-networking-v0/src/main/resources/station-networking-v0.accesswidener +++ b/station-networking-v0/src/main/resources/station-networking-v0.accesswidener @@ -5,5 +5,18 @@ transitive-extendable method net/minecraft/network/Connection method_1139 ()Z #r accessible field net/minecraft/class_166 field_581 I -accessible field net/minecraft/network/Connection field_1290 Z -accessible field net/minecraft/network/Connection field_1280 Ljava/lang/Object; \ No newline at end of file +accessible field net/minecraft/network/Connection field_1285 Z # running +accessible field net/minecraft/network/Connection field_1286 Ljava/util/List; # incomingPackets +accessible field net/minecraft/network/Connection field_1287 Ljava/util/List; # outgoingPackets +accessible field net/minecraft/network/Connection field_1288 Ljava/util/List; # delayedOutgoingPackets +accessible field net/minecraft/network/Connection field_1289 Lnet/minecraft/network/NetworkHandler; # listener +accessible field net/minecraft/network/Connection field_1290 Z # closed +accessible field net/minecraft/network/Connection field_1280 Ljava/lang/Object; # Thread Lock +accessible field net/minecraft/network/Connection field_1291 Ljava/lang/Thread; # Write Thread +accessible field net/minecraft/network/Connection field_1292 Ljava/lang/Thread; # Read Thread +accessible field net/minecraft/network/Connection field_1293 Z # disconnected +accessible field net/minecraft/network/Connection field_1294 Ljava/lang/String; # disconnectReason +accessible field net/minecraft/network/Connection field_1295 [Ljava/lang/Object; # disconnectArgs +accessible field net/minecraft/network/Connection field_1296 I # timeoutTicks +accessible field net/minecraft/network/Connection field_1297 I # bufferSize +accessible method net/minecraft/network/Connection disconnect (Ljava/lang/Exception;)V \ No newline at end of file diff --git a/station-networking-v0/src/main/resources/station-networking-v0.mixins.json b/station-networking-v0/src/main/resources/station-networking-v0.mixins.json index 3dbe1d865..e390d2c2f 100644 --- a/station-networking-v0/src/main/resources/station-networking-v0.mixins.json +++ b/station-networking-v0/src/main/resources/station-networking-v0.mixins.json @@ -16,5 +16,8 @@ "MinecraftServerMixin", "ServerConnectionListenerMixin", "ServerLoginNetworkHandlerMixin" + ], + "client": [ + "client.ClientNetworkHandlerMixin" ] } From 7840092306e192b219a25a468e7df6924914dfbb Mon Sep 17 00:00:00 2001 From: alpha Date: Wed, 10 Sep 2025 13:17:24 -0500 Subject: [PATCH 5/7] Make StationServerConnectionListener pass a SocketAddress Remove old code from StationConnection --- .../api/network/StationConnection.java | 35 +++++-------------- .../StationServerConnectionListener.java | 20 +++++------ .../mixin/network/MinecraftServerMixin.java | 3 +- 3 files changed, 19 insertions(+), 39 deletions(-) diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationConnection.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationConnection.java index 9c3ae8700..0e71875c1 100644 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationConnection.java +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationConnection.java @@ -8,15 +8,10 @@ import net.modificationstation.stationapi.api.network.packet.Payload; import net.modificationstation.stationapi.api.network.packet.PayloadType; import net.modificationstation.stationapi.api.registry.PayloadTypeRegistry; -import org.spongepowered.asm.mixin.Unique; -import java.io.DataInputStream; -import java.io.DataOutputStream; import java.io.EOFException; import java.io.IOException; -import java.nio.ByteBuffer; import java.nio.channels.ClosedChannelException; -import java.nio.channels.Selector; import java.nio.channels.SocketChannel; import java.util.*; import java.util.concurrent.atomic.AtomicBoolean; @@ -31,19 +26,14 @@ public class StationConnection extends Connection { protected List> outgoingPayloadsDelayed = Collections.synchronizedList(new ArrayList<>()); protected SocketChannel socketChannel; protected PacketByteBuf readBuffer; - protected PacketByteBuf writeBuffer; - private DataInputStream inputStream; - private DataOutputStream outputStream; public StationConnection(SocketChannel socket, String name, NetworkHandler networkHandler) { super(socket.socket(), name, networkHandler); this.socketChannel = socket; this.readBuffer = PacketByteBuf.pooled(); - this.writeBuffer = PacketByteBuf.pooled(); - - this.inputStream = readBuffer.getInputStream(); - this.outputStream = writeBuffer.getOutputStream(); +// this.writeBuffer = PacketByteBuf.pooled(); + // TODO make these virtual threads when we update to J21+ // this.field_1292 = new Thread(this::packetReadLoop, "Station" + name + " read thread"); // this.field_1291 = new Thread(this::packetWriteLoop, "Station" + name + " write thread"); // this.field_1292.start(); @@ -88,6 +78,7 @@ public void method_1122() { @Override public void method_1129() { // tick + // We don't maintain packet size stuff anymore // if (this.field_1297 > 1048576) { // this.disconnect("disconnect.overflow"); // } @@ -135,7 +126,7 @@ protected boolean pollPacket(List packets, PacketByteBuf buf) { synchronized(this.field_1280) { packet = packets.remove(0); } - Packet.write(packet, outputStream); + Packet.write(packet, buf.getOutputStream()); return true; } return false; @@ -178,7 +169,7 @@ protected boolean pollRead() { readBuffer.getSource().position(oldPos); - Packet packet = Packet.read(inputStream, this.field_1289.isServerSide()); + Packet packet = Packet.read(readBuffer.getInputStream(), this.field_1289.isServerSide()); if (packet != null) { field_1286.add(packet); if (packet instanceof ManagedPacket managedPacket && managedPacket.getType().blocking) { @@ -201,19 +192,14 @@ public void packetWrite() { try { if(this.field_1285) { // Poll packets til there is none left - PacketByteBuf buf = writeBuffer;//PacketByteBuf.pooled(); + PacketByteBuf buf = PacketByteBuf.pooled(); while(pollWrite(buf)) { } - try { - Thread.sleep(100L); - } catch (InterruptedException e) { - } try { - buf.flush(socketChannel); -// if (buf.flush(socketChannel)) -// PacketByteBuf.BUFFER_POOL.offer(buf.getSource()); + if (buf.flush(socketChannel)) + PacketByteBuf.BUFFER_POOL.offer(buf.getSource()); } catch (ClosedChannelException ignored) { } catch (EOFException e) { disconnect("disconnect.endOfStream"); @@ -248,11 +234,6 @@ public void packetRead() { pollRead(); } readBuffer.getSource().compact(); - - try { - Thread.sleep(100L); - } catch (InterruptedException var13) { - } } } catch (Exception e) { disconnect(e); diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationServerConnectionListener.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationServerConnectionListener.java index 5034a8de2..ce0e6d2ff 100644 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationServerConnectionListener.java +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationServerConnectionListener.java @@ -35,22 +35,20 @@ public class StationServerConnectionListener { private final List connections; private final List pendingConnections; - public StationServerConnectionListener(MinecraftServer server, InetAddress address, int port) throws IOException { + public StationServerConnectionListener(MinecraftServer server, SocketAddress address) throws IOException { this.server = server; ProtocolFamily family; - // TODO: Unix addresses? - if (address instanceof Inet4Address) - family = StandardProtocolFamily.INET; - else if (address instanceof Inet6Address) - family = StandardProtocolFamily.INET6; - else if (address == null) - family = null; - else - throw new RuntimeException("Unsupported address family: " + address.getHostAddress()); + if (address instanceof InetSocketAddress inetAddress) { + family = inetAddress.getAddress().getAddress().length == 4 ? StandardProtocolFamily.INET : StandardProtocolFamily.INET6; + } else if (address instanceof UnixDomainSocketAddress) { + family = StandardProtocolFamily.UNIX; + } else { + throw new RuntimeException("Unsupported socket address: " + address); + } int maxPlayers = server.field_2840.method_1246("max-players", 20); this.pendingConnections = new ArrayList<>(maxPlayers); this.connections = new ArrayList<>(maxPlayers); - this.socketChannel = NioNetworkPlugin.INSTANCE.openServer(new InetSocketAddress(address, port), family); + this.socketChannel = NioNetworkPlugin.INSTANCE.openServer(address, family); this.socketChannel.configureBlocking(false); this.selector = Selector.open(); this.socketChannel.register(selector, SelectionKey.OP_ACCEPT); diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/MinecraftServerMixin.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/MinecraftServerMixin.java index a482e5fc6..364212848 100644 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/MinecraftServerMixin.java +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/MinecraftServerMixin.java @@ -12,6 +12,7 @@ import java.io.IOException; import java.net.InetAddress; +import java.net.InetSocketAddress; @Mixin(MinecraftServer.class) public class MinecraftServerMixin implements StationMinecraftServer { @@ -20,7 +21,7 @@ public class MinecraftServerMixin implements StationMinecraftServer { @WrapOperation(method = "method_2166", at = @At(value = "NEW", target = "(Lnet/minecraft/server/MinecraftServer;Ljava/net/InetAddress;I)Lnet/minecraft/class_9;")) private class_9 createStationConnectionListener(MinecraftServer inetAddress, InetAddress address, int port, Operation original) throws IOException { - this.stationapi_listener = new StationServerConnectionListener((MinecraftServer) (Object) this, address, port); + this.stationapi_listener = new StationServerConnectionListener((MinecraftServer) (Object) this, new InetSocketAddress(address, port)); return null; } From 7527037c1a0a0d92c98106aaf8a29f7ac28fb490 Mon Sep 17 00:00:00 2001 From: alpha Date: Wed, 10 Sep 2025 13:19:07 -0500 Subject: [PATCH 6/7] Remove NetworkPlugin --- .../stationapi/api/network/NetworkPlugin.java | 15 --------- .../StationServerConnectionListener.java | 4 +-- .../api/network/nio/NioNetworkPlugin.java | 32 ------------------- 3 files changed, 2 insertions(+), 49 deletions(-) delete mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/NetworkPlugin.java delete mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/nio/NioNetworkPlugin.java diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/NetworkPlugin.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/NetworkPlugin.java deleted file mode 100644 index 364fc1a92..000000000 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/NetworkPlugin.java +++ /dev/null @@ -1,15 +0,0 @@ -package net.modificationstation.stationapi.api.network; - -import org.jetbrains.annotations.Nullable; - -import java.io.IOException; -import java.net.ProtocolFamily; -import java.net.SocketAddress; -import java.nio.channels.NetworkChannel; - -public interface NetworkPlugin { - - NetworkChannel openServer(SocketAddress address, @Nullable ProtocolFamily family) throws IOException; - - NetworkChannel open() throws IOException; -} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationServerConnectionListener.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationServerConnectionListener.java index ce0e6d2ff..fcde16c64 100644 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationServerConnectionListener.java +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationServerConnectionListener.java @@ -6,7 +6,6 @@ import net.minecraft.server.MinecraftServer; import net.minecraft.server.network.ServerLoginNetworkHandler; import net.minecraft.server.network.ServerPlayNetworkHandler; -import net.modificationstation.stationapi.api.network.nio.NioNetworkPlugin; import net.modificationstation.stationapi.impl.network.server.StationServerLoginNetworkHandler; import java.io.IOException; @@ -48,7 +47,8 @@ public StationServerConnectionListener(MinecraftServer server, SocketAddress add int maxPlayers = server.field_2840.method_1246("max-players", 20); this.pendingConnections = new ArrayList<>(maxPlayers); this.connections = new ArrayList<>(maxPlayers); - this.socketChannel = NioNetworkPlugin.INSTANCE.openServer(address, family); + this.socketChannel = ServerSocketChannel.open(family); + this.socketChannel.bind(address); this.socketChannel.configureBlocking(false); this.selector = Selector.open(); this.socketChannel.register(selector, SelectionKey.OP_ACCEPT); diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/nio/NioNetworkPlugin.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/nio/NioNetworkPlugin.java deleted file mode 100644 index ee8daa6b0..000000000 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/nio/NioNetworkPlugin.java +++ /dev/null @@ -1,32 +0,0 @@ -package net.modificationstation.stationapi.api.network.nio; - -import net.modificationstation.stationapi.api.network.NetworkPlugin; - -import java.io.IOException; -import java.net.ProtocolFamily; -import java.net.SocketAddress; -import java.nio.channels.NetworkChannel; -import java.nio.channels.ServerSocketChannel; - -/** - * Represents a TCP server - */ -public class NioNetworkPlugin implements NetworkPlugin { - public static final NioNetworkPlugin INSTANCE = new NioNetworkPlugin(); - - public NioNetworkPlugin() { - - } - - @Override - public ServerSocketChannel openServer(SocketAddress address, ProtocolFamily family) throws IOException { - ServerSocketChannel serverSocket = family == null ? ServerSocketChannel.open() : ServerSocketChannel.open(family); - serverSocket.bind(address); - return serverSocket; - } - - @Override - public NetworkChannel open() { - return null; - } -} From 22fd494a35a30ef13c99c439f904bdf6b47a8223 Mon Sep 17 00:00:00 2001 From: alpha Date: Wed, 10 Sep 2025 21:49:16 -0500 Subject: [PATCH 7/7] Payloads should be usable now --- .../modificationstation/sltest/MainTest.java | 5 ++ .../sltest/keyboard/KeyboardListener.java | 6 ++ .../sltest/network/ExamplePayload.java | 33 +++++++++ .../sltest/network/ExamplePayloadHandler.java | 18 +++++ .../sltest/network/NetworkListener.java | 13 ++++ .../sltest/option/OptionListener.java | 6 +- .../assets/sltest/stationapi/lang/en_US.lang | 1 + src/test/resources/fabric.mod.json | 3 + .../network/StationClientNetworkHandler.java | 17 +++++ .../payload/PayloadHandlerRegisterEvent.java | 25 +++++++ .../stationapi/api/network/PacketByteBuf.java | 3 - .../stationapi/api/network/PacketSender.java | 4 + .../api/network/PayloadHandler.java | 7 ++ .../api/network/StationConnection.java | 73 +++++++++++-------- .../api/network/StationMinecraftServer.java | 9 --- .../api/network/codec/StreamCodec.java | 13 ++++ .../api/network/codec/StreamCodecs.java | 2 + .../api/network/codec/StreamDecoder.java | 1 + .../api/network/codec/StreamEncoder.java | 1 + .../network/codec/StreamMemberEncoder.java | 6 ++ .../api/network/packet/PacketHelper.java | 59 ++++++++++++++- .../api/network/packet/Payload.java | 7 +- .../api/network/packet/PayloadType.java | 20 ++++- .../api/registry/PayloadTypeRegistry.java | 2 +- .../api/server/StationEntityTracker.java | 20 +++++ .../api/server/StationEntityTrackerEntry.java | 19 +++++ .../api/server/StationMinecraftServer.java | 13 ++++ .../api/server/network/PlayerLookup.java | 36 +++++++++ .../server/network/ServerLoginNetworking.java | 32 ++++++++ .../StationServerConnectionListener.java | 39 +++++----- .../StationServerPlayNetworkHandler.java | 15 ++++ .../packet/PacketHelperClientImpl.java | 41 +++++++++++ .../impl/network/packet/PacketHelperImpl.java | 34 +++++++++ .../StationServerLoginNetworkHandler.java | 1 - .../packet/PacketHelperServerImpl.java | 33 +++++++++ .../ServerConnectionListenerMixin.java | 19 ----- .../client/ClientNetworkHandlerAccessor.java | 12 +++ .../client/ClientNetworkHandlerMixin.java | 17 ++++- .../server/EntityTrackerEntryMixin.java | 35 +++++++++ .../network/server/EntityTrackerMixin.java | 33 +++++++++ .../{ => server}/MinecraftServerMixin.java | 7 +- .../ServerLoginNetworkHandlerMixin.java | 2 +- .../server/ServerPlayNetworkHandlerMixin.java | 26 +++++++ .../src/main/resources/fabric.mod.json | 14 +++- .../station-networking-v0.mixins.json | 9 ++- ...heckerServerLoginNetworkHandlerMixin.java} | 2 +- .../station-vanilla-checker-v0.mixins.json | 2 +- 47 files changed, 692 insertions(+), 103 deletions(-) create mode 100644 src/test/java/net/modificationstation/sltest/network/ExamplePayload.java create mode 100644 src/test/java/net/modificationstation/sltest/network/ExamplePayloadHandler.java create mode 100644 src/test/java/net/modificationstation/sltest/network/NetworkListener.java create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/api/client/network/StationClientNetworkHandler.java create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/api/event/network/payload/PayloadHandlerRegisterEvent.java create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/PacketSender.java create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/PayloadHandler.java delete mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationMinecraftServer.java create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/codec/StreamMemberEncoder.java create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/api/server/StationEntityTracker.java create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/api/server/StationEntityTrackerEntry.java create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/api/server/StationMinecraftServer.java create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/api/server/network/PlayerLookup.java create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/api/server/network/ServerLoginNetworking.java rename station-networking-v0/src/main/java/net/modificationstation/stationapi/api/{ => server}/network/StationServerConnectionListener.java (75%) create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/api/server/network/StationServerPlayNetworkHandler.java delete mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/ServerConnectionListenerMixin.java create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/client/ClientNetworkHandlerAccessor.java create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/server/EntityTrackerEntryMixin.java create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/server/EntityTrackerMixin.java rename station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/{ => server}/MinecraftServerMixin.java (84%) rename station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/{ => server}/ServerLoginNetworkHandlerMixin.java (96%) create mode 100644 station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/server/ServerPlayNetworkHandlerMixin.java rename station-vanilla-checker-v0/src/main/java/net/modificationstation/stationapi/mixin/network/server/{ServerLoginNetworkHandlerMixin.java => CheckerServerLoginNetworkHandlerMixin.java} (97%) diff --git a/src/test/java/net/modificationstation/sltest/MainTest.java b/src/test/java/net/modificationstation/sltest/MainTest.java index 6057adb68..9d2174212 100644 --- a/src/test/java/net/modificationstation/sltest/MainTest.java +++ b/src/test/java/net/modificationstation/sltest/MainTest.java @@ -1,11 +1,16 @@ package net.modificationstation.sltest; import net.fabricmc.api.ModInitializer; +import net.modificationstation.sltest.network.ExamplePayload; +import net.modificationstation.stationapi.api.registry.PayloadTypeRegistry; +import net.modificationstation.stationapi.api.registry.Registry; public class MainTest implements ModInitializer { @Override public void onInitialize() { // new Exception().printStackTrace(); + Registry.register(PayloadTypeRegistry.INSTANCE, SLTest.NAMESPACE) + .accept("example_payload", ExamplePayload.TYPE); } // @EventListener diff --git a/src/test/java/net/modificationstation/sltest/keyboard/KeyboardListener.java b/src/test/java/net/modificationstation/sltest/keyboard/KeyboardListener.java index c5872886d..b70bc5866 100644 --- a/src/test/java/net/modificationstation/sltest/keyboard/KeyboardListener.java +++ b/src/test/java/net/modificationstation/sltest/keyboard/KeyboardListener.java @@ -1,7 +1,10 @@ package net.modificationstation.sltest.keyboard; +import net.fabricmc.loader.api.FabricLoader; import net.mine_diver.unsafeevents.listener.EventListener; +import net.minecraft.client.Minecraft; import net.modificationstation.sltest.SLTest; +import net.modificationstation.sltest.network.ExamplePayload; import net.modificationstation.sltest.option.OptionListener; import net.modificationstation.stationapi.api.client.event.keyboard.KeyStateChangedEvent; import net.modificationstation.stationapi.api.network.packet.MessagePacket; @@ -13,7 +16,10 @@ public class KeyboardListener { @EventListener public static void keyStateChange(KeyStateChangedEvent event) { + Minecraft minecraft = (Minecraft) FabricLoader.getInstance().getGameInstance(); if (event.environment == KeyStateChangedEvent.Environment.IN_GAME && Keyboard.getEventKey() == OptionListener.testBind.code) PacketHelper.send(new MessagePacket(Identifier.of(SLTest.NAMESPACE, "give_me_diamonds"))); + if (event.environment == KeyStateChangedEvent.Environment.IN_GAME && Keyboard.getEventKey() == OptionListener.testBind2.code) + PacketHelper.send(new ExamplePayload(minecraft.player.inventory.getSelectedItem())); } } diff --git a/src/test/java/net/modificationstation/sltest/network/ExamplePayload.java b/src/test/java/net/modificationstation/sltest/network/ExamplePayload.java new file mode 100644 index 000000000..42016584e --- /dev/null +++ b/src/test/java/net/modificationstation/sltest/network/ExamplePayload.java @@ -0,0 +1,33 @@ +package net.modificationstation.sltest.network; + +import net.minecraft.item.ItemStack; +import net.modificationstation.sltest.SLTest; +import net.modificationstation.stationapi.api.network.PacketByteBuf; +import net.modificationstation.stationapi.api.network.codec.StreamCodec; +import net.modificationstation.stationapi.api.network.packet.Payload; +import net.modificationstation.stationapi.api.network.packet.PayloadType; + +public record ExamplePayload(ItemStack stack) implements Payload { + public static final StreamCodec STREAM_CODEC = StreamCodec.ofMember(ExamplePayload::write, ExamplePayload::new); + public static final PayloadType TYPE = new PayloadType<>(SLTest.NAMESPACE.id("example_payload"), STREAM_CODEC); + + public ExamplePayload(PacketByteBuf buf) { + this(new ItemStack(buf.readShort(), buf.readByte(), buf.readShort())); + } + + public void write(PacketByteBuf buf) { + buf.writeShort(stack.itemId); + buf.writeByte(stack.count); + buf.writeShort(stack.getDamage2()); + } + + @Override + public PayloadType type() { + return TYPE; + } + + @Override + public void handle(ExamplePayloadHandler handler) { + handler.handleExamplePayload(this); + } +} diff --git a/src/test/java/net/modificationstation/sltest/network/ExamplePayloadHandler.java b/src/test/java/net/modificationstation/sltest/network/ExamplePayloadHandler.java new file mode 100644 index 000000000..c5ce356c1 --- /dev/null +++ b/src/test/java/net/modificationstation/sltest/network/ExamplePayloadHandler.java @@ -0,0 +1,18 @@ +package net.modificationstation.sltest.network; + +import net.minecraft.server.network.ServerPlayNetworkHandler; +import net.modificationstation.stationapi.api.network.PayloadHandler; +import net.modificationstation.stationapi.mixin.player.server.ServerPlayNetworkHandlerAccessor; + +public class ExamplePayloadHandler implements PayloadHandler { + + private final ServerPlayNetworkHandler handler; + + public ExamplePayloadHandler(ServerPlayNetworkHandler handler) { + this.handler = handler; + } + + public void handleExamplePayload(ExamplePayload payload) { + ((ServerPlayNetworkHandlerAccessor) this.handler).getField_920().inventory.method_671(payload.stack()); + } +} diff --git a/src/test/java/net/modificationstation/sltest/network/NetworkListener.java b/src/test/java/net/modificationstation/sltest/network/NetworkListener.java new file mode 100644 index 000000000..145a984d7 --- /dev/null +++ b/src/test/java/net/modificationstation/sltest/network/NetworkListener.java @@ -0,0 +1,13 @@ +package net.modificationstation.sltest.network; + +import net.mine_diver.unsafeevents.listener.EventListener; +import net.minecraft.server.network.ServerPlayNetworkHandler; +import net.modificationstation.stationapi.api.event.network.payload.PayloadHandlerRegisterEvent; + +public class NetworkListener { + @EventListener + public void registerHandler(PayloadHandlerRegisterEvent event) { + if (event.networkHandler instanceof ServerPlayNetworkHandler handler) + event.register(ExamplePayload.TYPE, new ExamplePayloadHandler(handler)); + } +} diff --git a/src/test/java/net/modificationstation/sltest/option/OptionListener.java b/src/test/java/net/modificationstation/sltest/option/OptionListener.java index e18043f69..976017641 100644 --- a/src/test/java/net/modificationstation/sltest/option/OptionListener.java +++ b/src/test/java/net/modificationstation/sltest/option/OptionListener.java @@ -5,15 +5,19 @@ import net.mine_diver.unsafeevents.listener.EventListener; import net.minecraft.client.option.KeyBinding; import net.modificationstation.stationapi.api.client.event.option.KeyBindingRegisterEvent; +import org.lwjgl.input.Keyboard; public class OptionListener { @Environment(EnvType.CLIENT) @EventListener public void registerKeyBindings(KeyBindingRegisterEvent event) { - testBind = new KeyBinding("key.sltest.testBind", 21); + testBind = new KeyBinding("key.sltest.testBind", Keyboard.KEY_Y); + testBind2 = new KeyBinding("key.sltest.testBind2", Keyboard.KEY_U); event.keyBindings.add(testBind); + event.keyBindings.add(testBind2); } public static KeyBinding testBind; + public static KeyBinding testBind2; } diff --git a/src/test/resources/assets/sltest/stationapi/lang/en_US.lang b/src/test/resources/assets/sltest/stationapi/lang/en_US.lang index ad2425699..05278dbd7 100644 --- a/src/test/resources/assets/sltest/stationapi/lang/en_US.lang +++ b/src/test/resources/assets/sltest/stationapi/lang/en_US.lang @@ -1,6 +1,7 @@ item.@.testItem.name=Test Item item.@.idkSomething.name=IDK Something key.@.testBind=Diamonds! +key.@.testBind2=Duplicate! tile.@.testBlock.name=Test Block tile.@.testAnimatedBlock0.name=Test Animated Block tile.@.testAnimatedBlock1.name=Charged Animated Block diff --git a/src/test/resources/fabric.mod.json b/src/test/resources/fabric.mod.json index 43cbe9c22..7f8f5ce62 100644 --- a/src/test/resources/fabric.mod.json +++ b/src/test/resources/fabric.mod.json @@ -42,6 +42,9 @@ "net.modificationstation.sltest.texture.TextureListener", "net.modificationstation.sltest.render.entity.EntityRendererListener" ], + "stationapi:event_bus_server": [ + "net.modificationstation.sltest.network.NetworkListener" + ], "main": [ "net.modificationstation.sltest.MainTest" ], diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/client/network/StationClientNetworkHandler.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/client/network/StationClientNetworkHandler.java new file mode 100644 index 000000000..dc092c2d2 --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/client/network/StationClientNetworkHandler.java @@ -0,0 +1,17 @@ +package net.modificationstation.stationapi.api.client.network; + +import net.modificationstation.stationapi.api.network.PacketByteBuf; +import net.modificationstation.stationapi.api.network.packet.Payload; +import net.modificationstation.stationapi.api.network.packet.PayloadType; +import net.modificationstation.stationapi.api.util.Util; + +public interface StationClientNetworkHandler { + + default

> void sendPayload(P payload) { + sendPayload((PayloadType) payload.type(), payload); + } + + default

> void sendPayload(PayloadType type, P payload) { + Util.assertImpl(); + } +} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/event/network/payload/PayloadHandlerRegisterEvent.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/event/network/payload/PayloadHandlerRegisterEvent.java new file mode 100644 index 000000000..b8084f21c --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/event/network/payload/PayloadHandlerRegisterEvent.java @@ -0,0 +1,25 @@ +package net.modificationstation.stationapi.api.event.network.payload; + +import lombok.experimental.SuperBuilder; +import net.mine_diver.unsafeevents.Event; +import net.mine_diver.unsafeevents.event.EventPhases; +import net.minecraft.network.NetworkHandler; +import net.modificationstation.stationapi.api.StationAPI; +import net.modificationstation.stationapi.api.network.PacketByteBuf; +import net.modificationstation.stationapi.api.network.PayloadHandler; +import net.modificationstation.stationapi.api.network.packet.Payload; +import net.modificationstation.stationapi.api.network.packet.PayloadType; + +import java.util.Map; + +@SuperBuilder +@EventPhases(StationAPI.INTERNAL_PHASE) +public class PayloadHandlerRegisterEvent extends Event { + public final NetworkHandler networkHandler; + + public final Map>, PayloadHandler> handlers; + + public , H extends PayloadHandler> void register(PayloadType type, PayloadHandler handler) { + handlers.put(type, handler); + } +} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/PacketByteBuf.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/PacketByteBuf.java index b12b3cde5..5b7047dd3 100644 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/PacketByteBuf.java +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/PacketByteBuf.java @@ -37,9 +37,6 @@ public PacketByteBuf flip() { return this; } -// public PacketByteBuf resize(int newSize) { -// } - public PacketByteBuf slice(int index, int length) { return new PacketByteBuf(source.slice(index, length)); } diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/PacketSender.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/PacketSender.java new file mode 100644 index 000000000..8d8cfb7c2 --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/PacketSender.java @@ -0,0 +1,4 @@ +package net.modificationstation.stationapi.api.network; + +public interface PacketSender { +} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/PayloadHandler.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/PayloadHandler.java new file mode 100644 index 000000000..75f2a0a08 --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/PayloadHandler.java @@ -0,0 +1,7 @@ +package net.modificationstation.stationapi.api.network; + +import net.modificationstation.stationapi.api.network.packet.Payload; + +public interface PayloadHandler { + default void handle(Payload payload) {} +} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationConnection.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationConnection.java index 0e71875c1..89c746f5b 100644 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationConnection.java +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationConnection.java @@ -1,9 +1,12 @@ package net.modificationstation.stationapi.api.network; +import com.google.common.collect.ImmutableMap; +import lombok.Getter; import net.minecraft.network.Connection; import net.minecraft.network.NetworkHandler; import net.minecraft.network.packet.Packet; -import net.minecraft.network.packet.play.UpdateSignPacket; +import net.modificationstation.stationapi.api.StationAPI; +import net.modificationstation.stationapi.api.event.network.payload.PayloadHandlerRegisterEvent; import net.modificationstation.stationapi.api.network.packet.ManagedPacket; import net.modificationstation.stationapi.api.network.packet.Payload; import net.modificationstation.stationapi.api.network.packet.PayloadType; @@ -21,17 +24,20 @@ public class StationConnection extends Connection { private static final Object STATIONAPI$PACKET_READ_LOCK = new Object(); private static final AtomicBoolean STATIONAPI$BLOCKNG_PACKET = new AtomicBoolean(); - protected List incomingPayloads = Collections.synchronizedList(new ArrayList<>()); + @Getter + private Map>, PayloadHandler> handlers; + protected List> incomingPayloads = Collections.synchronizedList(new ArrayList<>()); protected List> outgoingPayloads = Collections.synchronizedList(new ArrayList<>()); protected List> outgoingPayloadsDelayed = Collections.synchronizedList(new ArrayList<>()); protected SocketChannel socketChannel; protected PacketByteBuf readBuffer; + private boolean blocking; public StationConnection(SocketChannel socket, String name, NetworkHandler networkHandler) { super(socket.socket(), name, networkHandler); + initHandlers(); this.socketChannel = socket; this.readBuffer = PacketByteBuf.pooled(); -// this.writeBuffer = PacketByteBuf.pooled(); // TODO make these virtual threads when we update to J21+ // this.field_1292 = new Thread(this::packetReadLoop, "Station" + name + " read thread"); @@ -40,6 +46,20 @@ public StationConnection(SocketChannel socket, String name, NetworkHandler netwo // this.field_1291.start(); } + @Override + public void method_1128(NetworkHandler networkHandler) { // setHandler + super.method_1128(networkHandler); + initHandlers(); + } + + public void initHandlers() { + Map>, PayloadHandler> handlers = new HashMap<>(); + + StationAPI.EVENT_BUS.post(PayloadHandlerRegisterEvent.builder().networkHandler(this.field_1289).handlers(handlers).build()); + + this.handlers = ImmutableMap.copyOf(handlers); + } + protected Function getPayloadDecoder() { return buf -> { int packetId = this.readBuffer.readByte() & 0xFF; // Apply bit mask to make sure range is between 0-255 @@ -60,7 +80,7 @@ protected

Consumer getPayloadEncoder(PayloadT }; } - public

void sendPayload(PayloadType type, P payload) { + public

> void sendPayload(PayloadType type, P payload) { if (!this.field_1290) { // this.closed Consumer encoder = getPayloadEncoder(type, payload); if (type.delayed()) @@ -91,27 +111,27 @@ public void method_1129() { // tick this.field_1296 = 0; } - int var1 = 100; + while (!this.incomingPayloads.isEmpty()) { + Payload payload = (Payload) this.incomingPayloads.remove(0); + var type = payload.type(); + payload.handle(this.handlers.get(type)); + if (type.blocking()) + // I have no idea why station api has this behavior, but we are on one thread now so we can't really be blocking the entire server network thread + blocking = false; + } + + int packetLimit = 100; - while (!this.field_1286.isEmpty() && var1-- >= 0) { + while (!this.field_1286.isEmpty() && packetLimit-- >= 0) { Packet packet = (Packet)this.field_1286.remove(0); if (packet instanceof ManagedPacket managedPacket) { packet.apply(managedPacket.getType().getHandler().orElse(this.field_1289)); - if (managedPacket.getType().blocking) { - synchronized (STATIONAPI$PACKET_READ_LOCK) { - STATIONAPI$BLOCKNG_PACKET.set(false); - STATIONAPI$PACKET_READ_LOCK.notifyAll(); - } - } + if (managedPacket.getType().blocking) + // I have no idea why station api has this behavior, but we are on one thread now so we can't really be blocking the entire server network thread + blocking = false; } else { - if (packet instanceof UpdateSignPacket updateSignPacket) { - if (updateSignPacket.x == 0 && updateSignPacket.y == -1 && updateSignPacket.z == 0) { - continue; - } - } packet.apply(this.field_1289); } - } this.method_1122(); @@ -172,15 +192,8 @@ protected boolean pollRead() { Packet packet = Packet.read(readBuffer.getInputStream(), this.field_1289.isServerSide()); if (packet != null) { field_1286.add(packet); - if (packet instanceof ManagedPacket managedPacket && managedPacket.getType().blocking) { - synchronized (STATIONAPI$PACKET_READ_LOCK) { - STATIONAPI$BLOCKNG_PACKET.set(true); - while (STATIONAPI$BLOCKNG_PACKET.get()) try { - STATIONAPI$PACKET_READ_LOCK.wait(); - } catch (InterruptedException ignored) { - } - } - } + if (packet instanceof ManagedPacket managedPacket) + blocking = managedPacket.getType().blocking; return true; } else { disconnect("disconnect.endOfStream"); @@ -188,7 +201,7 @@ protected boolean pollRead() { return false; } - public void packetWrite() { + public void writePackets() { try { if(this.field_1285) { // Poll packets til there is none left @@ -216,7 +229,9 @@ public void packetWrite() { } } - public void packetRead() { + public void readPackets() { + if (blocking) + return; try { if(this.field_1285 && !this.field_1290) { try { diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationMinecraftServer.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationMinecraftServer.java deleted file mode 100644 index fa2d5fe67..000000000 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationMinecraftServer.java +++ /dev/null @@ -1,9 +0,0 @@ -package net.modificationstation.stationapi.api.network; - -import net.modificationstation.stationapi.api.util.Util; - -public interface StationMinecraftServer { - default StationServerConnectionListener getStationConnectionListener() { - return Util.assertImpl(); - } -} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/codec/StreamCodec.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/codec/StreamCodec.java index 641016e81..f45894de0 100644 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/codec/StreamCodec.java +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/codec/StreamCodec.java @@ -7,4 +7,17 @@ * @param */ public interface StreamCodec extends StreamDecoder, StreamEncoder { + static StreamCodec ofMember(StreamMemberEncoder encoder, StreamDecoder decoder) { + return new StreamCodec<>() { + @Override + public V decode(B buf) { + return decoder.decode(buf); + } + + @Override + public void encode(B buf, V obj) { + encoder.encode(obj, buf); + } + }; + } } diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/codec/StreamCodecs.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/codec/StreamCodecs.java index 562da50ff..ea1c8a3d8 100644 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/codec/StreamCodecs.java +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/codec/StreamCodecs.java @@ -1,5 +1,6 @@ package net.modificationstation.stationapi.api.network.codec; +import net.minecraft.item.ItemStack; import net.modificationstation.stationapi.api.network.PacketByteBuf; /** @@ -15,4 +16,5 @@ public void encode(PacketByteBuf buf, Boolean bool) { buf.writeBoolean(bool); } }; + } diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/codec/StreamDecoder.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/codec/StreamDecoder.java index 8b9a07ce3..fa0952ca2 100644 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/codec/StreamDecoder.java +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/codec/StreamDecoder.java @@ -5,6 +5,7 @@ * @param The deserializer that decodes the object. * @param The object that will get deserialized. */ +@FunctionalInterface public interface StreamDecoder { T decode(I decoder); } diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/codec/StreamEncoder.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/codec/StreamEncoder.java index 4d66c56fc..4778ae73d 100644 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/codec/StreamEncoder.java +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/codec/StreamEncoder.java @@ -5,6 +5,7 @@ * @param The serializer use to serialize the object. * @param The object to serialize. */ +@FunctionalInterface public interface StreamEncoder { void encode(S encoder, T obj); } diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/codec/StreamMemberEncoder.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/codec/StreamMemberEncoder.java new file mode 100644 index 000000000..3e9a9862e --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/codec/StreamMemberEncoder.java @@ -0,0 +1,6 @@ +package net.modificationstation.stationapi.api.network.codec; + +@FunctionalInterface +public interface StreamMemberEncoder { + void encode(T obj, O encoder); +} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/packet/PacketHelper.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/packet/PacketHelper.java index e7e1aec07..766d41aa8 100644 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/packet/PacketHelper.java +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/packet/PacketHelper.java @@ -4,6 +4,7 @@ import net.minecraft.entity.player.PlayerEntity; import net.minecraft.network.packet.Packet; import net.modificationstation.stationapi.api.network.PacketByteBuf; +import net.modificationstation.stationapi.api.network.PayloadHandler; import net.modificationstation.stationapi.api.util.API; import net.modificationstation.stationapi.api.util.Identifier; import net.modificationstation.stationapi.api.util.SideUtil; @@ -24,10 +25,6 @@ public final class PacketHelper { @SuppressWarnings("Convert2MethodRef") // Method references load their target classes on both sides, causing crashes. private static final PacketHelperImpl INSTANCE = SideUtil.get(() -> new PacketHelperClientImpl(), () -> new PacketHelperServerImpl()); - public static

Packet createPayloadPacket(PayloadType type, P payload) { - return null; - } - /** * On client, sends the packet to the server if the current game is multiplayer, or handles the packet locally if the current game is singleplayer. * On server, handles the packet locally. @@ -38,6 +35,16 @@ public static void send(Packet packet) { INSTANCE.send(packet); } + /** + * On client, sends the payload to the server if the current game is multiplayer, or handles the packet locally if the current game is singleplayer. + * On server, handles the payload locally. + * @param payload the payload to send/handle. + */ + @API + public static void send(Payload payload) { + INSTANCE.send(payload); + } + /** * On client, ignores the packet if the current game is multiplayer, or handles the packet locally if the current game is singleplayer. * On server, sends the packet to the player's client. @@ -49,6 +56,17 @@ public static void sendTo(PlayerEntity player, Packet packet) { INSTANCE.sendTo(player, packet); } + /** + * On client, ignores the payload if the current game is multiplayer, or handles the payload locally if the current game is singleplayer. + * On server, sends the payload to the player's client. + * @param player the player to send the payload to. + * @param payload the payload to send/handle. + */ + @API + public static void sendTo(PlayerEntity player, Payload payload) { + INSTANCE.sendTo(player, payload); + } + /** * On client, ignores the packet if the current game is multiplayer, or handles the packet locally if the current game is singleplayer. * On server, both handles the packet locally and sends the packet to the player's client. @@ -60,6 +78,17 @@ public static void dispatchLocallyAndSendTo(PlayerEntity player, Packet packet) INSTANCE.dispatchLocallyAndSendTo(player, packet); } + /** + * On client, ignores the payload if the current game is multiplayer, or handles the payload locally if the current game is singleplayer. + * On server, both handles the payload locally and sends the payload to the player's client. + * @param player the player to send the payload to. + * @param payload the payload to send/handle. + */ + @API + public static void dispatchLocallyAndSendTo(PlayerEntity player, Payload payload) { + INSTANCE.dispatchLocallyAndSendTo(player, payload); + } + /** * On client, ignores the packet if the current game is multiplayer, or handles the packet locally if the current game is singleplayer. * On server, sends the packet to all players tracking the given entity. @@ -71,6 +100,17 @@ public static void sendToAllTracking(Entity entity, Packet packet) { INSTANCE.sendToAllTracking(entity, packet); } + /** + * On client, ignores the payload if the current game is multiplayer, or handles the payload locally if the current game is singleplayer. + * On server, sends the payload to all players tracking the given entity. + * @param entity the entity whose tracking players to send the payload to. + * @param payload the payload to send/handle. + */ + @API + public static void sendToAllTracking(Entity entity, Payload payload) { + INSTANCE.sendToAllTracking(entity, payload); + } + /** * On client, ignores the packet if the current game is multiplayer, or handles the packet locally if the current game is singleplayer. * On server, both handles the packet locally and sends the packet to all players tracking the given entity. @@ -82,6 +122,17 @@ public static void dispatchLocallyAndToAllTracking(Entity entity, Packet packet) INSTANCE.dispatchLocallyAndToAllTracking(entity, packet); } + /** + * On client, ignores the payload if the current game is multiplayer, or handles the payload locally if the current game is singleplayer. + * On server, both handles the payload locally and sends the payload to all players tracking the given entity. + * @param entity the entity whose tracking players to send the payload to. + * @param payload the payload to send/handle. + */ + @API + public static void dispatchLocallyAndToAllTracking(Entity entity, Payload payload) { + INSTANCE.dispatchLocallyAndToAllTracking(entity, payload); + } + /** * Registers the given packet. * diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/packet/Payload.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/packet/Payload.java index 9fe6fcdb1..76b8c65a0 100644 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/packet/Payload.java +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/packet/Payload.java @@ -1,11 +1,12 @@ package net.modificationstation.stationapi.api.network.packet; import net.modificationstation.stationapi.api.network.PacketByteBuf; +import net.modificationstation.stationapi.api.network.PayloadHandler; -public interface Payload { - int PACKET_ID = 253; +public interface Payload { + int PACKET_ID = 252; PayloadType type(); - void apply(HANDLER handler); + void handle(H handler); } diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/packet/PayloadType.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/packet/PayloadType.java index 1a4ecd533..3b92dec27 100644 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/packet/PayloadType.java +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/packet/PayloadType.java @@ -4,16 +4,32 @@ import net.modificationstation.stationapi.api.network.codec.StreamCodec; import net.modificationstation.stationapi.api.util.Identifier; +import java.util.Objects; + /** * Represents a payload type sent over the network. * @param id The identifier of the payload. * @param codec Represents how your payload should be encoded/decoded over the network. * @param delayed An option to be handled last after all other packets, this is equivalent to {@link net.minecraft.network.packet.Packet#worldPacket}. + * @param blocking * @param The context buffer used to serialize and deserialize the payload. * @param The actual payload that will be sent over the network. */ -public record PayloadType(Identifier id, StreamCodec codec, boolean delayed) { +public record PayloadType(Identifier id, StreamCodec codec, boolean delayed, boolean blocking) { public PayloadType(Identifier id, StreamCodec codec) { - this(id, codec, false); + this(id, codec, false, false); + } + + @Override + public boolean equals(Object obj) { + if (obj instanceof PayloadType other) { + return this.id.equals(other.id) && this.delayed == other.delayed; + } + return false; + } + + @Override + public int hashCode() { + return Objects.hash(id, delayed); } } diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/registry/PayloadTypeRegistry.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/registry/PayloadTypeRegistry.java index 35ae08020..ae68f7992 100644 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/registry/PayloadTypeRegistry.java +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/registry/PayloadTypeRegistry.java @@ -14,7 +14,7 @@ public class PayloadTypeRegistry extends SimpleRegistry> INSTANCE = Registries.create(KEY, new PayloadTypeRegistry(), Lifecycle.experimental()); private PayloadTypeRegistry() { - super(KEY, Lifecycle.experimental(), true); + super(KEY, Lifecycle.experimental(), false); RegistryAttributeHolder.get(this).addAttribute(RegistryAttribute.SYNCED); } } diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/server/StationEntityTracker.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/server/StationEntityTracker.java new file mode 100644 index 000000000..5775ce58c --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/server/StationEntityTracker.java @@ -0,0 +1,20 @@ +package net.modificationstation.stationapi.api.server; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.entity.Entity; +import net.modificationstation.stationapi.api.network.PacketByteBuf; +import net.modificationstation.stationapi.api.network.packet.Payload; +import net.modificationstation.stationapi.api.network.packet.PayloadType; +import net.modificationstation.stationapi.api.util.Util; + +@Environment(EnvType.SERVER) +public interface StationEntityTracker { + default void sendToListeners(Entity entity, PayloadType type, T payload) { + Util.assertImpl(); + } + + default void sendToAround(Entity entity, PayloadType type, T payload) { + Util.assertImpl(); + } +} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/server/StationEntityTrackerEntry.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/server/StationEntityTrackerEntry.java new file mode 100644 index 000000000..dc6b6a8a8 --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/server/StationEntityTrackerEntry.java @@ -0,0 +1,19 @@ +package net.modificationstation.stationapi.api.server; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.modificationstation.stationapi.api.network.PacketByteBuf; +import net.modificationstation.stationapi.api.network.packet.Payload; +import net.modificationstation.stationapi.api.network.packet.PayloadType; +import net.modificationstation.stationapi.api.util.Util; + +@Environment(EnvType.SERVER) +public interface StationEntityTrackerEntry { + default void sendToListeners(PayloadType type, T payload) { + Util.assertImpl(); + } + + default void sendToAround(PayloadType type, T payload) { + Util.assertImpl(); + } +} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/server/StationMinecraftServer.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/server/StationMinecraftServer.java new file mode 100644 index 000000000..f91e55f67 --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/server/StationMinecraftServer.java @@ -0,0 +1,13 @@ +package net.modificationstation.stationapi.api.server; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.modificationstation.stationapi.api.server.network.StationServerConnectionListener; +import net.modificationstation.stationapi.api.util.Util; + +@Environment(EnvType.SERVER) +public interface StationMinecraftServer { + default StationServerConnectionListener getStationConnectionListener() { + return Util.assertImpl(); + } +} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/server/network/PlayerLookup.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/server/network/PlayerLookup.java new file mode 100644 index 000000000..7812e813c --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/server/network/PlayerLookup.java @@ -0,0 +1,36 @@ +package net.modificationstation.stationapi.api.server.network; + +import net.minecraft.entity.player.ServerPlayerEntity; +import net.minecraft.server.MinecraftServer; + +import java.util.Collection; +import java.util.Collections; +import java.util.Objects; + +/** + * Helper methods to lookup players in a server. + * + *

The word "tracking" means that an entity/chunk on the server is known to a player's client (within in view distance) and the (block) entity should notify tracking clients of changes. + * + *

These methods should only be called on the server thread and only be used on logical a server. + */ +public class PlayerLookup { + /** + * Gets all the players on the minecraft server. + * + *

The returned collection is immutable. + * + * @param server the server + * @return all players on the server + */ + public static Collection all(MinecraftServer server) { + Objects.requireNonNull(server, "The server cannot be null"); + + // return an immutable collection to guard against accidental removals. + if (server.field_2842 != null) { + return Collections.unmodifiableCollection(server.field_2842.field_578); + } + + return Collections.emptyList(); + } +} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/server/network/ServerLoginNetworking.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/server/network/ServerLoginNetworking.java new file mode 100644 index 000000000..7a77229a3 --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/server/network/ServerLoginNetworking.java @@ -0,0 +1,32 @@ +package net.modificationstation.stationapi.api.server.network; + +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.network.ServerLoginNetworkHandler; +import net.modificationstation.stationapi.api.network.PacketByteBuf; +import net.modificationstation.stationapi.api.network.PacketSender; +import net.modificationstation.stationapi.api.network.packet.Payload; +import net.modificationstation.stationapi.api.network.packet.PayloadType; +import org.jetbrains.annotations.ApiStatus; + +public class ServerLoginNetworking { + public static void registerGlobalReceiver(PayloadType type, LoginPayloadHandler handler) { + + } + + public void registerReceiver(ServerLoginNetworkHandler networkHandler, PayloadType type, LoginPayloadHandler handler) { + + } + + public interface LoginPayloadHandler { + void receive(T payload, Context context); + } + + @ApiStatus.NonExtendable + public interface Context { + MinecraftServer server(); + + ServerLoginNetworkHandler networkHandler(); + + PacketSender responseSender(); + } +} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationServerConnectionListener.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/server/network/StationServerConnectionListener.java similarity index 75% rename from station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationServerConnectionListener.java rename to station-networking-v0/src/main/java/net/modificationstation/stationapi/api/server/network/StationServerConnectionListener.java index fcde16c64..a3feb5bdd 100644 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/network/StationServerConnectionListener.java +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/server/network/StationServerConnectionListener.java @@ -1,4 +1,4 @@ -package net.modificationstation.stationapi.api.network; +package net.modificationstation.stationapi.api.server.network; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; @@ -6,6 +6,7 @@ import net.minecraft.server.MinecraftServer; import net.minecraft.server.network.ServerLoginNetworkHandler; import net.minecraft.server.network.ServerPlayNetworkHandler; +import net.modificationstation.stationapi.api.network.StationConnection; import net.modificationstation.stationapi.impl.network.server.StationServerLoginNetworkHandler; import java.io.IOException; @@ -66,26 +67,30 @@ public void listen() { Iterator iterator = keys.iterator(); while (iterator.hasNext()) { SelectionKey key = iterator.next(); - if (key.isValid() && key.isAcceptable()) { - SocketChannel socket = this.socketChannel.accept(); - socket.setOption(StandardSocketOptions.TCP_NODELAY, true); // Vanilla doesn't do this, but I think it can improve networking performance, Probably look into RFC1122 which the java doc refers to - - var pendingConnection = new StationServerLoginNetworkHandler(this.server, socket, "Connection #" + this.connectionCounter++); - socket.configureBlocking(false); - socket.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE, pendingConnection.connection); - addPendingConnection(pendingConnection); - } + try { + if (key.isValid() && key.isAcceptable()) { + SocketChannel socket = this.socketChannel.accept(); + socket.setOption(StandardSocketOptions.TCP_NODELAY, true); // Vanilla doesn't do this, but I think it can improve networking performance, Probably look into RFC1122 which the java doc refers to + + var pendingConnection = new StationServerLoginNetworkHandler(this.server, socket, "Connection #" + this.connectionCounter++); + socket.configureBlocking(false); + socket.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE, pendingConnection.connection); + addPendingConnection(pendingConnection); + } - if (key.isValid() && key.isReadable()) { - if (key.attachment() instanceof StationConnection connection) { - connection.packetRead(); + if (key.isValid() && key.isReadable()) { + if (key.attachment() instanceof StationConnection connection) { + connection.readPackets(); + } } - } - if (key.isValid() && key.isWritable()) { - if (key.attachment() instanceof StationConnection connection) { - connection.packetWrite(); + if (key.isValid() && key.isWritable()) { + if (key.attachment() instanceof StationConnection connection) { + connection.writePackets(); + } } + } catch (Exception e) { + e.printStackTrace(); } iterator.remove(); diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/server/network/StationServerPlayNetworkHandler.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/server/network/StationServerPlayNetworkHandler.java new file mode 100644 index 000000000..5644ba35f --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/api/server/network/StationServerPlayNetworkHandler.java @@ -0,0 +1,15 @@ +package net.modificationstation.stationapi.api.server.network; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.modificationstation.stationapi.api.network.PacketByteBuf; +import net.modificationstation.stationapi.api.network.packet.Payload; +import net.modificationstation.stationapi.api.network.packet.PayloadType; +import net.modificationstation.stationapi.api.util.Util; + +@Environment(EnvType.SERVER) +public interface StationServerPlayNetworkHandler { + default void sendPayload(PayloadType type, T payload) { + Util.assertImpl(); + } +} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/impl/client/network/packet/PacketHelperClientImpl.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/impl/client/network/packet/PacketHelperClientImpl.java index b5a436139..212293253 100644 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/impl/client/network/packet/PacketHelperClientImpl.java +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/impl/client/network/packet/PacketHelperClientImpl.java @@ -4,9 +4,16 @@ import net.minecraft.client.Minecraft; import net.minecraft.entity.Entity; import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.network.Connection; import net.minecraft.network.packet.Packet; +import net.modificationstation.stationapi.api.network.PacketByteBuf; +import net.modificationstation.stationapi.api.network.PayloadHandler; +import net.modificationstation.stationapi.api.network.StationConnection; import net.modificationstation.stationapi.api.network.packet.ManagedPacket; +import net.modificationstation.stationapi.api.network.packet.Payload; +import net.modificationstation.stationapi.api.network.packet.PayloadType; import net.modificationstation.stationapi.impl.network.packet.PacketHelperImpl; +import net.modificationstation.stationapi.mixin.network.client.ClientNetworkHandlerAccessor; public class PacketHelperClientImpl extends PacketHelperImpl { @Override @@ -39,4 +46,38 @@ public void sendToAllTracking(Entity entity, Packet packet) { public void dispatchLocallyAndToAllTracking(Entity entity, Packet packet) { sendToAllTracking(entity, packet); } + + @Override + public void send(PayloadType> type, Payload payload) { + //noinspection deprecation + Minecraft minecraft = (Minecraft) FabricLoader.getInstance().getGameInstance(); + if (minecraft.world.isRemote) { + minecraft.getNetworkHandler().sendPayload(type, payload); + } else { + StationConnection connection = (StationConnection) ((ClientNetworkHandlerAccessor) minecraft.getNetworkHandler()).stationapi_getConnection(); + ((Payload) payload).handle(connection.getHandlers().get(type)); + } + } + + @Override + public void sendTo(PlayerEntity player, PayloadType> type, Payload payload) { + if (!player.world.isRemote) + ((Payload) payload).handle(((StationConnection) ((ClientNetworkHandlerAccessor) ((Minecraft) FabricLoader.getInstance().getGameInstance()).getNetworkHandler()).stationapi_getConnection()).getHandlers().get(type)); + } + + @Override + public void dispatchLocallyAndSendTo(PlayerEntity player, PayloadType> type, Payload payload) { + sendTo(player, type, payload); + } + + @Override + public void sendToAllTracking(Entity entity, PayloadType> type, Payload payload) { + //noinspection deprecation + sendTo(((Minecraft) FabricLoader.getInstance().getGameInstance()).player, type, payload); + } + + @Override + public void dispatchLocallyAndToAllTracking(Entity entity, PayloadType> type, Payload payload) { + sendToAllTracking(entity, type, payload); + } } diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/impl/network/packet/PacketHelperImpl.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/impl/network/packet/PacketHelperImpl.java index e36045d11..88bace69d 100644 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/impl/network/packet/PacketHelperImpl.java +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/impl/network/packet/PacketHelperImpl.java @@ -3,6 +3,10 @@ import net.minecraft.entity.Entity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.network.packet.Packet; +import net.modificationstation.stationapi.api.network.PacketByteBuf; +import net.modificationstation.stationapi.api.network.PayloadHandler; +import net.modificationstation.stationapi.api.network.packet.Payload; +import net.modificationstation.stationapi.api.network.packet.PayloadType; public abstract class PacketHelperImpl { public abstract void send(Packet packet); @@ -14,4 +18,34 @@ public abstract class PacketHelperImpl { public abstract void sendToAllTracking(Entity entity, Packet packet); public abstract void dispatchLocallyAndToAllTracking(Entity entity, Packet packet); + + public abstract void send(PayloadType> type, Payload payload); + + public abstract void sendTo(PlayerEntity player, PayloadType> type, Payload payload); + + public abstract void dispatchLocallyAndSendTo(PlayerEntity player, PayloadType> type, Payload payload); + + public abstract void sendToAllTracking(Entity entity, PayloadType> type, Payload payload); + + public abstract void dispatchLocallyAndToAllTracking(Entity entity, PayloadType> type, Payload payload); + + public void send(Payload payload) { + send((PayloadType>) payload.type(), payload); + } + + public void sendTo(PlayerEntity player, Payload payload) { + sendTo(player, (PayloadType>) payload.type(), payload); + } + + public void dispatchLocallyAndSendTo(PlayerEntity player, Payload payload) { + dispatchLocallyAndSendTo(player, (PayloadType>) payload.type(), payload); + } + + public void sendToAllTracking(Entity entity, Payload payload) { + sendToAllTracking(entity, (PayloadType>) payload.type(), payload); + } + + public void dispatchLocallyAndToAllTracking(Entity entity, Payload payload) { + dispatchLocallyAndToAllTracking(entity, (PayloadType>) payload.type(), payload); + } } diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/impl/network/server/StationServerLoginNetworkHandler.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/impl/network/server/StationServerLoginNetworkHandler.java index 843512111..a695dc745 100644 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/impl/network/server/StationServerLoginNetworkHandler.java +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/impl/network/server/StationServerLoginNetworkHandler.java @@ -4,7 +4,6 @@ import net.minecraft.server.network.ServerLoginNetworkHandler; import net.modificationstation.stationapi.api.network.StationConnection; -import java.net.Socket; import java.nio.channels.SocketChannel; public class StationServerLoginNetworkHandler extends ServerLoginNetworkHandler { diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/impl/server/network/packet/PacketHelperServerImpl.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/impl/server/network/packet/PacketHelperServerImpl.java index 7be48b4cf..ee84670f6 100644 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/impl/server/network/packet/PacketHelperServerImpl.java +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/impl/server/network/packet/PacketHelperServerImpl.java @@ -6,7 +6,11 @@ import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.network.packet.Packet; import net.minecraft.server.MinecraftServer; +import net.modificationstation.stationapi.api.network.PacketByteBuf; +import net.modificationstation.stationapi.api.network.PayloadHandler; import net.modificationstation.stationapi.api.network.packet.ManagedPacket; +import net.modificationstation.stationapi.api.network.packet.Payload; +import net.modificationstation.stationapi.api.network.packet.PayloadType; import net.modificationstation.stationapi.impl.network.packet.PacketHelperImpl; public class PacketHelperServerImpl extends PacketHelperImpl { @@ -42,4 +46,33 @@ public void dispatchLocallyAndToAllTracking(Entity entity, Packet packet) { send(packet); sendToAllTracking(entity, packet); } + + @Override + public void send(PayloadType> type, Payload payload) { + payload.handle(null); // TODO: implement proper packet circuiting for payload handlers + } + + @Override + public void sendTo(PlayerEntity player, PayloadType> type, Payload payload) { + ((ServerPlayerEntity) player).field_255.sendPayload(type, payload); + } + + @Override + public void dispatchLocallyAndSendTo(PlayerEntity player, PayloadType> type, Payload payload) { + send(type, payload); + sendTo(player, type, payload); + } + + @Override + public void sendToAllTracking(Entity entity, PayloadType> type, Payload payload) { + //noinspection deprecation + ((MinecraftServer) FabricLoader.getInstance().getGameInstance()) + .method_2165(entity.world.dimension.id).sendToAround(entity, type, payload); + } + + @Override + public void dispatchLocallyAndToAllTracking(Entity entity, PayloadType> type, Payload payload) { + send(type, payload); + sendToAllTracking(entity, type, payload); + } } diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/ServerConnectionListenerMixin.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/ServerConnectionListenerMixin.java deleted file mode 100644 index 8736923bd..000000000 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/ServerConnectionListenerMixin.java +++ /dev/null @@ -1,19 +0,0 @@ -package net.modificationstation.stationapi.mixin.network; - -import com.llamalad7.mixinextras.injector.wrapoperation.Operation; -import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import net.minecraft.class_9; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; - -import java.io.IOException; -import java.net.InetAddress; -import java.net.ServerSocket; - -@Mixin(class_9.class) -public class ServerConnectionListenerMixin { -// @WrapOperation(method = "", at = @At(value = "NEW", target = "(IILjava/net/InetAddress;)Ljava/net/ServerSocket;")) -// private ServerSocket createServerSocket(int port, int backlog, InetAddress address, Operation original) throws IOException { -// return null; -// } -} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/client/ClientNetworkHandlerAccessor.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/client/ClientNetworkHandlerAccessor.java new file mode 100644 index 000000000..e3c2953b7 --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/client/ClientNetworkHandlerAccessor.java @@ -0,0 +1,12 @@ +package net.modificationstation.stationapi.mixin.network.client; + +import net.minecraft.client.network.ClientNetworkHandler; +import net.minecraft.network.Connection; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(ClientNetworkHandler.class) +public interface ClientNetworkHandlerAccessor { + @Accessor("connection") + Connection stationapi_getConnection(); +} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/client/ClientNetworkHandlerMixin.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/client/ClientNetworkHandlerMixin.java index 7d57e2271..777019734 100644 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/client/ClientNetworkHandlerMixin.java +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/client/ClientNetworkHandlerMixin.java @@ -7,7 +7,11 @@ import net.minecraft.client.network.ClientNetworkHandler; import net.minecraft.network.Connection; import net.minecraft.network.NetworkHandler; +import net.modificationstation.stationapi.api.client.network.StationClientNetworkHandler; +import net.modificationstation.stationapi.api.network.PacketByteBuf; import net.modificationstation.stationapi.api.network.StationConnection; +import net.modificationstation.stationapi.api.network.packet.Payload; +import net.modificationstation.stationapi.api.network.packet.PayloadType; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @@ -23,7 +27,7 @@ import java.util.Set; @Mixin(ClientNetworkHandler.class) -public abstract class ClientNetworkHandlerMixin { +public abstract class ClientNetworkHandlerMixin implements StationClientNetworkHandler { @Shadow private boolean disconnected; @Shadow private Connection connection; @@ -52,6 +56,13 @@ private Connection useStationConnection(Socket oldSocket, String name, NetworkHa return new StationConnection(socket, "Station " + name, networkHandler); } + @Override + public

> void sendPayload(PayloadType type, P payload) { + if (!this.disconnected) { + ((StationConnection) this.connection).sendPayload(type, payload); + } + } + private void stationapi_listen() { while (!this.disconnected) { if (connection == null) @@ -65,11 +76,11 @@ private void stationapi_listen() { SelectionKey key = iterator.next(); if (key.isValid() && key.isReadable()) { - ((StationConnection) this.connection).packetRead(); + ((StationConnection) this.connection).readPackets(); } if (key.isValid() && key.isWritable()) { - ((StationConnection) this.connection).packetWrite(); + ((StationConnection) this.connection).writePackets(); } iterator.remove(); } diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/server/EntityTrackerEntryMixin.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/server/EntityTrackerEntryMixin.java new file mode 100644 index 000000000..f1dac3622 --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/server/EntityTrackerEntryMixin.java @@ -0,0 +1,35 @@ +package net.modificationstation.stationapi.mixin.network.server; + +import net.minecraft.class_174; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.ServerPlayerEntity; +import net.modificationstation.stationapi.api.network.PacketByteBuf; +import net.modificationstation.stationapi.api.network.packet.Payload; +import net.modificationstation.stationapi.api.network.packet.PayloadType; +import net.modificationstation.stationapi.api.server.StationEntityTrackerEntry; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +import java.util.Set; + +@Mixin(class_174.class) +public class EntityTrackerEntryMixin implements StationEntityTrackerEntry { + @Shadow public Set field_610; + + @Shadow public Entity field_597; + + @Override + public void sendToListeners(PayloadType type, T payload) { + for (ServerPlayerEntity player : this.field_610) { + player.field_255.sendPayload(type, payload); + } + } + + @Override + public void sendToAround(PayloadType type, T payload) { + this.sendToListeners(type, payload); + if (this.field_597 instanceof ServerPlayerEntity player) { + player.field_255.sendPayload(type, payload); + } + } +} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/server/EntityTrackerMixin.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/server/EntityTrackerMixin.java new file mode 100644 index 000000000..00ff47667 --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/server/EntityTrackerMixin.java @@ -0,0 +1,33 @@ +package net.modificationstation.stationapi.mixin.network.server; + +import net.minecraft.class_174; +import net.minecraft.class_488; +import net.minecraft.class_80; +import net.minecraft.entity.Entity; +import net.modificationstation.stationapi.api.network.PacketByteBuf; +import net.modificationstation.stationapi.api.network.packet.Payload; +import net.modificationstation.stationapi.api.network.packet.PayloadType; +import net.modificationstation.stationapi.api.server.StationEntityTracker; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(class_488.class) +public class EntityTrackerMixin implements StationEntityTracker { + @Shadow private class_80 field_2005; + + @Override + public void sendToListeners(Entity entity, PayloadType type, T payload) { + class_174 entry = (class_174) this.field_2005.method_772(entity.id); + if (entry != null) { + entry.sendToListeners(type, payload); + } + } + + @Override + public void sendToAround(Entity entity, PayloadType type, T payload) { + class_174 entry = (class_174)this.field_2005.method_772(entity.id); + if (entry != null) { + entry.sendToAround(type, payload); + } + } +} diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/MinecraftServerMixin.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/server/MinecraftServerMixin.java similarity index 84% rename from station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/MinecraftServerMixin.java rename to station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/server/MinecraftServerMixin.java index 364212848..54e1bcff2 100644 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/MinecraftServerMixin.java +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/server/MinecraftServerMixin.java @@ -1,12 +1,11 @@ -package net.modificationstation.stationapi.mixin.network; +package net.modificationstation.stationapi.mixin.network.server; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation; -import com.llamalad7.mixinextras.sugar.Local; import net.minecraft.class_9; import net.minecraft.server.MinecraftServer; -import net.modificationstation.stationapi.api.network.StationMinecraftServer; -import net.modificationstation.stationapi.api.network.StationServerConnectionListener; +import net.modificationstation.stationapi.api.server.StationMinecraftServer; +import net.modificationstation.stationapi.api.server.network.StationServerConnectionListener; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/ServerLoginNetworkHandlerMixin.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/server/ServerLoginNetworkHandlerMixin.java similarity index 96% rename from station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/ServerLoginNetworkHandlerMixin.java rename to station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/server/ServerLoginNetworkHandlerMixin.java index eb5fb96b4..18720e5a6 100644 --- a/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/ServerLoginNetworkHandlerMixin.java +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/server/ServerLoginNetworkHandlerMixin.java @@ -1,4 +1,4 @@ -package net.modificationstation.stationapi.mixin.network; +package net.modificationstation.stationapi.mixin.network.server; import com.llamalad7.mixinextras.injector.v2.WrapWithCondition; import com.llamalad7.mixinextras.injector.wrapoperation.Operation; diff --git a/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/server/ServerPlayNetworkHandlerMixin.java b/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/server/ServerPlayNetworkHandlerMixin.java new file mode 100644 index 000000000..ae092c8ba --- /dev/null +++ b/station-networking-v0/src/main/java/net/modificationstation/stationapi/mixin/network/server/ServerPlayNetworkHandlerMixin.java @@ -0,0 +1,26 @@ +package net.modificationstation.stationapi.mixin.network.server; + +import net.minecraft.network.Connection; +import net.minecraft.server.network.ServerPlayNetworkHandler; +import net.modificationstation.stationapi.api.network.PacketByteBuf; +import net.modificationstation.stationapi.api.network.StationConnection; +import net.modificationstation.stationapi.api.network.packet.Payload; +import net.modificationstation.stationapi.api.network.packet.PayloadType; +import net.modificationstation.stationapi.api.server.network.StationServerPlayNetworkHandler; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(ServerPlayNetworkHandler.class) +public class ServerPlayNetworkHandlerMixin implements StationServerPlayNetworkHandler { + @Shadow public Connection field_917; + + @Shadow private int field_922; + + @Shadow private int field_921; + + @Override + public void sendPayload(PayloadType type, T payload) { + ((StationConnection) this.field_917).sendPayload(type, payload); + this.field_922 = this.field_921; + } +} diff --git a/station-networking-v0/src/main/resources/fabric.mod.json b/station-networking-v0/src/main/resources/fabric.mod.json index c61cf0a67..aeb60a676 100644 --- a/station-networking-v0/src/main/resources/fabric.mod.json +++ b/station-networking-v0/src/main/resources/fabric.mod.json @@ -39,8 +39,20 @@ "custom": { "modmenu:api": true, "loom:injected_interfaces": { + "net/minecraft/client/network/ClientNetworkHandler": [ + "net/modificationstation/stationapi/api/client/network/StationClientNetworkHandler" + ], "net/minecraft/server/MinecraftServer": [ - "net/modificationstation/stationapi/api/network/StationMinecraftServer" + "net/modificationstation/stationapi/api/server/StationMinecraftServer" + ], + "net/minecraft/server/network/ServerPlayNetworkHandler": [ + "net/modificationstation/stationapi/api/server/network/StationServerPlayNetworkHandler" + ], + "net/minecraft/class_488": [ + "net/modificationstation/stationapi/api/server/StationEntityTracker" + ], + "net/minecraft/class_174": [ + "net/modificationstation/stationapi/api/server/StationEntityTrackerEntry" ] } } diff --git a/station-networking-v0/src/main/resources/station-networking-v0.mixins.json b/station-networking-v0/src/main/resources/station-networking-v0.mixins.json index e390d2c2f..049d717fb 100644 --- a/station-networking-v0/src/main/resources/station-networking-v0.mixins.json +++ b/station-networking-v0/src/main/resources/station-networking-v0.mixins.json @@ -13,11 +13,14 @@ "defaultRequire": 1 }, "server": [ - "MinecraftServerMixin", - "ServerConnectionListenerMixin", - "ServerLoginNetworkHandlerMixin" + "server.EntityTrackerEntryMixin", + "server.EntityTrackerMixin", + "server.MinecraftServerMixin", + "server.ServerLoginNetworkHandlerMixin", + "server.ServerPlayNetworkHandlerMixin" ], "client": [ + "client.ClientNetworkHandlerAccessor", "client.ClientNetworkHandlerMixin" ] } diff --git a/station-vanilla-checker-v0/src/main/java/net/modificationstation/stationapi/mixin/network/server/ServerLoginNetworkHandlerMixin.java b/station-vanilla-checker-v0/src/main/java/net/modificationstation/stationapi/mixin/network/server/CheckerServerLoginNetworkHandlerMixin.java similarity index 97% rename from station-vanilla-checker-v0/src/main/java/net/modificationstation/stationapi/mixin/network/server/ServerLoginNetworkHandlerMixin.java rename to station-vanilla-checker-v0/src/main/java/net/modificationstation/stationapi/mixin/network/server/CheckerServerLoginNetworkHandlerMixin.java index 1298793a6..2c8d031ff 100644 --- a/station-vanilla-checker-v0/src/main/java/net/modificationstation/stationapi/mixin/network/server/ServerLoginNetworkHandlerMixin.java +++ b/station-vanilla-checker-v0/src/main/java/net/modificationstation/stationapi/mixin/network/server/CheckerServerLoginNetworkHandlerMixin.java @@ -15,7 +15,7 @@ import org.spongepowered.asm.mixin.injection.callback.LocalCapture; @Mixin(ServerLoginNetworkHandler.class) -class ServerLoginNetworkHandlerMixin { +class CheckerServerLoginNetworkHandlerMixin { @Inject( method = "accept", at = @At( diff --git a/station-vanilla-checker-v0/src/main/resources/station-vanilla-checker-v0.mixins.json b/station-vanilla-checker-v0/src/main/resources/station-vanilla-checker-v0.mixins.json index 394ad5d28..2a0f0fc62 100644 --- a/station-vanilla-checker-v0/src/main/resources/station-vanilla-checker-v0.mixins.json +++ b/station-vanilla-checker-v0/src/main/resources/station-vanilla-checker-v0.mixins.json @@ -8,7 +8,7 @@ "NetworkHandlerMixin" ], "server": [ - "server.ServerLoginNetworkHandlerMixin" + "server.CheckerServerLoginNetworkHandlerMixin" ], "client": [ ],