From 3630aecf5f8a90da7882b6b0d083ef5f6f9a08ca Mon Sep 17 00:00:00 2001 From: Bixilon Date: Fri, 19 Jun 2020 14:25:56 +0200 Subject: [PATCH] wip modding api: PluginChannelHandler --- .../modding/channels/ChannelHandler.java | 21 ++++ .../channels/DefaultPluginChannels.java | 38 ++++++ .../channels/PluginChannelHandler.java | 112 ++++++++++++++++++ .../minosoft/protocol/network/Connection.java | 31 ++++- .../protocol/protocol/PacketHandler.java | 11 +- 5 files changed, 197 insertions(+), 16 deletions(-) create mode 100644 src/main/java/de/bixilon/minosoft/protocol/modding/channels/ChannelHandler.java create mode 100644 src/main/java/de/bixilon/minosoft/protocol/modding/channels/DefaultPluginChannels.java create mode 100644 src/main/java/de/bixilon/minosoft/protocol/modding/channels/PluginChannelHandler.java diff --git a/src/main/java/de/bixilon/minosoft/protocol/modding/channels/ChannelHandler.java b/src/main/java/de/bixilon/minosoft/protocol/modding/channels/ChannelHandler.java new file mode 100644 index 000000000..59ef017da --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/protocol/modding/channels/ChannelHandler.java @@ -0,0 +1,21 @@ +/* + * Codename Minosoft + * Copyright (C) 2020 Moritz Zwerger + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.protocol.modding.channels; + +import de.bixilon.minosoft.protocol.protocol.InByteBuffer; + +public interface ChannelHandler { + + void handle(PluginChannelHandler handler, InByteBuffer buffer); +} diff --git a/src/main/java/de/bixilon/minosoft/protocol/modding/channels/DefaultPluginChannels.java b/src/main/java/de/bixilon/minosoft/protocol/modding/channels/DefaultPluginChannels.java new file mode 100644 index 000000000..c6052819e --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/protocol/modding/channels/DefaultPluginChannels.java @@ -0,0 +1,38 @@ +/* + * Codename Minosoft + * Copyright (C) 2020 Moritz Zwerger + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.protocol.modding.channels; + +public enum DefaultPluginChannels { + MC_BRAND("MC|Brand"); + + + final String name; + + DefaultPluginChannels(String name) { + this.name = name; + } + + public static DefaultPluginChannels byName(String name) { + for (DefaultPluginChannels d : values()) { + if (d.getName().equals(name)) { + return d; + } + } + return null; + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/de/bixilon/minosoft/protocol/modding/channels/PluginChannelHandler.java b/src/main/java/de/bixilon/minosoft/protocol/modding/channels/PluginChannelHandler.java new file mode 100644 index 000000000..1906fabec --- /dev/null +++ b/src/main/java/de/bixilon/minosoft/protocol/modding/channels/PluginChannelHandler.java @@ -0,0 +1,112 @@ +/* + * Codename Minosoft + * Copyright (C) 2020 Moritz Zwerger + * + * This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with this program. If not, see . + * + * This software is not affiliated with Mojang AB, the original developer of Minecraft. + */ + +package de.bixilon.minosoft.protocol.modding.channels; + +import de.bixilon.minosoft.logging.Log; +import de.bixilon.minosoft.protocol.network.Connection; +import de.bixilon.minosoft.protocol.packets.serverbound.play.PacketPluginMessageSending; +import de.bixilon.minosoft.protocol.protocol.InByteBuffer; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class PluginChannelHandler { + final Map> channels; + final List registeredClientChannels; + final List registeredServerChannels; + final Connection connection; + + public PluginChannelHandler(Connection connection) { + channels = new HashMap<>(); + registeredClientChannels = new ArrayList<>(); + registeredServerChannels = new ArrayList<>(); + this.connection = connection; + } + + public void registerClientHandler(String name, ChannelHandler handler) { + if (channels.get(name) == null) { + // no channel with that name was registered yet + List handlerList = new ArrayList<>(); + handlerList.add(handler); + channels.put(name, handlerList); + return; + } + // was registered, appending to list + + channels.get(name).add(handler); + } + + public void unregisterClientHandler(String name, ChannelHandler handler) { + if (channels.get(name) == null) { + // not registered + return; + } + channels.get(name).remove(handler); + } + + public void handle(String name, byte[] data) { + if (name.equals("REGISTER")) { + // register this channel + String toRegisterName = new String(data); + registeredClientChannels.add(toRegisterName); + Log.debug(String.format("Server registered plugin channel \"%s\"", toRegisterName)); + return; + } + if (name.equals("UNREGISTER")) { + // register this channel + String toUnregisterName = new String(data); + registeredClientChannels.remove(toUnregisterName); + Log.debug(String.format("Server unregistered plugin channel \"%s\"", toUnregisterName)); + return; + } + // check if channel was registered or if it is a default channel + if (!registeredClientChannels.contains(name) && DefaultPluginChannels.byName(name) == null) { + Log.debug(String.format("Server tried to send data into unregistered plugin channel (name=\"%s\", messageLength=%d)", name, data.length)); + return; + } + if (channels.get(name) == null) { + Log.debug(String.format("Can not handle plugin message in channel \"%s\" (messageLength=%d)", name, data.length)); + return; + } + + for (ChannelHandler handler : channels.get(name)) { + handler.handle(this, new InByteBuffer(data)); + } + } + + public void sendRawData(String channel, byte[] data) { + connection.sendPacket(new PacketPluginMessageSending(channel, data)); + } + + public void registerServerChannel(String name) { + if (DefaultPluginChannels.byName(name) != null) { + // channel is a default channel, can not register + throw new IllegalArgumentException(String.format("Can not register default Minecraft plugin channel (name=%s)", name)); + } + sendRawData("REGISTER", name.getBytes()); + registeredServerChannels.add(name); + } + + public void unregisterServerChannel(String name) { + if (DefaultPluginChannels.byName(name) != null) { + // channel is a default channel, can not unregister + throw new IllegalArgumentException(String.format("Can not unregister default Minecraft plugin channel (name=%s)", name)); + } + sendRawData("UNREGISTER", name.getBytes()); + registeredServerChannels.remove(name); + } + +} diff --git a/src/main/java/de/bixilon/minosoft/protocol/network/Connection.java b/src/main/java/de/bixilon/minosoft/protocol/network/Connection.java index 6cd71c5b8..8a8df0efb 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/network/Connection.java +++ b/src/main/java/de/bixilon/minosoft/protocol/network/Connection.java @@ -13,8 +13,12 @@ package de.bixilon.minosoft.protocol.network; +import de.bixilon.minosoft.Minosoft; +import de.bixilon.minosoft.config.GameConfiguration; import de.bixilon.minosoft.game.datatypes.Player; import de.bixilon.minosoft.logging.Log; +import de.bixilon.minosoft.protocol.modding.channels.DefaultPluginChannels; +import de.bixilon.minosoft.protocol.modding.channels.PluginChannelHandler; import de.bixilon.minosoft.protocol.packets.ClientboundPacket; import de.bixilon.minosoft.protocol.packets.ServerboundPacket; import de.bixilon.minosoft.protocol.packets.serverbound.handshaking.PacketHandshake; @@ -33,11 +37,11 @@ public class Connection { private final int port; private final Network network; private final PacketHandler handler; + private final PluginChannelHandler pluginChannelHandler; private final ArrayList handlingQueue; + Thread handleThread; private Player player; private ConnectionState state = ConnectionState.DISCONNECTED; - Thread handleThread; - private boolean onlyPing; public Connection(String host, int port) { @@ -46,6 +50,8 @@ public class Connection { network = new Network(this); handlingQueue = new ArrayList<>(); handler = new PacketHandler(this); + pluginChannelHandler = new PluginChannelHandler(this); + registerDefaultChannels(); } /** @@ -130,6 +136,10 @@ public class Connection { return player; } + public void setPlayer(Player player) { + this.player = player; + } + public void sendPacket(ServerboundPacket p) { network.sendPacket(p); } @@ -157,11 +167,20 @@ public class Connection { handleThread.start(); } - public void setPlayer(Player player) { - this.player = player; - } - public void sendChatMessage(String message) { sendPacket(new PacketChatMessage(message)); } + + public PluginChannelHandler getPluginChannelHandler() { + return pluginChannelHandler; + } + + public void registerDefaultChannels() { + // MC|Brand + getPluginChannelHandler().registerClientHandler(DefaultPluginChannels.MC_BRAND.getName(), (handler, buffer) -> { + Log.info(String.format("Server is running %s on version %s", new String(buffer.readBytes(buffer.getBytesLeft())), getVersion().getName())); + + getPluginChannelHandler().sendRawData(DefaultPluginChannels.MC_BRAND.getName(), (Minosoft.getConfig().getBoolean(GameConfiguration.NETWORK_FAKE_CLIENT_BRAND) ? "vanilla" : "Minosoft").getBytes()); + }); + } } diff --git a/src/main/java/de/bixilon/minosoft/protocol/protocol/PacketHandler.java b/src/main/java/de/bixilon/minosoft/protocol/protocol/PacketHandler.java index 621982bb6..0bec7e2e1 100644 --- a/src/main/java/de/bixilon/minosoft/protocol/protocol/PacketHandler.java +++ b/src/main/java/de/bixilon/minosoft/protocol/protocol/PacketHandler.java @@ -13,8 +13,6 @@ package de.bixilon.minosoft.protocol.protocol; -import de.bixilon.minosoft.Minosoft; -import de.bixilon.minosoft.config.GameConfiguration; import de.bixilon.minosoft.game.datatypes.GameMode; import de.bixilon.minosoft.game.datatypes.blocks.Blocks; import de.bixilon.minosoft.game.datatypes.entities.meta.HumanMetaData; @@ -30,7 +28,6 @@ import de.bixilon.minosoft.protocol.packets.clientbound.status.PacketStatusRespo import de.bixilon.minosoft.protocol.packets.serverbound.login.PacketEncryptionResponse; import de.bixilon.minosoft.protocol.packets.serverbound.play.PacketKeepAliveResponse; import de.bixilon.minosoft.protocol.packets.serverbound.play.PacketPlayerPositionAndRotationSending; -import de.bixilon.minosoft.protocol.packets.serverbound.play.PacketPluginMessageSending; import javax.crypto.SecretKey; import java.math.BigInteger; @@ -103,13 +100,7 @@ public class PacketHandler { } public void handle(PacketPluginMessageReceiving pkg) { - if (pkg.getChannel().equals("MC|Brand")) { - // server brand received - Log.info(String.format("Server is running %s on version %s", new String(pkg.getData()), connection.getVersion().getName())); - - // send back own brand - connection.sendPacket(new PacketPluginMessageSending("MC|Brand", (Minosoft.getConfig().getBoolean(GameConfiguration.NETWORK_FAKE_CLIENT_BRAND) ? "vanilla" : "Minosoft"))); - } + connection.getPluginChannelHandler().handle(pkg.getChannel(), pkg.getData()); } public void handle(PacketSpawnLocation pkg) {