diff --git a/src/main/java/ru/bclib/api/dataexchange/Connector.java b/src/main/java/ru/bclib/api/dataexchange/Connector.java new file mode 100644 index 00000000..683c845e --- /dev/null +++ b/src/main/java/ru/bclib/api/dataexchange/Connector.java @@ -0,0 +1,19 @@ +package ru.bclib.api.dataexchange; + +import java.util.HashSet; +import java.util.Set; + +abstract class Connector { + protected final DataExchangeAPI api; + protected final Set descriptors; + + Connector(DataExchangeAPI api) { + this.api = api; + descriptors = new HashSet<>(); + } + public abstract boolean onClient(); + + public void addDescriptor(DataHandlerDescriptor desc){ + this.descriptors.add(desc); + } +} diff --git a/src/main/java/ru/bclib/api/dataexchange/ConnectorClientside.java b/src/main/java/ru/bclib/api/dataexchange/ConnectorClientside.java new file mode 100644 index 00000000..aba569a7 --- /dev/null +++ b/src/main/java/ru/bclib/api/dataexchange/ConnectorClientside.java @@ -0,0 +1,59 @@ +package ru.bclib.api.dataexchange; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; +import net.fabricmc.fabric.api.networking.v1.PacketSender; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientPacketListener; +import net.minecraft.network.FriendlyByteBuf; +import ru.bclib.BCLib; + +@Environment(EnvType.CLIENT) +class ConnectorClientside extends Connector { + private Minecraft client; + ConnectorClientside(DataExchangeAPI api) { + super(api); + this.client = null; + } + + + @Override + public boolean onClient() { + return true; + } + + protected void onPlayInit(ClientPacketListener handler, Minecraft client){ + if (this.client!=null && this.client != client){ + BCLib.LOGGER.warning("Client changed!"); + } + this.client = client; + for(DataHandlerDescriptor desc : descriptors){ + ClientPlayNetworking.registerReceiver(desc.identifier, (_client, _handler, _buf, _responseSender)->{ + receiveFromServer(desc, _client, _handler, _buf, _responseSender); + }); + } + } + + void onPlayReady(ClientPacketListener handler, PacketSender sender, Minecraft client){ + + } + + void onPlayDisconnect(ClientPacketListener handler, Minecraft client){ + for(DataHandlerDescriptor desc : descriptors) { + ClientPlayNetworking.unregisterReceiver(desc.identifier); + } + } + + void receiveFromServer(DataHandlerDescriptor desc, Minecraft client, ClientPacketListener handler, FriendlyByteBuf buf, PacketSender responseSender){ + DataHandler h = desc.instancer.get(); + h.receiveFromServer(client, handler, buf, responseSender); + } + + void sendToServer(DataHandler h){ + if (client==null){ + throw new RuntimeException("[internal error] Client not initialized yet!"); + } + h.sendToServer(this.client); + } +} diff --git a/src/main/java/ru/bclib/api/dataexchange/ConnectorServerside.java b/src/main/java/ru/bclib/api/dataexchange/ConnectorServerside.java new file mode 100644 index 00000000..39ce6253 --- /dev/null +++ b/src/main/java/ru/bclib/api/dataexchange/ConnectorServerside.java @@ -0,0 +1,64 @@ +package ru.bclib.api.dataexchange; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.networking.v1.PacketSender; +import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerGamePacketListenerImpl; +import ru.bclib.BCLib; + +@Environment(EnvType.SERVER) +class ConnectorServerside extends Connector { + private MinecraftServer server; + ConnectorServerside(DataExchangeAPI api) { + super(api); + server = null; + } + + @Override + public boolean onClient() { + return false; + } + + protected void onPlayInit(ServerGamePacketListenerImpl handler, MinecraftServer server){ + if (this.server!=null && this.server != server){ + BCLib.LOGGER.warning("Server changed!"); + } + this.server = server; + for(DataHandlerDescriptor desc : descriptors){ + ServerPlayNetworking.registerReceiver(handler, desc.identifier, (_server, _player, _handler, _buf, _responseSender) -> { + receiveFromClient(desc, _server, _player, _handler, _buf, _responseSender); + }); + } + } + + void onPlayReady(ServerGamePacketListenerImpl handler, PacketSender sender, MinecraftServer server){ + for(DataHandlerDescriptor desc : descriptors){ + if (desc.sendOnJoin){ + DataHandler h = desc.instancer.get(); + h.sendToClient(server, handler.player); + } + } + } + + void onPlayDisconnect(ServerGamePacketListenerImpl handler, MinecraftServer server){ + for(DataHandlerDescriptor desc : descriptors){ + ServerPlayNetworking.unregisterReceiver(handler, desc.identifier); + } + } + + void receiveFromClient(DataHandlerDescriptor desc, MinecraftServer server, ServerPlayer player, ServerGamePacketListenerImpl handler, FriendlyByteBuf buf, PacketSender responseSender){ + DataHandler h = desc.instancer.get(); + h.receiveFromClient(server, player, handler, buf, responseSender); + } + + void sendToClient(DataHandler h){ + if (server==null){ + throw new RuntimeException("[internal error] Server not initialized yet!"); + } + h.sendToClient(this.server); + } +} diff --git a/src/main/java/ru/bclib/api/dataexchange/DataExchangeAPI.java b/src/main/java/ru/bclib/api/dataexchange/DataExchangeAPI.java new file mode 100644 index 00000000..1d88fa26 --- /dev/null +++ b/src/main/java/ru/bclib/api/dataexchange/DataExchangeAPI.java @@ -0,0 +1,69 @@ +package ru.bclib.api.dataexchange; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayConnectionEvents; +import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; + +public class DataExchangeAPI { + private static DataExchangeAPI instance; + private final ConnectorServerside server; + private final ConnectorClientside client; + + public static DataExchangeAPI getInstance(){ + return instance; + } + + private static DataExchangeAPI getOrCreateInstance(boolean isClient){ + if (instance==null){ + instance = new DataExchangeAPI(isClient); + } + return instance; + } + + private DataExchangeAPI(boolean isClient){ + if (isClient){ + client = new ConnectorClientside(this); + server = null; + + ClientPlayConnectionEvents.INIT.register(client::onPlayInit); + ClientPlayConnectionEvents.JOIN.register(client::onPlayReady); + ClientPlayConnectionEvents.DISCONNECT.register(client::onPlayDisconnect); + } else { + client = null; + server = new ConnectorServerside(this); + + ServerPlayConnectionEvents.INIT.register(server::onPlayInit); + ServerPlayConnectionEvents.JOIN.register(server::onPlayReady); + ServerPlayConnectionEvents.DISCONNECT.register(server::onPlayDisconnect); + } + } + + + @Environment(EnvType.CLIENT) + public static void registerClientsideHandler(DataHandlerDescriptor desc){ + DataExchangeAPI api = DataExchangeAPI.getOrCreateInstance(true); + if (api.client == null){ + throw new RuntimeException("[Internal Error] DataExchangeAPI was already created as a Server"); + } + api.client.addDescriptor(desc); + } + + @Environment(EnvType.SERVER) + public static void registerServersideHandler(DataHandlerDescriptor desc){ + DataExchangeAPI api = DataExchangeAPI.getOrCreateInstance(false); + if (api.server == null){ + throw new RuntimeException("[Internal Error] DataExchangeAPI was already created as a Client"); + } + api.server.addDescriptor(desc); + } + + public static void send(DataHandler h){ + if (h.getOriginatesOnServer()){ + DataExchangeAPI.getInstance().server.sendToClient(h); + } else { + DataExchangeAPI.getInstance().client.sendToServer(h); + } + } + +} diff --git a/src/main/java/ru/bclib/api/dataexchange/DataHandler.java b/src/main/java/ru/bclib/api/dataexchange/DataHandler.java new file mode 100644 index 00000000..1200c15c --- /dev/null +++ b/src/main/java/ru/bclib/api/dataexchange/DataHandler.java @@ -0,0 +1,111 @@ +package ru.bclib.api.dataexchange; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; +import net.fabricmc.fabric.api.networking.v1.PacketByteBufs; +import net.fabricmc.fabric.api.networking.v1.PacketSender; +import net.fabricmc.fabric.api.networking.v1.PlayerLookup; +import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientPacketListener; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.server.network.ServerGamePacketListenerImpl; +import org.jetbrains.annotations.NotNull; + +public abstract class DataHandler { + private final boolean originatesOnServer; + @NotNull + private final ResourceLocation identifier; + + protected DataHandler(ResourceLocation identifier, boolean originatesOnServer){ + this.originatesOnServer = originatesOnServer; + this.identifier = identifier; + } + + final public boolean getOriginatesOnServer(){ + return originatesOnServer; + } + + final public ResourceLocation getIdentifier(){ + return identifier; + } + + @Environment(EnvType.CLIENT) + void receiveFromServer(Minecraft client, ClientPacketListener handler, FriendlyByteBuf buf, PacketSender responseSender){ + deserializeFromIncomingData(buf, responseSender, false); + client.execute(() -> runOnClient(client)); + } + + @Environment(EnvType.SERVER) + void receiveFromClient(MinecraftServer server, ServerPlayer player, ServerGamePacketListenerImpl handler, FriendlyByteBuf buf, PacketSender responseSender){ + deserializeFromIncomingData(buf, responseSender, true); + server.execute(() -> runOnServer(server)); + } + + protected void deserializeFromIncomingData(FriendlyByteBuf buf, PacketSender responseSender, boolean fromClient){ + } + + @Environment(EnvType.CLIENT) + protected void runOnClient(Minecraft client){ + + } + + @Environment(EnvType.SERVER) + protected void runOnServer(MinecraftServer server){ + + } + + protected void serializeData(FriendlyByteBuf buf) { + + } + + @Environment(EnvType.SERVER) + void sendToClient(MinecraftServer server){ + FriendlyByteBuf buf = PacketByteBufs.create(); + serializeData(buf); + + for (ServerPlayer player : PlayerLookup.all(server)) { + ServerPlayNetworking.send(player, this.identifier, buf); + } + } + + @Environment(EnvType.SERVER) + void sendToClient(MinecraftServer server, ServerPlayer player){ + FriendlyByteBuf buf = PacketByteBufs.create(); + serializeData(buf); + ServerPlayNetworking.send(player, this.identifier, buf); + } + + @Environment(EnvType.CLIENT) + void sendToServer(Minecraft client){ + FriendlyByteBuf buf = PacketByteBufs.create(); + serializeData(buf); + ClientPlayNetworking.send(identifier, buf); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + DataHandler that = (DataHandler) o; + return originatesOnServer == that.originatesOnServer && identifier.equals(that.identifier); + } + + @Override + public int hashCode() { + int hash = identifier.hashCode(); + if (originatesOnServer) hash |= 0x80000000; + else hash &=0x7FFFFFFF; + + return hash; + } + + @Override + public String toString() { + return "DataHandler{" + "originatesOnServer=" + originatesOnServer + ", identifier=" + identifier + '}'; + } +} diff --git a/src/main/java/ru/bclib/api/dataexchange/DataHandlerDescriptor.java b/src/main/java/ru/bclib/api/dataexchange/DataHandlerDescriptor.java new file mode 100644 index 00000000..9ca5077f --- /dev/null +++ b/src/main/java/ru/bclib/api/dataexchange/DataHandlerDescriptor.java @@ -0,0 +1,20 @@ +package ru.bclib.api.dataexchange; + +import net.minecraft.resources.ResourceLocation; + +import java.util.function.Supplier; + +public class DataHandlerDescriptor { + public DataHandlerDescriptor(ResourceLocation identifier, Supplier instancer){ + this(identifier, instancer, false); + } + public DataHandlerDescriptor(ResourceLocation identifier, Supplier instancer, boolean sendOnJoin){ + this.instancer = instancer; + this.identifier = identifier; + this.sendOnJoin = sendOnJoin; + } + + public final boolean sendOnJoin; + public final ResourceLocation identifier; + public final Supplier instancer; +} diff --git a/src/main/java/ru/bclib/api/dataexchange/TestHandler.java b/src/main/java/ru/bclib/api/dataexchange/TestHandler.java new file mode 100644 index 00000000..b805a299 --- /dev/null +++ b/src/main/java/ru/bclib/api/dataexchange/TestHandler.java @@ -0,0 +1,33 @@ +package ru.bclib.api.dataexchange; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.networking.v1.PacketSender; +import net.minecraft.client.Minecraft; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceLocation; +import ru.bclib.BCLib; + +public class TestHandler extends DataHandler{ + public static DataHandlerDescriptor DESCRIPTOR = new DataHandlerDescriptor(new ResourceLocation(BCLib.MOD_ID, "__test"), TestHandler::new, true); + + public TestHandler() { + super(DESCRIPTOR.identifier, true); + } + + @Override + protected void deserializeFromIncomingData(FriendlyByteBuf buf, PacketSender responseSender, boolean fromClient) { + BCLib.LOGGER.info("PROCESSING INCOMING TEST-DATA fromClient="+fromClient); + } + + @Override + @Environment(EnvType.CLIENT) + protected void runOnClient(Minecraft client) { + BCLib.LOGGER.info("RUNNING INCOMING TEST-DATA ON CLIENT"); + } + + @Override + protected void serializeData(FriendlyByteBuf buf) { + BCLib.LOGGER.info("BUILDING OUTGOING TEST-DATA ON SERVER"); + } +} diff --git a/src/main/java/ru/bclib/client/BCLibClient.java b/src/main/java/ru/bclib/client/BCLibClient.java index 7b338cfa..27bd5f66 100644 --- a/src/main/java/ru/bclib/client/BCLibClient.java +++ b/src/main/java/ru/bclib/client/BCLibClient.java @@ -3,6 +3,8 @@ package ru.bclib.client; import net.fabricmc.api.ClientModInitializer; import ru.bclib.api.ModIntegrationAPI; import ru.bclib.api.PostInitAPI; +import ru.bclib.api.dataexchange.DataExchangeAPI; +import ru.bclib.api.dataexchange.TestHandler; import ru.bclib.registry.BaseBlockEntityRenders; public class BCLibClient implements ClientModInitializer { @@ -10,6 +12,8 @@ public class BCLibClient implements ClientModInitializer { public void onInitializeClient() { ModIntegrationAPI.registerAll(); BaseBlockEntityRenders.register(); + DataExchangeAPI.registerClientsideHandler(TestHandler.DESCRIPTOR); + PostInitAPI.postInit(true); } } diff --git a/src/main/java/ru/bclib/server/BCLibServer.java b/src/main/java/ru/bclib/server/BCLibServer.java index e1ba1107..9568a169 100644 --- a/src/main/java/ru/bclib/server/BCLibServer.java +++ b/src/main/java/ru/bclib/server/BCLibServer.java @@ -3,11 +3,14 @@ package ru.bclib.server; import net.fabricmc.api.DedicatedServerModInitializer; import ru.bclib.api.ModIntegrationAPI; import ru.bclib.api.PostInitAPI; +import ru.bclib.api.dataexchange.*; public class BCLibServer implements DedicatedServerModInitializer { @Override public void onInitializeServer() { ModIntegrationAPI.registerAll(); + DataExchangeAPI.registerServersideHandler(TestHandler.DESCRIPTOR); + PostInitAPI.postInit(false); } }