package de.maxhenkel.voicechat.voice.server; import de.maxhenkel.voicechat.Voicechat; import de.maxhenkel.voicechat.compatibility.PlayerHideEvent; import de.maxhenkel.voicechat.compatibility.PlayerShowEvent; import de.maxhenkel.voicechat.net.NetManager; import de.maxhenkel.voicechat.net.PacketRateLimiter; import de.maxhenkel.voicechat.net.RequestSecretPacket; import de.maxhenkel.voicechat.net.SecretPacket; import de.maxhenkel.voicechat.plugins.PluginManager; import de.maxhenkel.voicechat.voice.common.Secret; import java.util.Map; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import org.bukkit.Bukkit; import org.bukkit.entity.Player; import org.bukkit.event.EventHandler; import org.bukkit.event.Listener; import org.bukkit.event.player.PlayerJoinEvent; import org.bukkit.event.player.PlayerQuitEvent; public class ServerVoiceEvents implements Listener { private static final int LEGACY_COMPATIBILITY_VERSION = 18; private static final int CURRENT_COMPATIBILITY_VERSION = 20; private final Map clientCompatibilities = new ConcurrentHashMap<>(); private final PacketRateLimiter rateLimiter; private Server server; public ServerVoiceEvents() { this.rateLimiter = new PacketRateLimiter(Voicechat.SERVER_CONFIG.tcpRateLimit.get()); } public void init() { if (this.server != null) { return; } if (!Bukkit.getOnlineMode()) { Voicechat.LOGGER.warn("Running in offline mode - Voice chat encryption is not secure!", new Object[0]); } Voicechat.compatibility.registerPlayerHideEvent(this::onPlayerHide); Voicechat.compatibility.registerPlayerShowEvent(this::onPlayerShow); this.server = new Server(); this.server.start(); PluginManager.instance().onServerStarted(); } public void onRequestSecretPacket(Player player, RequestSecretPacket packet) { UUID playerUUID; int clientVersion = packet.getCompatibilityVersion(); Voicechat.LOGGER.info("Received secret request of {} ({})", player.getName(), clientVersion); try { playerUUID = player.getUniqueId(); } catch (UnsupportedOperationException e) { player.kickPlayer("Tried to authenticate voice chat while still connecting"); Voicechat.LOGGER.warn("{} tried to authenticate voice chat while still connecting", player.getName()); return; } this.clientCompatibilities.put(playerUUID, clientVersion); if (!ServerVoiceEvents.isSupportedCompatibilityVersion(clientVersion)) { Voicechat.LOGGER.warn("Connected client {} has incompatible voice chat version (server={}, client={})", player.getName(), Voicechat.COMPATIBILITY_VERSION, clientVersion); ServerVoiceEvents.sendIncompatibleMessage(player, clientVersion); return; } this.initializePlayerConnection(player); } private static boolean isSupportedCompatibilityVersion(int version) { return version == LEGACY_COMPATIBILITY_VERSION || version == CURRENT_COMPATIBILITY_VERSION; } public boolean isCompatible(Player player) { return this.isCompatible(player.getUniqueId()); } public boolean isCompatible(UUID playerUuid) { return ServerVoiceEvents.isSupportedCompatibilityVersion(this.clientCompatibilities.getOrDefault(playerUuid, -1)); } public static void sendIncompatibleMessage(Player player, int clientCompatibilityVersion) { if (clientCompatibilityVersion <= 6) { player.sendMessage(String.format(Voicechat.TRANSLATIONS.voicechatNotCompatibleMessage.get(), "2.6.x", "Simple Voice Chat")); } else { Voicechat.compatibility.sendIncompatibleMessage(player, "2.6.x", "Simple Voice Chat"); } } public void initializePlayerConnection(Player player) { if (this.server == null) { return; } UUID playerUuid = player.getUniqueId(); this.server.getPlayerStateManager().onPlayerCompatibilityCheckSucceeded(player); this.server.getCategoryManager().onPlayerCompatibilityCheckSucceeded(player); this.server.getGroupManager().onPlayerCompatibilityCheckSucceeded(player); Secret secret = this.server.generateNewSecret(playerUuid); if (secret == null) { Voicechat.LOGGER.warn("Player already requested secret - ignoring", new Object[0]); return; } secret.setLegacyProtocol(this.clientCompatibilities.getOrDefault(playerUuid, -1) == LEGACY_COMPATIBILITY_VERSION); String voiceHost = PluginManager.instance().getVoiceHost(player, Voicechat.SERVER_CONFIG.voiceHost.get()); NetManager.sendToClient(player, new SecretPacket(player, secret, this.server.getPort(), Voicechat.SERVER_CONFIG, voiceHost)); Voicechat.LOGGER.info("Sent secret to {}", player.getName()); } @EventHandler public void onPlayerJoin(PlayerJoinEvent event) { this.server.getPlayerStateManager().onPlayerJoin(event); if (!Voicechat.SERVER_CONFIG.forceVoiceChat.get().booleanValue()) { return; } Player player = event.getPlayer(); Voicechat.compatibility.runTaskLater(() -> { if (!player.isOnline()) { return; } if (!this.isCompatible(player)) { player.kickPlayer(String.format(Voicechat.TRANSLATIONS.forceVoicechatKickMessage.get(), "Simple Voice Chat", Voicechat.INSTANCE.getDescription().getVersion())); } }, (long) Voicechat.SERVER_CONFIG.loginTimeout.get().intValue() / 50L); } @EventHandler public void onPlayerQuit(PlayerQuitEvent event) { this.server.getPlayerStateManager().onPlayerQuit(event); this.server.getGroupManager().onPlayerQuit(event); UUID playerUuid = event.getPlayer().getUniqueId(); this.clientCompatibilities.remove(playerUuid); this.rateLimiter.onPlayerLoggedOut(event.getPlayer()); if (this.server == null) { return; } this.server.disconnectClient(playerUuid); Voicechat.LOGGER.info("Disconnecting client {}", event.getPlayer().getName()); } public void onPlayerHide(PlayerHideEvent event) { this.server.getPlayerStateManager().onPlayerHide(event); } public void onPlayerShow(PlayerShowEvent event) { this.server.getPlayerStateManager().onPlayerShow(event); } public PacketRateLimiter getRateLimiter() { return this.rateLimiter; } public Server getServer() { return this.server; } }