From 09e3793fb27f3cdae86b91eecc704ff1911a3c3d Mon Sep 17 00:00:00 2001 From: Camotoy <20743703+Camotoy@users.noreply.github.com> Date: Fri, 12 Nov 2021 09:02:14 -0500 Subject: [PATCH] Refactor GeyserSession tracking for better concurrency --- .../spigot/world/GeyserPistonListener.java | 7 +- .../GeyserSpigot1_11CraftingListener.java | 2 +- .../world/GeyserSpigotBlockPlaceListener.java | 41 ++++---- .../standalone/gui/GeyserStandaloneGUI.java | 2 +- .../geysermc/connector/GeyserConnector.java | 84 +++++------------ .../geysermc/connector/SessionManager.java | 94 +++++++++++++++++++ .../connector/command/CommandExecutor.java | 2 +- .../command/defaults/ListCommand.java | 4 +- .../command/defaults/ReloadCommand.java | 6 +- .../org/geysermc/connector/dump/DumpInfo.java | 2 +- .../network/ConnectorServerEventHandler.java | 2 +- .../connector/network/QueryPacketHandler.java | 2 +- .../network/UpstreamPacketHandler.java | 8 ++ .../network/session/GeyserSession.java | 11 +-- .../entity/player/BedrockEmoteTranslator.java | 2 +- .../java/JavaLoginSuccessTranslator.java | 2 + .../scoreboard/ScoreboardUpdater.java | 8 +- .../geysermc/connector/utils/NewsHandler.java | 2 +- 18 files changed, 169 insertions(+), 112 deletions(-) create mode 100644 connector/src/main/java/org/geysermc/connector/SessionManager.java diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserPistonListener.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserPistonListener.java index 73d0300ff..39fefb2de 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserPistonListener.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserPistonListener.java @@ -49,6 +49,8 @@ import org.geysermc.connector.utils.Direction; import org.geysermc.platform.spigot.world.manager.GeyserSpigotWorldManager; import java.util.List; +import java.util.Map; +import java.util.UUID; public class GeyserPistonListener implements Listener { private final GeyserConnector connector; @@ -86,11 +88,12 @@ public class GeyserPistonListener implements Listener { Object2IntMap attachedBlocks = new Object2IntOpenHashMap<>(); boolean blocksFilled = false; - for (GeyserSession session : connector.getPlayers()) { - Player player = Bukkit.getPlayer(session.getPlayerEntity().getUuid()); + for (Map.Entry entry : connector.getSessionManager().getSessions().entrySet()) { + Player player = Bukkit.getPlayer(entry.getKey()); if (player == null || !player.getWorld().equals(world)) { continue; } + GeyserSession session = entry.getValue(); int dX = Math.abs(location.getBlockX() - player.getLocation().getBlockX()) >> 4; int dZ = Math.abs(location.getBlockZ() - player.getLocation().getBlockZ()) >> 4; diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigot1_11CraftingListener.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigot1_11CraftingListener.java index f7a9d6df7..1fa5ef87b 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigot1_11CraftingListener.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigot1_11CraftingListener.java @@ -80,7 +80,7 @@ public class GeyserSpigot1_11CraftingListener implements Listener { @EventHandler public void onPlayerJoin(PlayerJoinEvent event) { GeyserSession session = null; - for (GeyserSession otherSession : connector.getPlayers()) { + for (GeyserSession otherSession : connector.getSessionManager().getSessions().values()) { if (otherSession.getName().equals(event.getPlayer().getName())) { session = otherSession; break; diff --git a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotBlockPlaceListener.java b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotBlockPlaceListener.java index 8111c7e56..8e3dfe075 100644 --- a/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotBlockPlaceListener.java +++ b/bootstrap/spigot/src/main/java/org/geysermc/platform/spigot/world/GeyserSpigotBlockPlaceListener.java @@ -29,7 +29,6 @@ import com.nukkitx.math.vector.Vector3f; import com.nukkitx.protocol.bedrock.data.SoundEvent; import com.nukkitx.protocol.bedrock.packet.LevelSoundEventPacket; import lombok.AllArgsConstructor; -import org.bukkit.Bukkit; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.block.BlockPlaceEvent; @@ -41,32 +40,30 @@ import org.geysermc.platform.spigot.world.manager.GeyserSpigotWorldManager; @AllArgsConstructor public class GeyserSpigotBlockPlaceListener implements Listener { - private final GeyserConnector connector; private final GeyserSpigotWorldManager worldManager; @EventHandler public void place(final BlockPlaceEvent event) { - for (GeyserSession session : connector.getPlayers()) { - if (event.getPlayer() == Bukkit.getPlayer(session.getPlayerEntity().getUsername())) { - LevelSoundEventPacket placeBlockSoundPacket = new LevelSoundEventPacket(); - placeBlockSoundPacket.setSound(SoundEvent.PLACE); - placeBlockSoundPacket.setPosition(Vector3f.from(event.getBlockPlaced().getX(), event.getBlockPlaced().getY(), event.getBlockPlaced().getZ())); - placeBlockSoundPacket.setBabySound(false); - if (worldManager.isLegacy()) { - placeBlockSoundPacket.setExtraData(session.getBlockMappings().getBedrockBlockId(worldManager.getBlockAt(session, - event.getBlockPlaced().getX(), event.getBlockPlaced().getY(), event.getBlockPlaced().getZ()))); - } else { - String javaBlockId = event.getBlockPlaced().getBlockData().getAsString(); - placeBlockSoundPacket.setExtraData(session.getBlockMappings().getBedrockBlockId(BlockRegistries.JAVA_IDENTIFIERS.get().getOrDefault(javaBlockId, BlockStateValues.JAVA_AIR_ID))); - } - placeBlockSoundPacket.setIdentifier(":"); - session.sendUpstreamPacket(placeBlockSoundPacket); - session.setLastBlockPlacePosition(null); - session.setLastBlockPlacedId(null); - break; - } + GeyserSession session = connector.getPlayerByUuid(event.getPlayer().getUniqueId()); + if (session == null) { + return; } - } + LevelSoundEventPacket placeBlockSoundPacket = new LevelSoundEventPacket(); + placeBlockSoundPacket.setSound(SoundEvent.PLACE); + placeBlockSoundPacket.setPosition(Vector3f.from(event.getBlockPlaced().getX(), event.getBlockPlaced().getY(), event.getBlockPlaced().getZ())); + placeBlockSoundPacket.setBabySound(false); + if (worldManager.isLegacy()) { + placeBlockSoundPacket.setExtraData(session.getBlockMappings().getBedrockBlockId(worldManager.getBlockAt(session, + event.getBlockPlaced().getX(), event.getBlockPlaced().getY(), event.getBlockPlaced().getZ()))); + } else { + String javaBlockId = event.getBlockPlaced().getBlockData().getAsString(); + placeBlockSoundPacket.setExtraData(session.getBlockMappings().getBedrockBlockId(BlockRegistries.JAVA_IDENTIFIERS.get().getOrDefault(javaBlockId, BlockStateValues.JAVA_AIR_ID))); + } + placeBlockSoundPacket.setIdentifier(":"); + session.sendUpstreamPacket(placeBlockSoundPacket); + session.setLastBlockPlacePosition(null); + session.setLastBlockPlacedId(null); + } } diff --git a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/gui/GeyserStandaloneGUI.java b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/gui/GeyserStandaloneGUI.java index 3636dded8..e44d08b9e 100644 --- a/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/gui/GeyserStandaloneGUI.java +++ b/bootstrap/standalone/src/main/java/org/geysermc/platform/standalone/gui/GeyserStandaloneGUI.java @@ -306,7 +306,7 @@ public class GeyserStandaloneGUI { // Update player table playerTableModel.getDataVector().removeAllElements(); - for (GeyserSession player : GeyserConnector.getInstance().getPlayers()) { + for (GeyserSession player : GeyserConnector.getInstance().getSessionManager().getSessions().values()) { Vector row = new Vector<>(); row.add(player.getSocketAddress().getHostName()); row.add(player.getPlayerEntity().getUsername()); diff --git a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java index 17deca915..70550eedd 100644 --- a/connector/src/main/java/org/geysermc/connector/GeyserConnector.java +++ b/connector/src/main/java/org/geysermc/connector/GeyserConnector.java @@ -52,7 +52,6 @@ import org.geysermc.connector.registry.Registries; import org.geysermc.connector.network.translators.PacketTranslatorRegistry; import org.geysermc.connector.network.translators.item.ItemTranslator; import org.geysermc.connector.network.translators.world.WorldManager; -import org.geysermc.connector.network.translators.world.block.entity.SkullBlockEntityTranslator; import org.geysermc.connector.scoreboard.ScoreboardUpdater; import org.geysermc.connector.skin.FloodgateSkinUploader; import org.geysermc.connector.utils.*; @@ -72,10 +71,7 @@ import java.net.UnknownHostException; import java.security.Key; import java.text.DecimalFormat; import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -100,7 +96,7 @@ public class GeyserConnector { private static final String IP_REGEX = "\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b"; - private final List players = new ArrayList<>(); + private final SessionManager sessionManager = new SessionManager(); private static GeyserConnector instance; @@ -115,7 +111,7 @@ public class GeyserConnector { private FloodgateSkinUploader skinUploader; private final NewsHandler newsHandler; - private boolean shuttingDown = false; + private volatile boolean shuttingDown = false; private final ScheduledExecutorService generalThreadPool; @@ -123,7 +119,7 @@ public class GeyserConnector { private final PlatformType platformType; private final GeyserBootstrap bootstrap; - private Metrics metrics; + private final Metrics metrics; private GeyserConnector(PlatformType platformType, GeyserBootstrap bootstrap) { long startupTime = System.currentTimeMillis(); @@ -277,7 +273,7 @@ public class GeyserConnector { if (config.getMetrics().isEnabled()) { metrics = new Metrics(this, "GeyserMC", config.getMetrics().getUniqueId(), false, java.util.logging.Logger.getLogger("")); - metrics.addCustomChart(new Metrics.SingleLineChart("players", players::size)); + metrics.addCustomChart(new Metrics.SingleLineChart("players", sessionManager::size)); // Prevent unwanted words best we can metrics.addCustomChart(new Metrics.SimplePie("authMode", () -> config.getRemote().getAuthType().toString().toLowerCase())); metrics.addCustomChart(new Metrics.SimplePie("platform", platformType::getPlatformName)); @@ -285,7 +281,7 @@ public class GeyserConnector { metrics.addCustomChart(new Metrics.SimplePie("version", () -> GeyserConnector.VERSION)); metrics.addCustomChart(new Metrics.AdvancedPie("playerPlatform", () -> { Map valueMap = new HashMap<>(); - for (GeyserSession session : players) { + for (GeyserSession session : sessionManager.getAllSessions()) { if (session == null) continue; if (session.getClientData() == null) continue; String os = session.getClientData().getDeviceOs().toString(); @@ -299,7 +295,7 @@ public class GeyserConnector { })); metrics.addCustomChart(new Metrics.AdvancedPie("playerVersion", () -> { Map valueMap = new HashMap<>(); - for (GeyserSession session : players) { + for (GeyserSession session : sessionManager.getAllSessions()) { if (session == null) continue; if (session.getClientData() == null) continue; String version = session.getClientData().getGameVersion(); @@ -359,6 +355,8 @@ public class GeyserConnector { map.put(release, entry); return map; })); + } else { + metrics = null; } boolean isGui = false; @@ -392,40 +390,10 @@ public class GeyserConnector { bootstrap.getGeyserLogger().info(LanguageUtils.getLocaleStringLog("geyser.core.shutdown")); shuttingDown = true; - if (players.size() >= 1) { - bootstrap.getGeyserLogger().info(LanguageUtils.getLocaleStringLog("geyser.core.shutdown.kick.log", players.size())); - - // Make a copy to prevent ConcurrentModificationException - final List tmpPlayers = new ArrayList<>(players); - for (GeyserSession playerSession : tmpPlayers) { - playerSession.disconnect(LanguageUtils.getPlayerLocaleString("geyser.core.shutdown.kick.message", playerSession.getLocale())); - } - - CompletableFuture future = CompletableFuture.runAsync(new Runnable() { - @Override - public void run() { - // Simulate a long-running Job - try { - while (true) { - if (players.size() == 0) { - return; - } - - TimeUnit.MILLISECONDS.sleep(100); - } - } catch (InterruptedException e) { - throw new IllegalStateException(e); - } - } - }); - - // Block and wait for the future to complete - try { - future.get(); - bootstrap.getGeyserLogger().info(LanguageUtils.getLocaleStringLog("geyser.core.shutdown.kick.done")); - } catch (Exception e) { - // Quietly fail - } + if (sessionManager.size() >= 1) { + bootstrap.getGeyserLogger().info(LanguageUtils.getLocaleStringLog("geyser.core.shutdown.kick.log", sessionManager.size())); + sessionManager.disconnectAll("geyser.core.shutdown.kick.message"); + bootstrap.getGeyserLogger().info(LanguageUtils.getLocaleStringLog("geyser.core.shutdown.kick.done")); } generalThreadPool.shutdown(); @@ -437,20 +405,11 @@ public class GeyserConnector { skinUploader.close(); } newsHandler.shutdown(); - players.clear(); this.getCommandManager().getCommands().clear(); bootstrap.getGeyserLogger().info(LanguageUtils.getLocaleStringLog("geyser.core.shutdown.done")); } - public void addPlayer(GeyserSession player) { - players.add(player); - } - - public void removePlayer(GeyserSession player) { - players.remove(player); - } - /** * Gets a player by their current UUID * @@ -463,13 +422,7 @@ public class GeyserConnector { return null; } - for (GeyserSession session : players) { - if (uuid.equals(session.getPlayerEntity().getUuid())) { - return session; - } - } - - return null; + return sessionManager.getSessions().get(uuid); } /** @@ -480,8 +433,13 @@ public class GeyserConnector { */ @SuppressWarnings("unused") // API usage public GeyserSession getPlayerByXuid(String xuid) { - for (GeyserSession session : players) { - if (session.getAuthData() != null && session.getAuthData().getXboxUUID().equals(xuid)) { + for (GeyserSession session : sessionManager.getPendingSessions()) { + if (session.getAuthData().getXboxUUID().equals(xuid)) { + return session; + } + } + for (GeyserSession session : sessionManager.getSessions().values()) { + if (session.getAuthData().getXboxUUID().equals(xuid)) { return session; } } diff --git a/connector/src/main/java/org/geysermc/connector/SessionManager.java b/connector/src/main/java/org/geysermc/connector/SessionManager.java new file mode 100644 index 000000000..a2eb6d873 --- /dev/null +++ b/connector/src/main/java/org/geysermc/connector/SessionManager.java @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2019-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/Geyser + */ + +package org.geysermc.connector; + +import com.google.common.collect.ImmutableList; +import lombok.AccessLevel; +import lombok.Getter; +import org.geysermc.connector.network.session.GeyserSession; +import org.geysermc.connector.utils.LanguageUtils; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +public final class SessionManager { + /** + * A list of all players who don't currently have a permanent UUID attached yet. + */ + @Getter(AccessLevel.PACKAGE) + private final Set pendingSessions = ConcurrentHashMap.newKeySet(); + /** + * A list of all players who are currently in-game. + */ + @Getter + private final Map sessions = new ConcurrentHashMap<>(); + + /** + * Called once the player has successfully authenticated to the Geyser server. + */ + public void addPendingSession(GeyserSession session) { + pendingSessions.add(session); + } + + /** + * Called once a player has successfully logged into their Java server. + */ + public void addSession(UUID uuid, GeyserSession session) { + pendingSessions.remove(session); + sessions.put(uuid, session); + } + + public void removeSession(GeyserSession session) { + if (sessions.remove(session.getPlayerEntity().getUuid()) == null) { + // Session was likely pending + pendingSessions.remove(session); + } + } + + /** + * Creates a new, immutable list containing all pending and active sessions. + */ + public Collection getAllSessions() { + return ImmutableList.builder() // builderWithExpectedSize is probably not a good idea yet as older Spigot builds probably won't have it. + .addAll(pendingSessions) + .addAll(sessions.values()) + .build(); + } + + public void disconnectAll(String message) { + Collection sessions = getAllSessions(); + for (GeyserSession session : sessions) { + session.disconnect(LanguageUtils.getPlayerLocaleString(message, session.getLocale())); + } + } + + /** + * @return the total amount of sessions, including those pending. + */ + public int size() { + return pendingSessions.size() + sessions.size(); + } +} diff --git a/connector/src/main/java/org/geysermc/connector/command/CommandExecutor.java b/connector/src/main/java/org/geysermc/connector/command/CommandExecutor.java index 26dd8125a..d8b023529 100644 --- a/connector/src/main/java/org/geysermc/connector/command/CommandExecutor.java +++ b/connector/src/main/java/org/geysermc/connector/command/CommandExecutor.java @@ -53,7 +53,7 @@ public class CommandExecutor { return null; } - for (GeyserSession session : connector.getPlayers()) { + for (GeyserSession session : connector.getSessionManager().getSessions().values()) { if (sender.getName().equals(session.getPlayerEntity().getUsername())) { return session; } diff --git a/connector/src/main/java/org/geysermc/connector/command/defaults/ListCommand.java b/connector/src/main/java/org/geysermc/connector/command/defaults/ListCommand.java index 915a062a7..3058f2982 100644 --- a/connector/src/main/java/org/geysermc/connector/command/defaults/ListCommand.java +++ b/connector/src/main/java/org/geysermc/connector/command/defaults/ListCommand.java @@ -46,8 +46,8 @@ public class ListCommand extends GeyserCommand { @Override public void execute(GeyserSession session, CommandSender sender, String[] args) { String message = LanguageUtils.getPlayerLocaleString("geyser.commands.list.message", sender.getLocale(), - connector.getPlayers().size(), - connector.getPlayers().stream().map(GeyserSession::getName).collect(Collectors.joining(" "))); + connector.getSessionManager().size(), + connector.getSessionManager().getAllSessions().stream().map(GeyserSession::getName).collect(Collectors.joining(" "))); sender.sendMessage(message); } diff --git a/connector/src/main/java/org/geysermc/connector/command/defaults/ReloadCommand.java b/connector/src/main/java/org/geysermc/connector/command/defaults/ReloadCommand.java index d3f3d2f3f..3fa5adc31 100644 --- a/connector/src/main/java/org/geysermc/connector/command/defaults/ReloadCommand.java +++ b/connector/src/main/java/org/geysermc/connector/command/defaults/ReloadCommand.java @@ -32,8 +32,6 @@ import org.geysermc.connector.command.GeyserCommand; import org.geysermc.connector.network.session.GeyserSession; import org.geysermc.connector.utils.LanguageUtils; -import java.util.ArrayList; - public class ReloadCommand extends GeyserCommand { private final GeyserConnector connector; @@ -53,9 +51,7 @@ public class ReloadCommand extends GeyserCommand { sender.sendMessage(message); - for (GeyserSession otherSession : new ArrayList<>(connector.getPlayers())) { - otherSession.disconnect(LanguageUtils.getPlayerLocaleString("geyser.commands.reload.kick", otherSession.getLocale())); - } + connector.getSessionManager().disconnectAll("geyser.commands.reload.kick"); connector.reload(); } } diff --git a/connector/src/main/java/org/geysermc/connector/dump/DumpInfo.java b/connector/src/main/java/org/geysermc/connector/dump/DumpInfo.java index 3f566ee26..4d8eeefaf 100644 --- a/connector/src/main/java/org/geysermc/connector/dump/DumpInfo.java +++ b/connector/src/main/java/org/geysermc/connector/dump/DumpInfo.java @@ -114,7 +114,7 @@ public class DumpInfo { } this.userPlatforms = new Object2IntOpenHashMap<>(); - for (GeyserSession session : GeyserConnector.getInstance().getPlayers()) { + for (GeyserSession session : GeyserConnector.getInstance().getSessionManager().getAllSessions()) { DeviceOs device = session.getClientData().getDeviceOs(); userPlatforms.put(device, userPlatforms.getOrDefault(device, 0) + 1); } diff --git a/connector/src/main/java/org/geysermc/connector/network/ConnectorServerEventHandler.java b/connector/src/main/java/org/geysermc/connector/network/ConnectorServerEventHandler.java index dcc3cb50b..65f1d8d7d 100644 --- a/connector/src/main/java/org/geysermc/connector/network/ConnectorServerEventHandler.java +++ b/connector/src/main/java/org/geysermc/connector/network/ConnectorServerEventHandler.java @@ -124,7 +124,7 @@ public class ConnectorServerEventHandler implements BedrockServerEventHandler { pong.setPlayerCount(pingInfo.getPlayers().getOnline()); pong.setMaximumPlayerCount(pingInfo.getPlayers().getMax()); } else { - pong.setPlayerCount(connector.getPlayers().size()); + pong.setPlayerCount(connector.getSessionManager().getSessions().size()); pong.setMaximumPlayerCount(config.getMaxPlayers()); } diff --git a/connector/src/main/java/org/geysermc/connector/network/QueryPacketHandler.java b/connector/src/main/java/org/geysermc/connector/network/QueryPacketHandler.java index 8d3aeb435..89e2669bb 100644 --- a/connector/src/main/java/org/geysermc/connector/network/QueryPacketHandler.java +++ b/connector/src/main/java/org/geysermc/connector/network/QueryPacketHandler.java @@ -159,7 +159,7 @@ public class QueryPacketHandler { currentPlayerCount = String.valueOf(pingInfo.getPlayers().getOnline()); maxPlayerCount = String.valueOf(pingInfo.getPlayers().getMax()); } else { - currentPlayerCount = String.valueOf(connector.getPlayers().size()); + currentPlayerCount = String.valueOf(connector.getSessionManager().getSessions().size()); maxPlayerCount = String.valueOf(connector.getConfig().getMaxPlayers()); } diff --git a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java index 6915e4575..d8c2adef9 100644 --- a/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java +++ b/connector/src/main/java/org/geysermc/connector/network/UpstreamPacketHandler.java @@ -59,6 +59,12 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { @Override public boolean handle(LoginPacket loginPacket) { + if (connector.isShuttingDown()) { + // Don't allow new players in if we're no longer operating + session.disconnect(LanguageUtils.getLocaleStringLog("geyser.core.shutdown.kick.message")); + return true; + } + BedrockPacketCodec packetCodec = BedrockProtocol.getBedrockCodec(loginPacket.getProtocolVersion()); if (packetCodec == null) { String supportedVersions = BedrockProtocol.getAllSupportedVersions(); @@ -86,6 +92,8 @@ public class UpstreamPacketHandler extends LoggingPacketHandler { playStatus.setStatus(PlayStatusPacket.Status.LOGIN_SUCCESS); session.sendUpstreamPacket(playStatus); + connector.getSessionManager().addPendingSession(session); + ResourcePacksInfoPacket resourcePacksInfo = new ResourcePacksInfoPacket(); for(ResourcePack resourcePack : ResourcePack.PACKS.values()) { ResourcePackManifest.Header header = resourcePack.getManifest().getHeader(); diff --git a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java index db4d1fd8f..d9e95e9a9 100644 --- a/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java +++ b/connector/src/main/java/org/geysermc/connector/network/session/GeyserSession.java @@ -486,9 +486,7 @@ public class GeyserSession implements CommandSender { if (connector.getConfig().getEmoteOffhandWorkaround() != EmoteOffhandWorkaroundOption.NO_EMOTES) { this.emotes = new HashSet<>(); - // Make a copy to prevent ConcurrentModificationException - final List tmpPlayers = new ArrayList<>(connector.getPlayers()); - tmpPlayers.forEach(player -> this.emotes.addAll(player.getEmotes())); + connector.getSessionManager().getSessions().values().forEach(player -> this.emotes.addAll(player.getEmotes())); } else { this.emotes = null; } @@ -498,7 +496,7 @@ public class GeyserSession implements CommandSender { connector.getLogger().info(LanguageUtils.getLocaleStringLog("geyser.network.disconnect", address, disconnectReason)); disconnect(disconnectReason.name()); - connector.removePlayer(this); + connector.getSessionManager().removeSession(this); }); } @@ -911,7 +909,6 @@ public class GeyserSession implements CommandSender { if (!internalConnect) { downstream.connect(); } - connector.addPlayer(this); } public void disconnect(String reason) { @@ -921,7 +918,7 @@ public class GeyserSession implements CommandSender { downstream.disconnect(reason); } if (upstream != null && !upstream.isClosed()) { - connector.getPlayers().remove(this); + connector.getSessionManager().removeSession(this); upstream.disconnect(reason); } } @@ -1442,7 +1439,7 @@ public class GeyserSession implements CommandSender { public void refreshEmotes(List emotes) { this.emotes.addAll(emotes); - for (GeyserSession player : connector.getPlayers()) { + for (GeyserSession player : connector.getSessionManager().getSessions().values()) { List pieces = new ArrayList<>(); for (UUID piece : emotes) { if (!player.getEmotes().contains(piece)) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockEmoteTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockEmoteTranslator.java index a20c3872c..e53167301 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockEmoteTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/bedrock/entity/player/BedrockEmoteTranslator.java @@ -53,7 +53,7 @@ public class BedrockEmoteTranslator extends PacketTranslator { } long javaId = session.getPlayerEntity().getEntityId(); - for (GeyserSession otherSession : session.getConnector().getPlayers()) { + for (GeyserSession otherSession : session.getConnector().getSessionManager().getSessions().values()) { if (otherSession != session) { if (otherSession.isClosed()) continue; if (otherSession.getEventLoop().inEventLoop()) { diff --git a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaLoginSuccessTranslator.java b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaLoginSuccessTranslator.java index d7675bd07..e3350c5eb 100644 --- a/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaLoginSuccessTranslator.java +++ b/connector/src/main/java/org/geysermc/connector/network/translators/java/JavaLoginSuccessTranslator.java @@ -47,6 +47,8 @@ public class JavaLoginSuccessTranslator extends PacketTranslator sessions = connector.getSessionManager().getSessions().values(); if (currentTime - lastPacketsPerSecondUpdate >= 1000) { lastPacketsPerSecondUpdate = currentTime; - for (GeyserSession session : connector.getPlayers()) { + for (GeyserSession session : sessions) { ScoreboardSession scoreboardSession = session.getWorldCache().getScoreboardSession(); int oldPps = scoreboardSession.getPacketsPerSecond(); @@ -94,7 +96,7 @@ public final class ScoreboardUpdater extends Thread { if (currentTime - lastUpdate >= FIRST_MILLIS_BETWEEN_UPDATES) { lastUpdate = currentTime; - for (GeyserSession session : connector.getPlayers()) { + for (GeyserSession session : sessions) { WorldCache worldCache = session.getWorldCache(); ScoreboardSession scoreboardSession = worldCache.getScoreboardSession(); @@ -132,7 +134,7 @@ public final class ScoreboardUpdater extends Thread { if (timeSpent > 0) { connector.getLogger().info(String.format( "Scoreboard updater: took %s ms. Updated %s players", - timeSpent, connector.getPlayers().size() + timeSpent, sessions.size() )); } } diff --git a/connector/src/main/java/org/geysermc/connector/utils/NewsHandler.java b/connector/src/main/java/org/geysermc/connector/utils/NewsHandler.java index 8272fb0b2..24df69817 100644 --- a/connector/src/main/java/org/geysermc/connector/utils/NewsHandler.java +++ b/connector/src/main/java/org/geysermc/connector/utils/NewsHandler.java @@ -109,7 +109,7 @@ public class NewsHandler { // } break; case BROADCAST_TO_OPERATORS: - for (GeyserSession player : GeyserConnector.getInstance().getPlayers()) { + for (GeyserSession player : GeyserConnector.getInstance().getSessionManager().getSessions().values()) { if (player.getOpPermissionLevel() >= 2) { session.sendMessage(ChatColor.GREEN + news.getMessage()); }