diff --git a/build.gradle.kts b/build.gradle.kts index 62eb7ac..2408b68 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,5 +1,4 @@ plugins { - application alias(libs.plugins.indra) alias(libs.plugins.indra.licenser.spotless) } @@ -8,26 +7,17 @@ group = "org.geysermc.globallinkserver" dependencies { implementation(libs.gson) // newer version required for record support - implementation(libs.fastutil.common) - implementation(libs.bundles.protocol) - implementation(libs.mcprotocollib) { - exclude("io.netty", "netty-all") - } + implementation(libs.bundles.fastutil) - // mcprotocollib won't work without this - implementation(libs.netty.handler) + compileOnly(libs.spigot.api) + compileOnly(libs.geyser.api) - implementation(libs.adventure.text.legacy) implementation(libs.mariadb.client) compileOnly(libs.checker.qual) } -application { - mainClass.set("org.geysermc.globallinkserver.GlobalLinkServer") -} - indra { github("GeyserMC", "GlobalLinkServer") { ci(true) @@ -57,8 +47,4 @@ tasks.jar { archiveBaseName = "GlobalLinkServer" archiveVersion = "" archiveClassifier = "" - - manifest { - attributes["Main-Class"] = application.mainClass - } } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index bd60cdf..718a495 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1 @@ -version=1.0-SNAPSHOT \ No newline at end of file +version=2.0-SNAPSHOT \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 375193f..1ff1829 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,25 +1,23 @@ [versions] gson = "2.10.1" -protocol = "3.0.0.Beta5-20241022.154658-14" -netty = "4.1.110.Final" fastutil = "8.5.2" -mcprotocollib = "86903ec" # Revert from jitpack after release adventure-text = "4.15.0-20231207.074016-23" # Match version to MCPL mariadb-client = "2.7.3" checker-qual = "3.21.1" indra = "3.1.2" +spigot-api = "1.16.5-R0.1-SNAPSHOT" + [libraries] -gson = { module = "com.google.code.gson:gson", version.ref = "gson" } -protocol-codec = { module = "org.cloudburstmc.protocol:bedrock-codec", version.ref = "protocol" } -protocol-connection = { module = "org.cloudburstmc.protocol:bedrock-connection", version.ref = "protocol" } -protocol-common = { module = "org.cloudburstmc.protocol:common", version.ref = "protocol" } +spigot-api = { group = "org.spigotmc", name = "spigot-api", version.ref = "spigot-api" } +geyser-api = { group = "org.geysermc.geyser", name = "api", version = "2.6.0-SNAPSHOT" } -netty-handler = { group = "io.netty", name = "netty-handler", version.ref = "netty" } +gson = { module = "com.google.code.gson:gson", version.ref = "gson" } -fastutil-common = { module = "com.nukkitx.fastutil:fastutil-common", version.ref = "fastutil" } -mcprotocollib = { module = "com.github.GeyserMC:mcprotocollib", version.ref = "mcprotocollib" } -adventure-text-legacy = { module = "net.kyori:adventure-text-serializer-legacy", version.ref = "adventure-text" } +fastutil-int-int-maps = { group = "com.nukkitx.fastutil", name = "fastutil-int-int-maps", version.ref = "fastutil" } +fastutil-int-object-maps = { group = "com.nukkitx.fastutil", name = "fastutil-int-object-maps", version.ref = "fastutil" } +fastutil-object-int-maps = { group = "com.nukkitx.fastutil", name = "fastutil-object-int-maps", version.ref = "fastutil" } +fastutil-object-object-maps = { group = "com.nukkitx.fastutil", name = "fastutil-object-object-maps", version.ref = "fastutil" } mariadb-client = { module = "org.mariadb.jdbc:mariadb-java-client", version.ref = "mariadb-client" } checker-qual = { module = "org.checkerframework:checker-qual", version.ref = "checker-qual" } @@ -30,4 +28,4 @@ indra-publishing = { id = "net.kyori.indra.publishing", version.ref = "indra" } indra-licenser-spotless = { id = "net.kyori.indra.licenser.spotless", version.ref = "indra" } [bundles] -protocol = ["protocol-codec", "protocol-connection", "protocol-common"] \ No newline at end of file +fastutil = [ "fastutil-int-int-maps", "fastutil-int-object-maps", "fastutil-object-int-maps", "fastutil-object-object-maps" ] diff --git a/src/main/java/org/geysermc/globallinkserver/GlobalLinkServer.java b/src/main/java/org/geysermc/globallinkserver/GlobalLinkServer.java index c2ac3cb..500d16b 100644 --- a/src/main/java/org/geysermc/globallinkserver/GlobalLinkServer.java +++ b/src/main/java/org/geysermc/globallinkserver/GlobalLinkServer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2024 GeyserMC + * Copyright (c) 2021-2025 GeyserMC * Licensed under the MIT license * @link https://github.com/GeyserMC/GlobalLinkServer */ @@ -8,39 +8,49 @@ import java.util.Timer; import java.util.TimerTask; import java.util.logging.Logger; -import org.geysermc.globallinkserver.bedrock.BedrockServer; +import org.bukkit.Bukkit; +import org.bukkit.command.Command; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.plugin.java.JavaPlugin; +import org.checkerframework.checker.nullness.qual.NonNull; import org.geysermc.globallinkserver.config.Config; import org.geysermc.globallinkserver.config.ConfigReader; -import org.geysermc.globallinkserver.java.JavaServer; import org.geysermc.globallinkserver.link.LinkManager; -import org.geysermc.globallinkserver.player.PlayerManager; +import org.geysermc.globallinkserver.util.CommandUtils; -public class GlobalLinkServer { +public class GlobalLinkServer extends JavaPlugin { private static final Timer TIMER = new Timer(); - public static final Logger LOGGER = Logger.getGlobal(); + public static Logger LOGGER; - public static void main(String... args) { - // Make logging more simple, adopted from https://stackoverflow.com/a/5937929 - System.setProperty( - "java.util.logging.SimpleFormatter.format", "%1$tY-%1$tm-%1$td %1$tH:%1$tM:%1$tS %5$s%6$s%n"); + private LinkManager linkManager; - Config config = ConfigReader.readConfig(); + @Override + public void onEnable() { + LOGGER = getLogger(); - PlayerManager playerManager = new PlayerManager(); - LinkManager linkManager = new LinkManager(config); + Config config = ConfigReader.readConfig(); + linkManager = new LinkManager(config); - new JavaServer(playerManager, linkManager).startServer(config); - new BedrockServer(playerManager, linkManager).startServer(config); + Bukkit.getScheduler().scheduleSyncRepeatingTask(this, linkManager::cleanupTempLinks, 0, 0); TimerTask task = new TimerTask() { @Override public void run() { - linkManager.cleanupTempLinks(playerManager); + linkManager.cleanupTempLinks(); } }; TIMER.scheduleAtFixedRate(task, 0L, 60_000L); - LOGGER.info( - "Started Global Linking Server on java: " + config.javaPort() + ", bedrock: " + config.bedrockPort()); + LOGGER.info("Started Global Linking Server plugin!"); + } + + @Override + public boolean onCommand( + @NonNull CommandSender sender, @NonNull Command command, @NonNull String label, @NonNull String[] args) { + if (sender instanceof Player player) { + CommandUtils.handleCommand(linkManager, player, command.getName() + " " + String.join(" ", args)); + } + return true; } } diff --git a/src/main/java/org/geysermc/globallinkserver/Server.java b/src/main/java/org/geysermc/globallinkserver/Server.java deleted file mode 100644 index a9a3d1e..0000000 --- a/src/main/java/org/geysermc/globallinkserver/Server.java +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Copyright (c) 2021-2021 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/GlobalLinkServer - */ - -package org.geysermc.globallinkserver; - -import org.geysermc.globallinkserver.config.Config; - -public interface Server { - /** - * Starts the server using the server configuration. - * - * @param config the configuration to use - * @return true if the server started successfully, false otherwise - */ - boolean startServer(Config config); - - void shutdown(); -} diff --git a/src/main/java/org/geysermc/globallinkserver/bedrock/BedrockPlayer.java b/src/main/java/org/geysermc/globallinkserver/bedrock/BedrockPlayer.java deleted file mode 100644 index bcbb011..0000000 --- a/src/main/java/org/geysermc/globallinkserver/bedrock/BedrockPlayer.java +++ /dev/null @@ -1,254 +0,0 @@ -/* - * Copyright (c) 2021-2023 GeyserMC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/GlobalLinkServer - */ -package org.geysermc.globallinkserver.bedrock; - -import io.netty.buffer.Unpooled; - -import java.util.Collections; -import java.util.UUID; -import org.cloudburstmc.math.vector.Vector2f; -import org.cloudburstmc.math.vector.Vector3f; -import org.cloudburstmc.math.vector.Vector3i; -import org.cloudburstmc.nbt.NbtMap; -import org.cloudburstmc.protocol.bedrock.BedrockServerSession; -import org.cloudburstmc.protocol.bedrock.data.AuthoritativeMovementMode; -import org.cloudburstmc.protocol.bedrock.data.ChatRestrictionLevel; -import org.cloudburstmc.protocol.bedrock.data.EduSharedUriResource; -import org.cloudburstmc.protocol.bedrock.data.GamePublishSetting; -import org.cloudburstmc.protocol.bedrock.data.GameRuleData; -import org.cloudburstmc.protocol.bedrock.data.GameType; -import org.cloudburstmc.protocol.bedrock.data.PlayerPermission; -import org.cloudburstmc.protocol.bedrock.data.SpawnBiomeType; -import org.cloudburstmc.protocol.bedrock.data.command.CommandData; -import org.cloudburstmc.protocol.bedrock.data.command.CommandOverloadData; -import org.cloudburstmc.protocol.bedrock.data.command.CommandParam; -import org.cloudburstmc.protocol.bedrock.data.command.CommandParamData; -import org.cloudburstmc.protocol.bedrock.data.command.CommandPermission; -import org.cloudburstmc.protocol.bedrock.packet.AvailableCommandsPacket; -import org.cloudburstmc.protocol.bedrock.packet.BiomeDefinitionListPacket; -import org.cloudburstmc.protocol.bedrock.packet.LevelChunkPacket; -import org.cloudburstmc.protocol.bedrock.packet.PlayStatusPacket; -import org.cloudburstmc.protocol.bedrock.packet.SetEntityMotionPacket; -import org.cloudburstmc.protocol.bedrock.packet.StartGamePacket; -import org.cloudburstmc.protocol.bedrock.packet.TextPacket; -import org.cloudburstmc.protocol.bedrock.util.ChainValidationResult; -import org.cloudburstmc.protocol.common.util.OptionalBoolean; -import org.geysermc.globallinkserver.bedrock.util.PaletteUtils; -import org.geysermc.globallinkserver.player.Player; -import org.geysermc.globallinkserver.util.Utils; - -public class BedrockPlayer implements Player { - private final BedrockServerSession session; - private final UUID uniqueId; - private final String xuid; - private final String username; - - private int linkId; - - public BedrockPlayer(BedrockServerSession session, ChainValidationResult.IdentityData identity) { - this.session = session; - this.xuid = identity.xuid; - this.uniqueId = new UUID(0, Utils.parseLong(xuid)); - this.username = identity.displayName; - } - - @Override - public void sendMessage(String message) { - TextPacket packet = new TextPacket(); - packet.setPlatformChatId(""); - packet.setSourceName(""); - packet.setXuid(xuid); - packet.setType(TextPacket.Type.SYSTEM); - packet.setNeedsTranslation(false); - packet.setMessage(formatMessage(message)); - session.sendPacket(packet); - } - - @Override - public void disconnect(String reason) { - session.disconnect(formatMessage(reason)); - } - - /** - * Send a few packets to get the client to load into the world - */ - public void sendStartGame() { - // A lot of this likely doesn't need to be changed - StartGamePacket startGamePacket = new StartGamePacket(); - startGamePacket.setUniqueEntityId(1); - startGamePacket.setRuntimeEntityId(1); - startGamePacket.setPlayerGameType(GameType.CREATIVE); - startGamePacket.setPlayerPosition(Vector3f.from(0, 64 + 2, 0)); - startGamePacket.setRotation(Vector2f.ONE); - startGamePacket.setPlayerPropertyData(NbtMap.EMPTY); - - startGamePacket.setSeed(0L); - startGamePacket.setDimensionId(2); - startGamePacket.setGeneratorId(1); - startGamePacket.setSpawnBiomeType(SpawnBiomeType.DEFAULT); - startGamePacket.setCustomBiomeName(""); - startGamePacket.setForceExperimentalGameplay(OptionalBoolean.empty()); - startGamePacket.setLevelGameType(GameType.CREATIVE); - startGamePacket.setDifficulty(0); - startGamePacket.setDefaultSpawn(Vector3i.ZERO); - startGamePacket.setAchievementsDisabled(true); - startGamePacket.setCurrentTick(-1); - startGamePacket.setEduEditionOffers(0); - startGamePacket.setEduFeaturesEnabled(false); - startGamePacket.setEducationProductionId(""); - startGamePacket.setEduSharedUriResource(EduSharedUriResource.EMPTY); - startGamePacket.setRainLevel(0); - startGamePacket.setLightningLevel(0); - startGamePacket.setMultiplayerGame(true); - startGamePacket.setBroadcastingToLan(true); - startGamePacket.getGamerules().add(new GameRuleData<>("showcoordinates", false)); - startGamePacket.setPlatformBroadcastMode(GamePublishSetting.PUBLIC); - startGamePacket.setXblBroadcastMode(GamePublishSetting.PUBLIC); - startGamePacket.setCommandsEnabled(true); - startGamePacket.setChatRestrictionLevel(ChatRestrictionLevel.NONE); - startGamePacket.setTexturePacksRequired(false); - startGamePacket.setBonusChestEnabled(false); - startGamePacket.setStartingWithMap(false); - startGamePacket.setTrustingPlayers(true); - startGamePacket.setDefaultPlayerPermission(PlayerPermission.VISITOR); - startGamePacket.setServerChunkTickRange(4); - startGamePacket.setBehaviorPackLocked(false); - startGamePacket.setResourcePackLocked(false); - startGamePacket.setFromLockedWorldTemplate(false); - startGamePacket.setUsingMsaGamertagsOnly(false); - startGamePacket.setFromWorldTemplate(false); - startGamePacket.setWorldTemplateOptionLocked(false); - - startGamePacket.setServerEngine(""); - startGamePacket.setLevelId(""); - startGamePacket.setLevelName("GlobalLinkServer"); - startGamePacket.setPremiumWorldTemplateId(""); - startGamePacket.setWorldTemplateId(new UUID(0, 0)); - startGamePacket.setCurrentTick(0); - startGamePacket.setEnchantmentSeed(0); - startGamePacket.setMultiplayerCorrelationId(""); - startGamePacket.setVanillaVersion("*"); - - startGamePacket.setAuthoritativeMovementMode(AuthoritativeMovementMode.CLIENT); - startGamePacket.setRewindHistorySize(0); - startGamePacket.setServerAuthoritativeBlockBreaking(false); - - startGamePacket.setServerId(""); - startGamePacket.setWorldId(""); - startGamePacket.setScenarioId(""); - - session.sendPacket(startGamePacket); - - // Send an empty chunk - LevelChunkPacket data = new LevelChunkPacket(); - data.setChunkX(0); - data.setChunkZ(0); - data.setSubChunksLength(0); - data.setData(Unpooled.wrappedBuffer(PaletteUtils.EMPTY_LEVEL_CHUNK_DATA)); - data.setCachingEnabled(false); - session.sendPacket(data); - - // Send the biomes - BiomeDefinitionListPacket biomeDefinitionListPacket = new BiomeDefinitionListPacket(); - biomeDefinitionListPacket.setDefinitions(PaletteUtils.BIOMES_PALETTE); - session.sendPacket(biomeDefinitionListPacket); - - // Let the client know the player can spawn - PlayStatusPacket playStatusPacket = new PlayStatusPacket(); - playStatusPacket.setStatus(PlayStatusPacket.Status.PLAYER_SPAWN); - session.sendPacket(playStatusPacket); - - // Freeze the player - SetEntityMotionPacket setEntityMotionPacket = new SetEntityMotionPacket(); - setEntityMotionPacket.setRuntimeEntityId(1); - setEntityMotionPacket.setMotion(Vector3f.ZERO); - session.sendPacket(setEntityMotionPacket); - - // Send the available commands - AvailableCommandsPacket availableCommandsPacket = new AvailableCommandsPacket(); - - CommandParamData linkIdParam = new CommandParamData(); - linkIdParam.setName("linkId"); - linkIdParam.setType(CommandParam.INT); - linkIdParam.setOptional(true); - - availableCommandsPacket.getCommands().add(new CommandData( - "linkaccount", - "Link your account", - Collections.EMPTY_SET, - CommandPermission.ANY, - null, - Collections.EMPTY_LIST, - new CommandOverloadData[] { - new CommandOverloadData( - false, - new CommandParamData[] { - linkIdParam - } - ) - } - )); - - availableCommandsPacket.getCommands().add(new CommandData( - "unlinkaccount", - "Unlink your account", - Collections.EMPTY_SET, - CommandPermission.ANY, - null, - Collections.EMPTY_LIST, - new CommandOverloadData[] {} - )); - - session.sendPacket(availableCommandsPacket); - } - - public BedrockServerSession session() { - return session; - } - - @Override - public UUID uniqueId() { - return uniqueId; - } - - public String xuid() { - return xuid; - } - - @Override - public String username() { - return username; - } - - @Override - public int linkId() { - return linkId; - } - - @Override - public void linkId(int linkId) { - this.linkId = linkId; - } -} diff --git a/src/main/java/org/geysermc/globallinkserver/bedrock/BedrockServer.java b/src/main/java/org/geysermc/globallinkserver/bedrock/BedrockServer.java deleted file mode 100644 index 6e1b5a9..0000000 --- a/src/main/java/org/geysermc/globallinkserver/bedrock/BedrockServer.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright (c) 2021-2023 GeyserMC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/GlobalLinkServer - */ -package org.geysermc.globallinkserver.bedrock; - -import java.net.InetSocketAddress; -import java.util.concurrent.ThreadLocalRandom; - -import org.cloudburstmc.protocol.bedrock.BedrockPong; -import org.cloudburstmc.protocol.bedrock.BedrockServerSession; -import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec; -import org.cloudburstmc.protocol.bedrock.netty.initializer.BedrockServerInitializer; -import org.geysermc.globallinkserver.Server; -import org.geysermc.globallinkserver.bedrock.util.BedrockVersionUtils; -import org.geysermc.globallinkserver.config.Config; -import org.geysermc.globallinkserver.link.LinkManager; -import org.geysermc.globallinkserver.player.PlayerManager; - -public class BedrockServer implements Server { - private final PlayerManager playerManager; - private final LinkManager linkManager; - - private NettyServer server; - - public BedrockServer(PlayerManager playerManager, LinkManager linkManager) { - this.playerManager = playerManager; - this.linkManager = linkManager; - } - - @Override - public boolean startServer(Config config) { - if (server != null) { - return false; - } - - BedrockCodec latestCodec = BedrockVersionUtils.LATEST_CODEC; - server = new NettyServer( - new BedrockPong() - .edition("MCPE") - .motd("Global Linking") - .subMotd("Server") - .playerCount(0) - .maximumPlayerCount(1) - .gameType("Survival") - .ipv4Port(config.bedrockPort()) - .protocolVersion(latestCodec.getProtocolVersion()) - .version(latestCodec.getMinecraftVersion()) - .serverId(ThreadLocalRandom.current().nextLong()), - new ServerInitializer()); - server.bind(new InetSocketAddress(config.bindIp(), config.bedrockPort())) - .awaitUninterruptibly(); - return true; - } - - @Override - public void shutdown() { - server.shutdown(); - server = null; - } - - class ServerInitializer extends BedrockServerInitializer { - @Override - protected void initSession(BedrockServerSession session) { - session.setPacketHandler(new PacketHandler(session, playerManager, linkManager)); - } - } -} diff --git a/src/main/java/org/geysermc/globallinkserver/bedrock/NettyServer.java b/src/main/java/org/geysermc/globallinkserver/bedrock/NettyServer.java deleted file mode 100644 index da3814b..0000000 --- a/src/main/java/org/geysermc/globallinkserver/bedrock/NettyServer.java +++ /dev/null @@ -1,64 +0,0 @@ -/* - * Copyright (c) 2021-2022 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/GlobalLinkServer - */ - -package org.geysermc.globallinkserver.bedrock; - -import io.netty.bootstrap.ServerBootstrap; -import io.netty.channel.ChannelFuture; -import io.netty.channel.EventLoopGroup; -import io.netty.channel.nio.NioEventLoopGroup; -import io.netty.channel.socket.nio.NioDatagramChannel; -import org.cloudburstmc.netty.channel.raknet.RakChannelFactory; -import org.cloudburstmc.netty.channel.raknet.config.RakChannelOption; -import org.cloudburstmc.protocol.bedrock.BedrockPong; -import org.cloudburstmc.protocol.bedrock.netty.initializer.BedrockServerInitializer; - -import java.net.InetSocketAddress; - -public class NettyServer { - private final EventLoopGroup group; - private final ServerBootstrap bootstrap; - - private ChannelFuture future; - - public NettyServer(BedrockPong pong, BedrockServerInitializer serverInitializer) { - group = new NioEventLoopGroup(); - bootstrap = new ServerBootstrap() - .channelFactory(RakChannelFactory.server(NioDatagramChannel.class)) - .option(RakChannelOption.RAK_GUID, pong.serverId()) - .option(RakChannelOption.RAK_ADVERTISEMENT, pong.toByteBuf()) - .group(group) - .childHandler(serverInitializer); - } - - public ChannelFuture bind(InetSocketAddress address) { - return future = bootstrap.bind(address); - } - - public void shutdown() { - group.shutdownGracefully(); - future.channel().closeFuture().syncUninterruptibly(); - } -} diff --git a/src/main/java/org/geysermc/globallinkserver/bedrock/PacketHandler.java b/src/main/java/org/geysermc/globallinkserver/bedrock/PacketHandler.java deleted file mode 100644 index ca13859..0000000 --- a/src/main/java/org/geysermc/globallinkserver/bedrock/PacketHandler.java +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright (c) 2021-2023 GeyserMC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/GlobalLinkServer - */ -package org.geysermc.globallinkserver.bedrock; - -import org.cloudburstmc.protocol.bedrock.BedrockServerSession; -import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec; -import org.cloudburstmc.protocol.bedrock.data.PacketCompressionAlgorithm; -import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket; -import org.cloudburstmc.protocol.bedrock.packet.BedrockPacketHandler; -import org.cloudburstmc.protocol.bedrock.packet.ClientCacheStatusPacket; -import org.cloudburstmc.protocol.bedrock.packet.CommandRequestPacket; -import org.cloudburstmc.protocol.bedrock.packet.LoginPacket; -import org.cloudburstmc.protocol.bedrock.packet.NetworkSettingsPacket; -import org.cloudburstmc.protocol.bedrock.packet.PlayStatusPacket; -import org.cloudburstmc.protocol.bedrock.packet.RequestNetworkSettingsPacket; -import org.cloudburstmc.protocol.bedrock.packet.ResourcePackClientResponsePacket; -import org.cloudburstmc.protocol.bedrock.packet.ResourcePackStackPacket; -import org.cloudburstmc.protocol.bedrock.packet.ResourcePacksInfoPacket; -import org.cloudburstmc.protocol.bedrock.packet.SetLocalPlayerAsInitializedPacket; -import org.cloudburstmc.protocol.bedrock.util.ChainValidationResult; -import org.cloudburstmc.protocol.common.PacketSignal; -import org.geysermc.globallinkserver.bedrock.util.BedrockVersionUtils; -import org.geysermc.globallinkserver.link.LinkManager; -import org.geysermc.globallinkserver.player.PlayerManager; -import org.geysermc.globallinkserver.util.CommandUtils; -import org.geysermc.globallinkserver.util.Utils; - -public class PacketHandler implements BedrockPacketHandler { - private final BedrockServerSession session; - private final PlayerManager playerManager; - private final LinkManager linkManager; - - private BedrockPlayer player; - private long lastCommand; - - /** - * In Protocol V554 and above, RequestNetworkSettingsPacket is sent before LoginPacket. - */ - private boolean networkSettingsRequested = false; - - public PacketHandler(BedrockServerSession session, PlayerManager playerManager, LinkManager linkManager) { - this.session = session; - this.playerManager = playerManager; - this.linkManager = linkManager; - } - - @Override - public void onDisconnect(String reason) { - if (player != null) { - playerManager.removeBedrockPlayer(player); - } - } - - @Override - public PacketSignal handlePacket(BedrockPacket packet) { - BedrockPacketHandler.super.handlePacket(packet); - return PacketSignal.HANDLED; // Avoids warning spam about all the packets we ignore and don't handle - } - - private boolean setCorrectCodec(int protocolVersion) { - BedrockCodec packetCodec = BedrockVersionUtils.bedrockCodec(protocolVersion); - if (packetCodec == null) { - // Protocol version is not supported - PlayStatusPacket status = new PlayStatusPacket(); - if (protocolVersion > BedrockVersionUtils.latestProtocolVersion()) { - status.setStatus(PlayStatusPacket.Status.LOGIN_FAILED_SERVER_OLD); - } else { - status.setStatus(PlayStatusPacket.Status.LOGIN_FAILED_CLIENT_OLD); - } - - session.sendPacketImmediately(status); - session.disconnect(); - return false; - } - - session.setCodec(packetCodec); - return true; - } - - @Override - public PacketSignal handle(RequestNetworkSettingsPacket packet) { - if (!setCorrectCodec(packet.getProtocolVersion())) { - return PacketSignal.HANDLED; // Unsupported version, client has been disconnected - } - - // New since 1.19.30 - sent before login packet - PacketCompressionAlgorithm algorithm = PacketCompressionAlgorithm.ZLIB; - - NetworkSettingsPacket responsePacket = new NetworkSettingsPacket(); - responsePacket.setCompressionAlgorithm(algorithm); - responsePacket.setCompressionThreshold(512); - session.sendPacketImmediately(responsePacket); - - session.setCompression(algorithm); - networkSettingsRequested = true; - return PacketSignal.HANDLED; - } - - @Override - public PacketSignal handle(LoginPacket packet) { - if (!networkSettingsRequested) { - // This is expected for pre-1.19.30 - PlayStatusPacket statusPacket = new PlayStatusPacket(); - statusPacket.setStatus(PlayStatusPacket.Status.LOGIN_FAILED_CLIENT_OLD); - session.sendPacketImmediately(statusPacket); - - session.disconnect(); - return PacketSignal.HANDLED; - } - - try { - ChainValidationResult.IdentityData extraData = - Utils.validateAndEncryptConnection(session, packet.getChain(), packet.getExtra()); - - PlayStatusPacket status = new PlayStatusPacket(); - status.setStatus(PlayStatusPacket.Status.LOGIN_SUCCESS); - session.sendPacket(status); - - ResourcePacksInfoPacket info = new ResourcePacksInfoPacket(); - session.sendPacket(info); - - player = playerManager.addBedrockPlayer(session, extraData); - } catch (AssertionError | Exception error) { - session.disconnect("disconnect.loginFailed"); - } - return PacketSignal.HANDLED; - } - - @Override - public PacketSignal handle(ClientCacheStatusPacket packet) { - return PacketSignal.HANDLED; - } - - @Override - public PacketSignal handle(ResourcePackClientResponsePacket packet) { - switch (packet.getStatus()) { - case COMPLETED: - player.sendStartGame(); - break; - case HAVE_ALL_PACKS: - ResourcePackStackPacket stack = new ResourcePackStackPacket(); - stack.setExperimentsPreviouslyToggled(false); - stack.setForcedToAccept(false); - stack.setGameVersion("*"); - session.sendPacket(stack); - break; - default: - session.disconnect("disconnectionScreen.resourcePack"); - break; - } - return PacketSignal.HANDLED; - } - - @Override - public PacketSignal handle(SetLocalPlayerAsInitializedPacket packet) { - player.sendJoinMessages(); - return PacketSignal.HANDLED; - } - - @Override - public PacketSignal handle(CommandRequestPacket packet) { - String message = packet.getCommand(); - if (message.startsWith("/")) { - long now = System.currentTimeMillis(); - if (now - lastCommand < 4_000) { - player.sendMessage("&cYou're sending commands too fast"); - } else { - lastCommand = now; - CommandUtils.handleCommand(linkManager, playerManager, player, message); - } - } else { - player.sendMessage("&7The darkness doesn't know how to respond to your message"); - } - return PacketSignal.HANDLED; - } -} diff --git a/src/main/java/org/geysermc/globallinkserver/bedrock/util/BedrockVersionUtils.java b/src/main/java/org/geysermc/globallinkserver/bedrock/util/BedrockVersionUtils.java deleted file mode 100644 index 02c8e8f..0000000 --- a/src/main/java/org/geysermc/globallinkserver/bedrock/util/BedrockVersionUtils.java +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2021-2024 GeyserMC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/GlobalLinkServer - */ -package org.geysermc.globallinkserver.bedrock.util; - -import java.util.ArrayList; -import java.util.List; -import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec; -import org.cloudburstmc.protocol.bedrock.codec.v589.Bedrock_v589; -import org.cloudburstmc.protocol.bedrock.codec.v594.Bedrock_v594; -import org.cloudburstmc.protocol.bedrock.codec.v618.Bedrock_v618; -import org.cloudburstmc.protocol.bedrock.codec.v622.Bedrock_v622; -import org.cloudburstmc.protocol.bedrock.codec.v630.Bedrock_v630; -import org.cloudburstmc.protocol.bedrock.codec.v649.Bedrock_v649; -import org.cloudburstmc.protocol.bedrock.codec.v662.Bedrock_v662; -import org.cloudburstmc.protocol.bedrock.codec.v671.Bedrock_v671; -import org.cloudburstmc.protocol.bedrock.codec.v685.Bedrock_v685; -import org.cloudburstmc.protocol.bedrock.codec.v686.Bedrock_v686; -import org.cloudburstmc.protocol.bedrock.codec.v712.Bedrock_v712; -import org.cloudburstmc.protocol.bedrock.codec.v729.Bedrock_v729; -import org.cloudburstmc.protocol.bedrock.codec.v748.Bedrock_v748; - -/** - * Contains information about the supported Bedrock protocols in GlobalLinkServer. - */ -public class BedrockVersionUtils { - /** - * A list of all supported Bedrock versions that can join GlobalLinkServer - */ - public static final List SUPPORTED_BEDROCK_CODECS = new ArrayList<>() {{ - add(Bedrock_v589.CODEC); - add(Bedrock_v594.CODEC); - add(Bedrock_v618.CODEC); - add(Bedrock_v622.CODEC); - add(Bedrock_v630.CODEC); - add(Bedrock_v649.CODEC); - add(Bedrock_v662.CODEC); - add(Bedrock_v671.CODEC); - add(Bedrock_v685.CODEC); - add(Bedrock_v686.CODEC); - add(Bedrock_v712.CODEC); - add(Bedrock_v729.CODEC); - add(Bedrock_v748.CODEC); - }}; - - /** - * Default Bedrock codec that should act as a fallback. Should represent the latest available - * release of the game that GlobalLinkServer supports. - */ - public static final BedrockCodec LATEST_CODEC = SUPPORTED_BEDROCK_CODECS.get(SUPPORTED_BEDROCK_CODECS.size() - 1); - - /** - * Gets the {@link BedrockCodec} of the given protocol version. - * @param protocolVersion The protocol version to attempt to find - * @return The packet codec, or null if the client's protocol is unsupported - */ - public static BedrockCodec bedrockCodec(int protocolVersion) { - for (BedrockCodec packetCodec : SUPPORTED_BEDROCK_CODECS) { - if (packetCodec.getProtocolVersion() == protocolVersion) { - return packetCodec; - } - } - return null; - } - - public static int latestProtocolVersion() { - return LATEST_CODEC.getProtocolVersion(); - } -} diff --git a/src/main/java/org/geysermc/globallinkserver/bedrock/util/PaletteUtils.java b/src/main/java/org/geysermc/globallinkserver/bedrock/util/PaletteUtils.java deleted file mode 100644 index c7b7f75..0000000 --- a/src/main/java/org/geysermc/globallinkserver/bedrock/util/PaletteUtils.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (c) 2021-2021 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/GlobalLinkServer - */ - -package org.geysermc.globallinkserver.bedrock.util; - -import org.cloudburstmc.nbt.*; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -/** - * This class is mostly copied from Geyser - */ -public class PaletteUtils { - public static final NbtMap BIOMES_PALETTE; - public static final byte[] EMPTY_LEVEL_CHUNK_DATA; - - private static final NbtMap EMPTY_TAG = NbtMap.EMPTY; - - static { - /* Load biomes */ - // Build a fake plains biome entry - NbtMapBuilder plainsBuilder = NbtMap.builder(); - plainsBuilder.putFloat("blue_spores", 0f); - plainsBuilder.putFloat("white_ash", 0f); - plainsBuilder.putFloat("ash", 0f); - plainsBuilder.putFloat("temperature", 0f); - plainsBuilder.putFloat("red_spores", 0f); - plainsBuilder.putFloat("downfall", 0f); - - plainsBuilder.put("minecraft:overworld_generation_rules", NbtMap.EMPTY); - plainsBuilder.put("minecraft:climate", NbtMap.EMPTY); - plainsBuilder.put("tags", NbtList.EMPTY); - - // Add the fake plains to the map - NbtMapBuilder biomesBuilder = NbtMap.builder(); - biomesBuilder.put("plains", plainsBuilder.build()); - - // Build the biomes palette - BIOMES_PALETTE = biomesBuilder.build(); - - /* Create empty chunk data */ - try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { - outputStream.write(new byte[258]); // Biomes + Border Size + Extra Data Size - - try (NBTOutputStream nbtOutputStream = NbtUtils.createNetworkWriter(outputStream)) { - nbtOutputStream.writeTag(EMPTY_TAG); - } - - EMPTY_LEVEL_CHUNK_DATA = outputStream.toByteArray(); - } catch (IOException e) { - throw new AssertionError("Unable to generate empty level chunk data"); - } - } -} diff --git a/src/main/java/org/geysermc/globallinkserver/config/Config.java b/src/main/java/org/geysermc/globallinkserver/config/Config.java index 465edc5..712eb21 100644 --- a/src/main/java/org/geysermc/globallinkserver/config/Config.java +++ b/src/main/java/org/geysermc/globallinkserver/config/Config.java @@ -1,37 +1,10 @@ /* - * Copyright (c) 2021-2023 GeyserMC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * @author GeyserMC + * Copyright (c) 2021-2025 GeyserMC + * Licensed under the MIT license * @link https://github.com/GeyserMC/GlobalLinkServer */ package org.geysermc.globallinkserver.config; -import com.google.gson.annotations.SerializedName; - public record Config( - @SerializedName("bind-ip") String bindIp, - @SerializedName("bind-port-java") int javaPort, - @SerializedName("bind-port-bedrock") int bedrockPort, // database related - String hostname, - String username, - String password, - String database) {} + String hostname, String username, String password, String database) {} diff --git a/src/main/java/org/geysermc/globallinkserver/config/ConfigReader.java b/src/main/java/org/geysermc/globallinkserver/config/ConfigReader.java index a7c186d..e1b7815 100644 --- a/src/main/java/org/geysermc/globallinkserver/config/ConfigReader.java +++ b/src/main/java/org/geysermc/globallinkserver/config/ConfigReader.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021-2024 GeyserMC + * Copyright (c) 2021-2025 GeyserMC * Licensed under the MIT license * @link https://github.com/GeyserMC/GlobalLinkServer */ diff --git a/src/main/java/org/geysermc/globallinkserver/java/JavaPlayer.java b/src/main/java/org/geysermc/globallinkserver/java/JavaPlayer.java deleted file mode 100644 index 81d9dea..0000000 --- a/src/main/java/org/geysermc/globallinkserver/java/JavaPlayer.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright (c) 2021-2024 GeyserMC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/GlobalLinkServer - */ -package org.geysermc.globallinkserver.java; - -import java.util.UUID; -import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; -import org.geysermc.globallinkserver.player.Player; -import org.geysermc.mcprotocollib.auth.GameProfile; -import org.geysermc.mcprotocollib.network.Session; -import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.ClientboundSystemChatPacket; -import org.geysermc.mcprotocollib.protocol.packet.login.clientbound.ClientboundLoginDisconnectPacket; - -public class JavaPlayer implements Player { - private static final LegacyComponentSerializer LEGACY_SERIALIZER = LegacyComponentSerializer.legacyAmpersand(); - - private final Session session; - private final GameProfile profile; - - private int linkId; - - public JavaPlayer(Session session, GameProfile profile) { - this.session = session; - this.profile = profile; - } - - @Override - public void sendMessage(String message) { - session.send(new ClientboundSystemChatPacket(LEGACY_SERIALIZER.deserialize(message), false)); - } - - @Override - public void disconnect(String reason) { - session.send(new ClientboundLoginDisconnectPacket(LEGACY_SERIALIZER.deserialize(reason))); - session.disconnect(LEGACY_SERIALIZER.deserialize(reason)); - } - - @Override - public UUID uniqueId() { - return profile.getId(); - } - - @Override - public String username() { - return profile.getName(); - } - - @Override - public int linkId() { - return linkId; - } - - @Override - public void linkId(int linkId) { - this.linkId = linkId; - } -} diff --git a/src/main/java/org/geysermc/globallinkserver/java/JavaServer.java b/src/main/java/org/geysermc/globallinkserver/java/JavaServer.java deleted file mode 100644 index ab3ec1d..0000000 --- a/src/main/java/org/geysermc/globallinkserver/java/JavaServer.java +++ /dev/null @@ -1,195 +0,0 @@ -/* - * Copyright (c) 2021-2024 GeyserMC - * Licensed under the MIT license - * @link https://github.com/GeyserMC/GlobalLinkServer - */ -package org.geysermc.globallinkserver.java; - -import static org.geysermc.mcprotocollib.protocol.codec.MinecraftCodec.CODEC; - -import java.util.BitSet; -import java.util.Collections; -import java.util.List; -import java.util.OptionalInt; -import net.kyori.adventure.key.Key; -import net.kyori.adventure.text.Component; -import org.cloudburstmc.math.vector.Vector3i; -import org.cloudburstmc.nbt.NbtMap; -import org.geysermc.globallinkserver.config.Config; -import org.geysermc.globallinkserver.link.LinkManager; -import org.geysermc.globallinkserver.player.PlayerManager; -import org.geysermc.mcprotocollib.auth.SessionService; -import org.geysermc.mcprotocollib.network.Server; -import org.geysermc.mcprotocollib.network.event.server.ServerAdapter; -import org.geysermc.mcprotocollib.network.event.server.ServerClosedEvent; -import org.geysermc.mcprotocollib.network.event.server.SessionAddedEvent; -import org.geysermc.mcprotocollib.network.event.session.ConnectedEvent; -import org.geysermc.mcprotocollib.network.tcp.TcpServer; -import org.geysermc.mcprotocollib.protocol.MinecraftConstants; -import org.geysermc.mcprotocollib.protocol.MinecraftProtocol; -import org.geysermc.mcprotocollib.protocol.data.game.command.CommandNode; -import org.geysermc.mcprotocollib.protocol.data.game.command.CommandParser; -import org.geysermc.mcprotocollib.protocol.data.game.command.CommandType; -import org.geysermc.mcprotocollib.protocol.data.game.command.properties.IntegerProperties; -import org.geysermc.mcprotocollib.protocol.data.game.entity.player.GameMode; -import org.geysermc.mcprotocollib.protocol.data.game.entity.player.PlayerSpawnInfo; -import org.geysermc.mcprotocollib.protocol.data.game.level.LightUpdateData; -import org.geysermc.mcprotocollib.protocol.data.game.level.block.BlockEntityInfo; -import org.geysermc.mcprotocollib.protocol.data.game.level.notify.GameEvent; -import org.geysermc.mcprotocollib.protocol.data.status.PlayerInfo; -import org.geysermc.mcprotocollib.protocol.data.status.ServerStatusInfo; -import org.geysermc.mcprotocollib.protocol.data.status.VersionInfo; -import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.ClientboundCommandsPacket; -import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.ClientboundLoginPacket; -import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.player.ClientboundPlayerAbilitiesPacket; -import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.player.ClientboundPlayerPositionPacket; -import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.level.ClientboundGameEventPacket; -import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.level.ClientboundLevelChunkWithLightPacket; -import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.level.ClientboundSetDefaultSpawnPositionPacket; - -public class JavaServer implements org.geysermc.globallinkserver.Server { - private final PlayerManager playerManager; - private final LinkManager linkManager; - - private final ClientboundLevelChunkWithLightPacket cachedChunk = cachedChunk(); - - private final ServerStatusInfo pong = new ServerStatusInfo( - Component.text("Global Link Server"), - new PlayerInfo(1, 0, Collections.emptyList()), - new VersionInfo(CODEC.getMinecraftVersion(), CODEC.getProtocolVersion()), - null, - false); - - private Server server; - - public JavaServer(PlayerManager playerManager, LinkManager linkManager) { - this.playerManager = playerManager; - this.linkManager = linkManager; - } - - @Override - public boolean startServer(Config config) { - if (server != null) { - return false; - } - - server = new TcpServer(config.bindIp(), config.javaPort(), MinecraftProtocol::new); - - server.setGlobalFlag(MinecraftConstants.SESSION_SERVICE_KEY, new SessionService()); - server.setGlobalFlag(MinecraftConstants.VERIFY_USERS_KEY, true); - server.setGlobalFlag(MinecraftConstants.SERVER_INFO_BUILDER_KEY, session -> pong); - server.setGlobalFlag(MinecraftConstants.SERVER_LOGIN_HANDLER_KEY, session -> { - session.send(new ClientboundCommandsPacket( - new CommandNode[] { - new CommandNode( - CommandType.ROOT, true, new int[] {1, 3}, OptionalInt.empty(), null, null, null, null), - new CommandNode( - CommandType.LITERAL, - true, - new int[] {2}, - OptionalInt.empty(), - "linkaccount", - null, - null, - null), - new CommandNode( - CommandType.ARGUMENT, - true, - new int[0], - OptionalInt.empty(), - "code", - CommandParser.INTEGER, - new IntegerProperties(0, 9999), - null), - new CommandNode( - CommandType.LITERAL, - true, - new int[0], - OptionalInt.empty(), - "unlinkaccount", - null, - null, - null) - }, - 0)); - - session.send(new ClientboundLoginPacket( - 0, - false, - new Key[] {Key.key("minecraft:the_end")}, - 1, - 0, - 0, - false, - false, - false, - new PlayerSpawnInfo( - 2, - Key.key("minecraft:the_end"), - 100, - GameMode.SPECTATOR, - GameMode.SPECTATOR, - false, - false, - null, - 100), - true)); - - session.send(new ClientboundPlayerAbilitiesPacket(false, false, true, false, 0f, 0f)); - - // this packet is also required to let our player spawn, but the location itself doesn't matter - session.send(new ClientboundSetDefaultSpawnPositionPacket(Vector3i.ZERO, 0)); - - // we have to listen to the teleport confirm on the PacketHandler to prevent respawn request packet spam, - // so send it after calling ConnectedEvent which adds the PacketHandler as listener - session.send(new ClientboundPlayerPositionPacket(0, 0, 0, 0, 0, 0)); - - // these packets are required since 1.20.3 - session.send(new ClientboundGameEventPacket(GameEvent.LEVEL_CHUNKS_LOAD_START, null)); - session.send(cachedChunk); - - // Manually call the connect event - session.callEvent(new ConnectedEvent(session)); - }); - server.setGlobalFlag(MinecraftConstants.SERVER_COMPRESSION_THRESHOLD, 256); // default - - server.addListener(new ServerAdapter() { - @Override - public void serverClosed(ServerClosedEvent event) { - super.serverClosed(event); - } - - @Override - public void sessionAdded(SessionAddedEvent event) { - event.getSession().addListener(new PacketHandler(event.getSession(), linkManager, playerManager)); - } - }); - - server.bind(); - return true; - } - - @Override - public void shutdown() { - server.close(); - server = null; - } - - private ClientboundLevelChunkWithLightPacket cachedChunk() { - // 8 bytes for every section: - // short - block count - // for both blocks and biomes: - // byte - bits per entry - // varint(1) - block ID (0, air) - // varint(1) - data length - // times 16 for the 16 chunk sections that the end biome has - byte[] chunkData = new byte[8 * 16]; - - // just setting everything to empty seems to do the trick - var lightData = - new LightUpdateData(new BitSet(), new BitSet(), new BitSet(), new BitSet(), List.of(), List.of()); - // same applies for the heightmaps - return new ClientboundLevelChunkWithLightPacket( - 0, 0, chunkData, NbtMap.EMPTY, new BlockEntityInfo[0], lightData); - } -} diff --git a/src/main/java/org/geysermc/globallinkserver/java/PacketHandler.java b/src/main/java/org/geysermc/globallinkserver/java/PacketHandler.java deleted file mode 100644 index 0cce0de..0000000 --- a/src/main/java/org/geysermc/globallinkserver/java/PacketHandler.java +++ /dev/null @@ -1,96 +0,0 @@ -/* - * Copyright (c) 2021-2024 GeyserMC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/GlobalLinkServer - */ -package org.geysermc.globallinkserver.java; - -import org.geysermc.globallinkserver.link.LinkManager; -import org.geysermc.globallinkserver.player.PlayerManager; -import org.geysermc.globallinkserver.util.CommandUtils; -import org.geysermc.mcprotocollib.auth.GameProfile; -import org.geysermc.mcprotocollib.network.Session; -import org.geysermc.mcprotocollib.network.event.session.ConnectedEvent; -import org.geysermc.mcprotocollib.network.event.session.DisconnectedEvent; -import org.geysermc.mcprotocollib.network.event.session.SessionAdapter; -import org.geysermc.mcprotocollib.network.packet.Packet; -import org.geysermc.mcprotocollib.protocol.MinecraftConstants; -import org.geysermc.mcprotocollib.protocol.packet.ingame.clientbound.entity.player.ClientboundSetHealthPacket; -import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.ServerboundChatCommandPacket; -import org.geysermc.mcprotocollib.protocol.packet.ingame.serverbound.level.ServerboundAcceptTeleportationPacket; - -public class PacketHandler extends SessionAdapter { - private final Session session; - private final LinkManager linkManager; - private final PlayerManager playerManager; - - private JavaPlayer player; - private long lastCommand; - - public PacketHandler(Session session, LinkManager linkManager, PlayerManager playerManager) { - this.session = session; - this.linkManager = linkManager; - this.playerManager = playerManager; - } - - @Override - public void packetReceived(Session session, Packet packet) { - try { - if (packet instanceof ServerboundChatCommandPacket) { - long now = System.currentTimeMillis(); - if (now - lastCommand < 4_000) { - player.sendMessage("&cYou're sending commands too fast"); - return; - } - lastCommand = now; - String message = "/" + ((ServerboundChatCommandPacket) packet).getCommand(); - CommandUtils.handleCommand(linkManager, playerManager, player, message); - } - - if (packet instanceof ServerboundAcceptTeleportationPacket) { - // if we keep the health on 0, the client will spam us respawn request packets :/ - session.send(new ClientboundSetHealthPacket(1, 0, 0)); - } - } catch (Exception e) { - e.printStackTrace(); - } - } - - @Override - public void connected(ConnectedEvent event) { - try { - GameProfile profile = event.getSession().getFlag(MinecraftConstants.PROFILE_KEY); - - player = playerManager.addJavaPlayer(session, profile); - player.sendJoinMessages(); - } catch (Exception e) { - e.printStackTrace(); - } - } - - @Override - public void disconnected(DisconnectedEvent event) { - if (player != null) { - playerManager.removeJavaPlayer(player); - } - } -} diff --git a/src/main/java/org/geysermc/globallinkserver/link/LinkManager.java b/src/main/java/org/geysermc/globallinkserver/link/LinkManager.java index d58fb66..f2094c8 100644 --- a/src/main/java/org/geysermc/globallinkserver/link/LinkManager.java +++ b/src/main/java/org/geysermc/globallinkserver/link/LinkManager.java @@ -1,25 +1,6 @@ /* - * Copyright (c) 2021-2023 GeyserMC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * @author GeyserMC + * Copyright (c) 2021-2025 GeyserMC + * Licensed under the MIT license * @link https://github.com/GeyserMC/GlobalLinkServer */ package org.geysermc.globallinkserver.link; @@ -31,17 +12,15 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.SQLException; -import java.util.Iterator; -import java.util.List; -import java.util.Random; +import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; import org.geysermc.globallinkserver.config.Config; -import org.geysermc.globallinkserver.java.JavaPlayer; -import org.geysermc.globallinkserver.player.Player; -import org.geysermc.globallinkserver.player.PlayerManager; +import org.geysermc.globallinkserver.util.Utils; import org.mariadb.jdbc.MariaDbPoolDataSource; public class LinkManager { @@ -52,6 +31,7 @@ public class LinkManager { private final MariaDbPoolDataSource dataSource; private final Random random = new Random(); + private final HashMap CURRENT_LINK_CODES = new HashMap<>(); public LinkManager(Config config) { try { @@ -65,18 +45,17 @@ public LinkManager(Config config) { public int createTempLink(Player player) { TempLink link = new TempLink(); - if (player instanceof JavaPlayer) { - link.javaId(player.uniqueId()); - link.javaUsername(player.username()); + if (Utils.isBedrockPlayer(player)) { + link.bedrockId(player.getUniqueId()); } else { - link.bedrockId(player.uniqueId()); + link.javaId(player.getUniqueId()); + link.javaUsername(player.getDisplayName()); } link.expiryTime(System.currentTimeMillis() + TEMP_LINK_DURATION); link.code(createCode()); tempLinks.put(link.code(), link); - - player.linkId(link.code()); + CURRENT_LINK_CODES.put(player.getUniqueId(), link.code()); return link.code(); } @@ -104,8 +83,11 @@ private boolean isLinkValid(TempLink link) { return link != null && currentMillis - link.expiryTime() < TEMP_LINK_DURATION; } - public void removeTempLink(int linkId) { - tempLinks.remove(linkId); + public void removeTempLinkIfPresent(Player player) { + Integer linkId = CURRENT_LINK_CODES.remove(player.getUniqueId()); + if (linkId != null) { + tempLinks.remove((int) linkId); + } } public CompletableFuture finaliseLink(TempLink tempLink) { @@ -136,12 +118,12 @@ public CompletableFuture unlinkAccount(Player player) { try (Connection connection = dataSource.getConnection()) { PreparedStatement query; - if (player instanceof JavaPlayer) { - query = connection.prepareStatement("DELETE FROM `links` WHERE `java_id` = ?;"); - query.setString(1, player.uniqueId().toString()); - } else { + if (Utils.isBedrockPlayer(player)) { query = connection.prepareStatement("DELETE FROM `links` WHERE `bedrock_id` = ?;"); - query.setLong(1, player.uniqueId().getLeastSignificantBits()); + query.setLong(1, player.getUniqueId().getLeastSignificantBits()); + } else { + query = connection.prepareStatement("DELETE FROM `links` WHERE `java_id` = ?;"); + query.setString(1, player.getUniqueId().toString()); } boolean affected = query.executeUpdate() != 0; query.close(); @@ -153,7 +135,7 @@ public CompletableFuture unlinkAccount(Player player) { executorService); } - public void cleanupTempLinks(PlayerManager playerManager) { + public void cleanupTempLinks() { IntSet removedLinks = new IntArraySet(); Iterator iterator = tempLinks.values().iterator(); @@ -168,12 +150,16 @@ public void cleanupTempLinks(PlayerManager playerManager) { } } - List players = playerManager.playersByTempLinkIds(removedLinks); - for (Player player : players) { - player.sendMessage(String.format( - "&cYour link (%s) has expired! Run the link account command again if you need a new code.", - player.linkId())); - player.linkId(0); + for (Map.Entry entry : CURRENT_LINK_CODES.entrySet()) { + if (removedLinks.contains((int) entry.getValue())) { + Player player = Bukkit.getPlayer(entry.getKey()); + int currentLinkCode = CURRENT_LINK_CODES.remove(entry.getKey()); + if (player != null) { + player.sendMessage(String.format( + "&cYour link (%s) has expired! Run the link account command again if you need a new code.", + currentLinkCode)); + } + } } } } diff --git a/src/main/java/org/geysermc/globallinkserver/player/Player.java b/src/main/java/org/geysermc/globallinkserver/player/Player.java deleted file mode 100644 index 5d31cf9..0000000 --- a/src/main/java/org/geysermc/globallinkserver/player/Player.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2021-2023 GeyserMC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/GlobalLinkServer - */ -package org.geysermc.globallinkserver.player; - -import java.util.UUID; - -public interface Player { - UUID uniqueId(); - - String username(); - - int linkId(); - - void linkId(int linkId); - - void sendMessage(String message); - - void disconnect(String reason); - - default void sendJoinMessages() { - sendMessage( - "&eTo start the linking process run `&9/linkaccount&e` or run `&9/linkaccount &3&e` to finish the process."); - sendMessage("&eTo unlink your account (if it is linked) run `&9/unlinkaccount&e`."); - } - - default String formatMessage(String message) { - return message.replace("&", "ยง"); - } -} diff --git a/src/main/java/org/geysermc/globallinkserver/player/PlayerManager.java b/src/main/java/org/geysermc/globallinkserver/player/PlayerManager.java deleted file mode 100644 index 5813113..0000000 --- a/src/main/java/org/geysermc/globallinkserver/player/PlayerManager.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright (c) 2021-2024 GeyserMC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * @author GeyserMC - * @link https://github.com/GeyserMC/GlobalLinkServer - */ -package org.geysermc.globallinkserver.player; - -import it.unimi.dsi.fastutil.ints.IntSet; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.UUID; -import org.cloudburstmc.protocol.bedrock.BedrockServerSession; -import org.cloudburstmc.protocol.bedrock.util.ChainValidationResult; -import org.geysermc.globallinkserver.bedrock.BedrockPlayer; -import org.geysermc.globallinkserver.java.JavaPlayer; -import org.geysermc.mcprotocollib.auth.GameProfile; -import org.geysermc.mcprotocollib.network.Session; - -public class PlayerManager { - private final Map javaPlayers = new HashMap<>(); - private final Map bedrockPlayers = new HashMap<>(); - - public BedrockPlayer addBedrockPlayer(BedrockServerSession session, ChainValidationResult.IdentityData identity) { - BedrockPlayer player = new BedrockPlayer(session, identity); - - BedrockPlayer old = bedrockPlayers.put(player.username(), player); - if (old != null) { - old.disconnect("You logged in from somewhere else"); - } - - return player; - } - - public JavaPlayer addJavaPlayer(Session session, GameProfile gameProfile) { - JavaPlayer player = new JavaPlayer(session, gameProfile); - - JavaPlayer old = javaPlayers.put(gameProfile.getName(), player); - if (old != null) { - old.disconnect("You logged in from somewhere else"); - } - - return player; - } - - public void removeJavaPlayer(JavaPlayer player) { - javaPlayers.remove(player.username(), player); - } - - public void removeBedrockPlayer(BedrockPlayer player) { - bedrockPlayers.remove(player.username(), player); - } - - public List playersByTempLinkIds(IntSet removedTempLinks) { - List players = new ArrayList<>(); - - for (JavaPlayer player : javaPlayers.values()) { - if (removedTempLinks.contains(player.linkId())) { - players.add(player); - } - } - for (BedrockPlayer player : bedrockPlayers.values()) { - if (removedTempLinks.contains(player.linkId())) { - players.add(player); - } - } - - return players; - } - - public void kickPlayers(UUID javaPlayer, UUID bedrockPlayer, String reason) { - for (JavaPlayer player : javaPlayers.values()) { - if (player.uniqueId().equals(javaPlayer)) { - player.disconnect(reason); - } - } - for (BedrockPlayer player : bedrockPlayers.values()) { - if (player.uniqueId().equals(bedrockPlayer)) { - player.disconnect(reason); - } - } - } -} diff --git a/src/main/java/org/geysermc/globallinkserver/util/CommandUtils.java b/src/main/java/org/geysermc/globallinkserver/util/CommandUtils.java index 2339ce2..c09be13 100644 --- a/src/main/java/org/geysermc/globallinkserver/util/CommandUtils.java +++ b/src/main/java/org/geysermc/globallinkserver/util/CommandUtils.java @@ -1,38 +1,17 @@ /* - * Copyright (c) 2021-2023 GeyserMC - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * @author GeyserMC + * Copyright (c) 2021-2025 GeyserMC + * Licensed under the MIT license * @link https://github.com/GeyserMC/GlobalLinkServer */ package org.geysermc.globallinkserver.util; -import org.geysermc.globallinkserver.java.JavaPlayer; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; import org.geysermc.globallinkserver.link.LinkManager; import org.geysermc.globallinkserver.link.TempLink; -import org.geysermc.globallinkserver.player.Player; -import org.geysermc.globallinkserver.player.PlayerManager; public class CommandUtils { - public static void handleCommand( - LinkManager linkManager, PlayerManager playerManager, Player player, String message) { + public static void handleCommand(LinkManager linkManager, Player player, String message) { String[] args = message.split(" "); @@ -52,11 +31,11 @@ public static void handleCommand( return; } - if (player instanceof JavaPlayer) { - tempLink.javaId(player.uniqueId()); - tempLink.javaUsername(player.username()); + if (Utils.isBedrockPlayer(player)) { + tempLink.bedrockId(player.getUniqueId()); } else { - tempLink.bedrockId(player.uniqueId()); + tempLink.javaId(player.getUniqueId()); + tempLink.javaUsername(player.getDisplayName()); } if (tempLink.javaId() == null || tempLink.bedrockId() == null) { @@ -76,20 +55,25 @@ public static void handleCommand( return; } - playerManager.kickPlayers( - tempLink.javaId(), tempLink.bedrockId(), "&aYou are now successfully linked! :)"); + Player javaPlayer = Bukkit.getPlayer(tempLink.javaId()); + Player bedrockPlayer = Bukkit.getPlayer(tempLink.bedrockId()); + + if (javaPlayer != null) { + javaPlayer.kickPlayer("&aYou are now successfully linked! :)"); + } + + if (bedrockPlayer != null) { + bedrockPlayer.kickPlayer("&aYou are now successfully linked! :)"); + } }); return; } if (args.length == 1) { - if (player.linkId() != 0) { - linkManager.removeTempLink(player.linkId()); - } + linkManager.removeTempLinkIfPresent(player); String code = String.format("%04d", linkManager.createTempLink(player)); - - String otherPlatform = player instanceof JavaPlayer ? "Bedrock" : "Java"; + String otherPlatform = Utils.isBedrockPlayer(player) ? "Java" : "Bedrock"; player.sendMessage("&aPlease join on " + otherPlatform + " and run `&9/linkaccount &3" + code + "&a`"); return; diff --git a/src/main/java/org/geysermc/globallinkserver/util/Utils.java b/src/main/java/org/geysermc/globallinkserver/util/Utils.java index 1441e9a..240ac59 100644 --- a/src/main/java/org/geysermc/globallinkserver/util/Utils.java +++ b/src/main/java/org/geysermc/globallinkserver/util/Utils.java @@ -1,39 +1,12 @@ /* - * Copyright (c) 2021-2021 GeyserMC. http://geysermc.org - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - * @author GeyserMC + * Copyright (c) 2021-2025 GeyserMC + * Licensed under the MIT license * @link https://github.com/GeyserMC/GlobalLinkServer */ - package org.geysermc.globallinkserver.util; -import org.cloudburstmc.protocol.bedrock.BedrockServerSession; -import org.cloudburstmc.protocol.bedrock.packet.ServerToClientHandshakePacket; -import org.cloudburstmc.protocol.bedrock.util.ChainValidationResult; -import org.cloudburstmc.protocol.bedrock.util.EncryptionUtils; - -import javax.crypto.SecretKey; -import java.security.KeyPair; -import java.security.PublicKey; -import java.util.List; +import org.bukkit.entity.Player; +import org.geysermc.geyser.api.GeyserApi; public class Utils { @@ -45,40 +18,7 @@ public static int parseInt(String toParse) { } } - public static long parseLong(String toParse) { - try { - return Long.parseLong(toParse); - } catch (NumberFormatException exception) { - return -1; - } - } - - public static ChainValidationResult.IdentityData validateAndEncryptConnection(BedrockServerSession session, List certChainData, String clientDataJwt) throws Exception { - ChainValidationResult result = EncryptionUtils.validateChain(certChainData); - if (!result.signed()) { - throw new IllegalArgumentException("Chain is not signed"); - } - PublicKey identityPublicKey = result.identityClaims().parsedIdentityPublicKey(); - - byte[] clientDataPayload = EncryptionUtils.verifyClientData(clientDataJwt, identityPublicKey); - if (clientDataPayload == null) { - throw new IllegalStateException("Client data isn't signed by the given chain data"); - } - - startEncryptionHandshake(session, identityPublicKey); - - return result.identityClaims().extraData; - } - - private static void startEncryptionHandshake(BedrockServerSession session, PublicKey key) throws Exception { - KeyPair serverKeyPair = EncryptionUtils.createKeyPair(); - byte[] token = EncryptionUtils.generateRandomToken(); - - ServerToClientHandshakePacket packet = new ServerToClientHandshakePacket(); - packet.setJwt(EncryptionUtils.createHandshakeJwt(serverKeyPair, token)); - session.sendPacketImmediately(packet); - - SecretKey encryptionKey = EncryptionUtils.getSecretKey(serverKeyPair.getPrivate(), key, token); - session.enableEncryption(encryptionKey); + public static boolean isBedrockPlayer(Player player) { + return GeyserApi.api().isBedrockPlayer(player.getUniqueId()); } } diff --git a/src/main/resources/plugin.yml b/src/main/resources/plugin.yml new file mode 100644 index 0000000..397d717 --- /dev/null +++ b/src/main/resources/plugin.yml @@ -0,0 +1,6 @@ +main: org.geysermc.globallinkserver.GlobalLinkServer +name: GlobalLinkServerPlugin +author: GeyserMC +website: link.geysermc.org +version: 1.0.0 +api-version: 1.13