(1.1.0) Added capes, voice, FNAW skin disabler to plugin
This commit is contained in:
parent
0821e23613
commit
42c57894f9
Binary file not shown.
|
@ -30,9 +30,11 @@ import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerPipe
|
|||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.web.HttpWebServer;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.shit.CompatWarning;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.BinaryHttpClient;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.CapeServiceOffline;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.ISkinService;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.SkinService;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.SkinServiceOffline;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.voice.VoiceService;
|
||||
import net.md_5.bungee.api.plugin.Plugin;
|
||||
import net.md_5.bungee.api.plugin.PluginManager;
|
||||
import net.md_5.bungee.netty.PipelineUtils;
|
||||
|
@ -55,9 +57,8 @@ import net.md_5.bungee.BungeeCord;
|
|||
*/
|
||||
public class EaglerXBungee extends Plugin {
|
||||
|
||||
public static final String NATIVE_BUNGEECORD_BUILD = "1.20-R0.3-SNAPSHOT:eda268b:1797";
|
||||
public static final String NATIVE_WATERFALL_BUILD = "1.20-R0.2-SNAPSHOT:92b5149:562";
|
||||
public static final String NATIVE_FLAMECORD_BUILD = "1.1.1";
|
||||
public static final String NATIVE_BUNGEECORD_BUILD = "1.20-R0.3-SNAPSHOT:67c65e0:1828";
|
||||
public static final String NATIVE_WATERFALL_BUILD = "1.20-R0.3-SNAPSHOT:da6aaf6:572";
|
||||
|
||||
static {
|
||||
CompatWarning.displayCompatWarning();
|
||||
|
@ -72,6 +73,8 @@ public class EaglerXBungee extends Plugin {
|
|||
private Timer authServiceTasks = null;
|
||||
private final ChannelFutureListener newChannelListener;
|
||||
private ISkinService skinService;
|
||||
private CapeServiceOffline capeService;
|
||||
private VoiceService voiceService;
|
||||
private DefaultAuthSystem defaultAuthSystem;
|
||||
|
||||
public EaglerXBungee() {
|
||||
|
@ -128,7 +131,10 @@ public class EaglerXBungee extends Plugin {
|
|||
}
|
||||
}
|
||||
getProxy().registerChannel(SkinService.CHANNEL);
|
||||
getProxy().registerChannel(CapeServiceOffline.CHANNEL);
|
||||
getProxy().registerChannel(EaglerPipeline.UPDATE_CERT_CHANNEL);
|
||||
getProxy().registerChannel(VoiceService.CHANNEL);
|
||||
getProxy().registerChannel(EaglerPacketEventListener.FNAW_SKIN_ENABLE_CHANNEL);
|
||||
startListeners();
|
||||
if(skinServiceTasks != null) {
|
||||
skinServiceTasks.cancel();
|
||||
|
@ -165,6 +171,7 @@ public class EaglerXBungee extends Plugin {
|
|||
}
|
||||
}, 1000l, 1000l);
|
||||
}
|
||||
capeService = new CapeServiceOffline();
|
||||
if(authConf.isEnableAuthentication() && authConf.isUseBuiltInAuthentication()) {
|
||||
try {
|
||||
defaultAuthSystem = DefaultAuthSystem.initializeAuthSystem(authConf);
|
||||
|
@ -185,6 +192,12 @@ public class EaglerXBungee extends Plugin {
|
|||
}, 60000l, 60000l);
|
||||
}
|
||||
}
|
||||
if(conf.getEnableVoiceChat()) {
|
||||
voiceService = new VoiceService(conf);
|
||||
logger().warning("Voice chat enabled, not recommended for public servers!");
|
||||
}else {
|
||||
logger().info("Voice chat disabled, add \"allow_voice: true\" to your listeners to enable");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -193,13 +206,19 @@ public class EaglerXBungee extends Plugin {
|
|||
mgr.unregisterListeners(this);
|
||||
mgr.unregisterCommands(this);
|
||||
getProxy().unregisterChannel(SkinService.CHANNEL);
|
||||
getProxy().unregisterChannel(CapeServiceOffline.CHANNEL);
|
||||
getProxy().unregisterChannel(EaglerPipeline.UPDATE_CERT_CHANNEL);
|
||||
getProxy().unregisterChannel(VoiceService.CHANNEL);
|
||||
getProxy().unregisterChannel(EaglerPacketEventListener.FNAW_SKIN_ENABLE_CHANNEL);
|
||||
stopListeners();
|
||||
if(skinServiceTasks != null) {
|
||||
skinServiceTasks.cancel();
|
||||
skinServiceTasks = null;
|
||||
}
|
||||
skinService.shutdown();
|
||||
skinService = null;
|
||||
capeService.shutdown();
|
||||
capeService = null;
|
||||
if(defaultAuthSystem != null) {
|
||||
defaultAuthSystem.destroy();
|
||||
defaultAuthSystem = null;
|
||||
|
@ -208,6 +227,7 @@ public class EaglerXBungee extends Plugin {
|
|||
authServiceTasks = null;
|
||||
}
|
||||
}
|
||||
voiceService = null;
|
||||
BinaryHttpClient.killEventLoop();
|
||||
}
|
||||
|
||||
|
@ -278,10 +298,18 @@ public class EaglerXBungee extends Plugin {
|
|||
return skinService;
|
||||
}
|
||||
|
||||
public CapeServiceOffline getCapeService() {
|
||||
return capeService;
|
||||
}
|
||||
|
||||
public DefaultAuthSystem getAuthService() {
|
||||
return defaultAuthSystem;
|
||||
}
|
||||
|
||||
public VoiceService getVoiceService() {
|
||||
return voiceService;
|
||||
}
|
||||
|
||||
public static EaglerXBungee getEagler() {
|
||||
return instance;
|
||||
}
|
||||
|
|
|
@ -9,12 +9,14 @@ import java.io.InputStream;
|
|||
import java.io.InputStreamReader;
|
||||
import java.io.PrintWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
|
@ -31,7 +33,7 @@ import net.md_5.bungee.config.YamlConfiguration;
|
|||
import net.md_5.bungee.protocol.Property;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022-2023 lax1dude. All Rights Reserved.
|
||||
* Copyright (c) 2022-2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
|
@ -84,12 +86,14 @@ public class EaglerBungeeConfig {
|
|||
Configuration listenerYml = prov.load(getConfigFile(directory, "listeners.yml"));
|
||||
Iterator<String> listeners = listenerYml.getKeys().iterator();
|
||||
Map<String, EaglerListenerConfig> serverListeners = new HashMap();
|
||||
boolean voiceChat = false;
|
||||
|
||||
while(listeners.hasNext()) {
|
||||
String name = listeners.next();
|
||||
EaglerListenerConfig conf = EaglerListenerConfig.loadConfig(listenerYml.getSection(name), contentTypes);
|
||||
if(conf != null) {
|
||||
serverListeners.put(name, conf);
|
||||
voiceChat |= conf.getEnableVoiceChat();
|
||||
}else {
|
||||
EaglerXBungee.logger().severe("Invalid listener config: " + name);
|
||||
}
|
||||
|
@ -105,6 +109,9 @@ public class EaglerBungeeConfig {
|
|||
Configuration updatesYml = prov.load(getConfigFile(directory, "updates.yml"));
|
||||
EaglerUpdateConfig updatesConfig = EaglerUpdateConfig.loadConfig(updatesYml);
|
||||
|
||||
Configuration iceServersYml = prov.load(getConfigFile(directory, "ice_servers.yml"));
|
||||
Collection<String> iceServers = loadICEServers(iceServersYml);
|
||||
|
||||
if(authConfig.isEnableAuthentication()) {
|
||||
for(EaglerListenerConfig lst : serverListeners.values()) {
|
||||
if(lst.getRatelimitLogin() != null) lst.getRatelimitLogin().setDivisor(2);
|
||||
|
@ -135,12 +142,18 @@ public class EaglerBungeeConfig {
|
|||
if(eaglerPlayersVanillaSkin != null && eaglerPlayersVanillaSkin.length() == 0) {
|
||||
eaglerPlayersVanillaSkin = null;
|
||||
}
|
||||
boolean enableIsEaglerPlayerProperty = configYml.getBoolean("enable_is_eagler_player_property", true);
|
||||
Set<String> disableVoiceOnServers = new HashSet((Collection<String>)configYml.getList("disable_voice_chat_on_servers"));
|
||||
boolean disableFNAWSkinsEverywhere = configYml.getBoolean("disable_fnaw_skins_everywhere", false);
|
||||
Set<String> disableFNAWSkinsOnServers = new HashSet((Collection<String>)configYml.getList("disable_fnaw_skins_on_servers"));
|
||||
|
||||
final EaglerBungeeConfig ret = new EaglerBungeeConfig(serverName, serverUUID, websocketKeepAliveTimeout,
|
||||
websocketHandshakeTimeout, websocketCompressionLevel, serverListeners, contentTypes,
|
||||
downloadVanillaSkins, validSkinUrls, uuidRateLimitPlayer, uuidRateLimitGlobal, skinRateLimitPlayer,
|
||||
skinRateLimitGlobal, skinCacheURI, keepObjectsDays, keepProfilesDays, maxObjects, maxProfiles,
|
||||
antagonistsRateLimit, sqliteDriverClass, sqliteDriverPath, eaglerPlayersVanillaSkin, authConfig, updatesConfig);
|
||||
antagonistsRateLimit, sqliteDriverClass, sqliteDriverPath, eaglerPlayersVanillaSkin,
|
||||
enableIsEaglerPlayerProperty, authConfig, updatesConfig, iceServers, voiceChat,
|
||||
disableVoiceOnServers, disableFNAWSkinsEverywhere, disableFNAWSkinsOnServers);
|
||||
|
||||
if(eaglerPlayersVanillaSkin != null) {
|
||||
VanillaDefaultSkinProfileLoader.lookupVanillaSkinUser(ret);
|
||||
|
@ -202,6 +215,18 @@ public class EaglerBungeeConfig {
|
|||
}
|
||||
}
|
||||
|
||||
private static Collection<String> loadICEServers(Configuration config) {
|
||||
Collection<String> ret = new ArrayList(config.getList("voice_stun_servers"));
|
||||
Configuration turnServers = config.getSection("voice_turn_servers");
|
||||
Iterator<String> turnItr = turnServers.getKeys().iterator();
|
||||
while(turnItr.hasNext()) {
|
||||
String name = turnItr.next();
|
||||
Configuration turnSvr = turnServers.getSection(name);
|
||||
ret.add(turnSvr.getString("url") + ";" + turnSvr.getString("username") + ";" + turnSvr.getString("password"));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private static JsonObject parseJsonObject(InputStream file) throws IOException {
|
||||
StringBuilder str = new StringBuilder();
|
||||
|
@ -243,8 +268,14 @@ public class EaglerBungeeConfig {
|
|||
private final String sqliteDriverClass;
|
||||
private final String sqliteDriverPath;
|
||||
private final String eaglerPlayersVanillaSkin;
|
||||
private final boolean enableIsEaglerPlayerProperty;
|
||||
private final EaglerAuthConfig authConfig;
|
||||
private final EaglerUpdateConfig updateConfig;
|
||||
private final Collection<String> iceServers;
|
||||
private final boolean enableVoiceChat;
|
||||
private final Set<String> disableVoiceOnServers;
|
||||
private final boolean disableFNAWSkinsEverywhere;
|
||||
private final Set<String> disableFNAWSkinsOnServers;
|
||||
Property[] eaglerPlayersVanillaSkinCached = new Property[] { isEaglerProperty };
|
||||
|
||||
public String getServerName() {
|
||||
|
@ -362,6 +393,10 @@ public class EaglerBungeeConfig {
|
|||
return eaglerPlayersVanillaSkin;
|
||||
}
|
||||
|
||||
public boolean getEnableIsEaglerPlayerProperty() {
|
||||
return enableIsEaglerPlayerProperty;
|
||||
}
|
||||
|
||||
public Property[] getEaglerPlayersVanillaSkinProperties() {
|
||||
return eaglerPlayersVanillaSkinCached;
|
||||
}
|
||||
|
@ -378,6 +413,26 @@ public class EaglerBungeeConfig {
|
|||
return updateConfig;
|
||||
}
|
||||
|
||||
public Collection<String> getICEServers() {
|
||||
return iceServers;
|
||||
}
|
||||
|
||||
public boolean getEnableVoiceChat() {
|
||||
return enableVoiceChat;
|
||||
}
|
||||
|
||||
public Set<String> getDisableVoiceOnServersSet() {
|
||||
return disableVoiceOnServers;
|
||||
}
|
||||
|
||||
public boolean getDisableFNAWSkinsEverywhere() {
|
||||
return disableFNAWSkinsEverywhere;
|
||||
}
|
||||
|
||||
public Set<String> getDisableFNAWSkinsOnServersSet() {
|
||||
return disableFNAWSkinsOnServers;
|
||||
}
|
||||
|
||||
private EaglerBungeeConfig(String serverName, UUID serverUUID, long websocketKeepAliveTimeout,
|
||||
long websocketHandshakeTimeout, int httpWebsocketCompressionLevel,
|
||||
Map<String, EaglerListenerConfig> serverListeners, Map<String, HttpContentType> contentTypes,
|
||||
|
@ -385,7 +440,9 @@ public class EaglerBungeeConfig {
|
|||
int uuidRateLimitGlobal, int skinRateLimitPlayer, int skinRateLimitGlobal, String skinCacheURI,
|
||||
int keepObjectsDays, int keepProfilesDays, int maxObjects, int maxProfiles, int antagonistsRateLimit,
|
||||
String sqliteDriverClass, String sqliteDriverPath, String eaglerPlayersVanillaSkin,
|
||||
EaglerAuthConfig authConfig, EaglerUpdateConfig updateConfig) {
|
||||
boolean enableIsEaglerPlayerProperty, EaglerAuthConfig authConfig, EaglerUpdateConfig updateConfig,
|
||||
Collection<String> iceServers, boolean enableVoiceChat, Set<String> disableVoiceOnServers,
|
||||
boolean disableFNAWSkinsEverywhere, Set<String> disableFNAWSkinsOnServers) {
|
||||
this.serverName = serverName;
|
||||
this.serverUUID = serverUUID;
|
||||
this.serverListeners = serverListeners;
|
||||
|
@ -408,8 +465,14 @@ public class EaglerBungeeConfig {
|
|||
this.sqliteDriverClass = sqliteDriverClass;
|
||||
this.sqliteDriverPath = sqliteDriverPath;
|
||||
this.eaglerPlayersVanillaSkin = eaglerPlayersVanillaSkin;
|
||||
this.enableIsEaglerPlayerProperty = enableIsEaglerPlayerProperty;
|
||||
this.authConfig = authConfig;
|
||||
this.updateConfig = updateConfig;
|
||||
this.iceServers = iceServers;
|
||||
this.enableVoiceChat = enableVoiceChat;
|
||||
this.disableVoiceOnServers = disableVoiceOnServers;
|
||||
this.disableFNAWSkinsEverywhere = disableFNAWSkinsEverywhere;
|
||||
this.disableFNAWSkinsOnServers = disableFNAWSkinsOnServers;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -120,6 +120,8 @@ public class EaglerListenerConfig extends ListenerInfo {
|
|||
contentTypes, indexPage, page404);
|
||||
}
|
||||
|
||||
boolean enableVoiceChat = config.getBoolean("allow_voice", false);
|
||||
|
||||
EaglerRateLimiter ratelimitIp = null;
|
||||
EaglerRateLimiter ratelimitLogin = null;
|
||||
EaglerRateLimiter ratelimitMOTD = null;
|
||||
|
@ -149,7 +151,7 @@ public class EaglerListenerConfig extends ListenerInfo {
|
|||
cacheTrending, cachePortfolios);
|
||||
return new EaglerListenerConfig(hostv4, hostv6, maxPlayer, tabListType, defaultServer, forceDefaultServer,
|
||||
forwardIp, forwardIpHeader, redirectLegacyClientsTo, serverIcon, serverMOTD, allowMOTD, allowQuery,
|
||||
cacheConfig, httpServer, ratelimitIp, ratelimitLogin, ratelimitMOTD, ratelimitQuery);
|
||||
cacheConfig, httpServer, enableVoiceChat, ratelimitIp, ratelimitLogin, ratelimitMOTD, ratelimitQuery);
|
||||
}
|
||||
|
||||
private final InetSocketAddress address;
|
||||
|
@ -169,6 +171,7 @@ public class EaglerListenerConfig extends ListenerInfo {
|
|||
private final HttpWebServer webServer;
|
||||
private boolean serverIconSet = false;
|
||||
private int[] serverIconPixels = null;
|
||||
private final boolean enableVoiceChat;
|
||||
private final EaglerRateLimiter ratelimitIp;
|
||||
private final EaglerRateLimiter ratelimitLogin;
|
||||
private final EaglerRateLimiter ratelimitMOTD;
|
||||
|
@ -178,8 +181,8 @@ public class EaglerListenerConfig extends ListenerInfo {
|
|||
String tabListType, String defaultServer, boolean forceDefaultServer, boolean forwardIp,
|
||||
String forwardIpHeader, String redirectLegacyClientsTo, String serverIcon, List<String> serverMOTD,
|
||||
boolean allowMOTD, boolean allowQuery, MOTDCacheConfiguration motdCacheConfig, HttpWebServer webServer,
|
||||
EaglerRateLimiter ratelimitIp, EaglerRateLimiter ratelimitLogin, EaglerRateLimiter ratelimitMOTD,
|
||||
EaglerRateLimiter ratelimitQuery) {
|
||||
boolean enableVoiceChat, EaglerRateLimiter ratelimitIp, EaglerRateLimiter ratelimitLogin,
|
||||
EaglerRateLimiter ratelimitMOTD, EaglerRateLimiter ratelimitQuery) {
|
||||
super(address, String.join("\n", serverMOTD), maxPlayer, 60, Arrays.asList(defaultServer), forceDefaultServer,
|
||||
Collections.emptyMap(), tabListType, false, false, 0, false, false);
|
||||
this.address = address;
|
||||
|
@ -197,6 +200,7 @@ public class EaglerListenerConfig extends ListenerInfo {
|
|||
this.allowQuery = allowQuery;
|
||||
this.motdCacheConfig = motdCacheConfig;
|
||||
this.webServer = webServer;
|
||||
this.enableVoiceChat = enableVoiceChat;
|
||||
this.ratelimitIp = ratelimitIp;
|
||||
this.ratelimitLogin = ratelimitLogin;
|
||||
this.ratelimitMOTD = ratelimitMOTD;
|
||||
|
@ -285,6 +289,10 @@ public class EaglerListenerConfig extends ListenerInfo {
|
|||
return redirectLegacyClientsTo;
|
||||
}
|
||||
|
||||
public boolean getEnableVoiceChat() {
|
||||
return enableVoiceChat;
|
||||
}
|
||||
|
||||
public EaglerRateLimiter getRatelimitIp() {
|
||||
return ratelimitIp;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.handlers;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.UUID;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
|
@ -14,16 +15,23 @@ import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee;
|
|||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.auth.DefaultAuthSystem;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerAuthConfig;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerInitialHandler;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.CapePackets;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.CapeServiceOffline;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.SkinPackets;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.SkinService;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.voice.VoiceService;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.voice.VoiceSignalPackets;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
import net.md_5.bungee.api.ProxyServer;
|
||||
import net.md_5.bungee.api.chat.TextComponent;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
import net.md_5.bungee.api.connection.ProxiedPlayer;
|
||||
import net.md_5.bungee.api.connection.Server;
|
||||
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
|
||||
import net.md_5.bungee.api.event.PluginMessageEvent;
|
||||
import net.md_5.bungee.api.event.PostLoginEvent;
|
||||
import net.md_5.bungee.api.event.ServerConnectedEvent;
|
||||
import net.md_5.bungee.api.event.ServerDisconnectEvent;
|
||||
import net.md_5.bungee.api.plugin.Listener;
|
||||
import net.md_5.bungee.connection.InitialHandler;
|
||||
import net.md_5.bungee.connection.LoginResult;
|
||||
|
@ -47,6 +55,8 @@ import net.md_5.bungee.protocol.Property;
|
|||
*/
|
||||
public class EaglerPacketEventListener implements Listener {
|
||||
|
||||
public static final String FNAW_SKIN_ENABLE_CHANNEL = "EAG|FNAWSEn-1.8";
|
||||
|
||||
public final EaglerXBungee plugin;
|
||||
|
||||
public EaglerPacketEventListener(EaglerXBungee plugin) {
|
||||
|
@ -56,28 +66,46 @@ public class EaglerPacketEventListener implements Listener {
|
|||
@EventHandler
|
||||
public void onPluginMessage(final PluginMessageEvent event) {
|
||||
if(event.getSender() instanceof UserConnection) {
|
||||
final UserConnection player = (UserConnection)event.getSender();
|
||||
if(player.getPendingConnection() instanceof EaglerInitialHandler) {
|
||||
if(SkinService.CHANNEL.equals(event.getTag())) {
|
||||
event.setCancelled(true);
|
||||
final UserConnection sender = (UserConnection)event.getSender();
|
||||
if(sender.getPendingConnection() instanceof EaglerInitialHandler) {
|
||||
ProxyServer.getInstance().getScheduler().runAsync(plugin, new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
SkinPackets.processPacket(event.getData(), sender, plugin.getSkinService());
|
||||
SkinPackets.processPacket(event.getData(), player, plugin.getSkinService());
|
||||
} catch (IOException e) {
|
||||
event.getSender().disconnect(new TextComponent("Skin packet error!"));
|
||||
EaglerXBungee.logger().log(Level.SEVERE, "Eagler user \"" + sender.getName() + "\" raised an exception handling skins!", e);
|
||||
EaglerXBungee.logger().log(Level.SEVERE, "Eagler user \"" + player.getName() + "\" raised an exception handling skins!", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}else {
|
||||
event.getSender().disconnect(new TextComponent("Cannot send \"" + SkinService.CHANNEL + "\" on a non-eagler connection!"));
|
||||
}else if(CapeServiceOffline.CHANNEL.equals(event.getTag())) {
|
||||
event.setCancelled(true);
|
||||
try {
|
||||
CapePackets.processPacket(event.getData(), player, plugin.getCapeService());
|
||||
} catch (IOException e) {
|
||||
event.getSender().disconnect(new TextComponent("Cape packet error!"));
|
||||
EaglerXBungee.logger().log(Level.SEVERE, "Eagler user \"" + player.getName() + "\" raised an exception handling capes!", e);
|
||||
}
|
||||
}else if(VoiceService.CHANNEL.equals(event.getTag())) {
|
||||
event.setCancelled(true);
|
||||
VoiceService svc = plugin.getVoiceService();
|
||||
if(svc != null && ((EaglerInitialHandler)player.getPendingConnection()).getEaglerListenerConfig().getEnableVoiceChat()) {
|
||||
try {
|
||||
VoiceSignalPackets.processPacket(event.getData(), player, svc);
|
||||
} catch (IOException e) {
|
||||
event.getSender().disconnect(new TextComponent("Voice signal packet error!"));
|
||||
EaglerXBungee.logger().log(Level.SEVERE, "Eagler user \"" + player.getName() + "\" raised an exception handling voice signals!", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}else if(event.getSender() instanceof Server && event.getReceiver() instanceof UserConnection) {
|
||||
UserConnection player = (UserConnection)event.getReceiver();
|
||||
if("EAG|GetDomain".equals(event.getTag()) && player.getPendingConnection() instanceof EaglerInitialHandler) {
|
||||
event.setCancelled(true);
|
||||
String domain = ((EaglerInitialHandler)player.getPendingConnection()).getOrigin();
|
||||
if(domain == null) {
|
||||
((Server)event.getSender()).sendData("EAG|Domain", new byte[] { 0 });
|
||||
|
@ -119,9 +147,9 @@ public class EaglerPacketEventListener implements Listener {
|
|||
}
|
||||
}
|
||||
}
|
||||
EaglerAuthConfig authConf = EaglerXBungee.getEagler().getConfig().getAuthConfig();
|
||||
EaglerAuthConfig authConf = plugin.getConfig().getAuthConfig();
|
||||
if(authConf.isEnableAuthentication() && authConf.isUseBuiltInAuthentication()) {
|
||||
DefaultAuthSystem srv = EaglerXBungee.getEagler().getAuthService();
|
||||
DefaultAuthSystem srv = plugin.getAuthService();
|
||||
if(srv != null) {
|
||||
srv.handleVanillaLogin(event);
|
||||
}
|
||||
|
@ -131,6 +159,47 @@ public class EaglerPacketEventListener implements Listener {
|
|||
|
||||
@EventHandler
|
||||
public void onConnectionLost(PlayerDisconnectEvent event) {
|
||||
plugin.getSkinService().unregisterPlayer(event.getPlayer().getUniqueId());
|
||||
UUID uuid = event.getPlayer().getUniqueId();
|
||||
plugin.getSkinService().unregisterPlayer(uuid);
|
||||
plugin.getCapeService().unregisterPlayer(uuid);
|
||||
if(event.getPlayer() instanceof UserConnection) {
|
||||
UserConnection player = (UserConnection)event.getPlayer();
|
||||
if((player.getPendingConnection() instanceof EaglerInitialHandler)
|
||||
&& ((EaglerInitialHandler) player.getPendingConnection()).getEaglerListenerConfig()
|
||||
.getEnableVoiceChat()) {
|
||||
plugin.getVoiceService().handlePlayerLoggedOut(player);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onServerConnected(ServerConnectedEvent event) {
|
||||
if(event.getPlayer() instanceof UserConnection) {
|
||||
UserConnection player = (UserConnection)event.getPlayer();
|
||||
if(player.getPendingConnection() instanceof EaglerInitialHandler) {
|
||||
EaglerInitialHandler handler = (EaglerInitialHandler) player.getPendingConnection();
|
||||
ServerInfo sv = event.getServer().getInfo();
|
||||
boolean fnawSkins = !plugin.getConfig().getDisableFNAWSkinsEverywhere() && !plugin.getConfig().getDisableFNAWSkinsOnServersSet().contains(sv.getName());
|
||||
if(fnawSkins != handler.currentFNAWSkinEnableStatus) {
|
||||
handler.currentFNAWSkinEnableStatus = fnawSkins;
|
||||
player.sendData(FNAW_SKIN_ENABLE_CHANNEL, new byte[] { fnawSkins ? (byte)1 : (byte)0 });
|
||||
}
|
||||
if(handler.getEaglerListenerConfig().getEnableVoiceChat()) {
|
||||
plugin.getVoiceService().handleServerConnected(player, sv);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@EventHandler
|
||||
public void onServerDisconnected(ServerDisconnectEvent event) {
|
||||
if(event.getPlayer() instanceof UserConnection) {
|
||||
UserConnection player = (UserConnection)event.getPlayer();
|
||||
if((player.getPendingConnection() instanceof EaglerInitialHandler)
|
||||
&& ((EaglerInitialHandler) player.getPendingConnection()).getEaglerListenerConfig()
|
||||
.getEnableVoiceChat()) {
|
||||
plugin.getVoiceService().handleServerDisconnected(player, event.getTarget());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,11 +10,12 @@ import java.util.UUID;
|
|||
|
||||
import gnu.trove.set.TIntSet;
|
||||
import gnu.trove.set.hash.TIntHashSet;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerBungeeConfig;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerListenerConfig;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.SimpleRateLimiter;
|
||||
import net.md_5.bungee.BungeeCord;
|
||||
import net.md_5.bungee.api.chat.BaseComponent;
|
||||
import net.md_5.bungee.api.config.ListenerInfo;
|
||||
import net.md_5.bungee.connection.InitialHandler;
|
||||
import net.md_5.bungee.connection.LoginResult;
|
||||
import net.md_5.bungee.netty.ChannelWrapper;
|
||||
|
@ -67,12 +68,17 @@ public class EaglerInitialHandler extends InitialHandler {
|
|||
public final SimpleRateLimiter skinLookupRateLimiter;
|
||||
public final SimpleRateLimiter skinUUIDLookupRateLimiter;
|
||||
public final SimpleRateLimiter skinTextureDownloadRateLimiter;
|
||||
public final SimpleRateLimiter capeLookupRateLimiter;
|
||||
public final SimpleRateLimiter voiceConnectRateLimiter;
|
||||
public final String origin;
|
||||
public final ClientCertificateHolder clientCertificate;
|
||||
public final Set<ClientCertificateHolder> certificatesToSend;
|
||||
public final TIntSet certificatesSent;
|
||||
public boolean currentFNAWSkinEnableStatus = true;
|
||||
|
||||
public EaglerInitialHandler(BungeeCord bungee, ListenerInfo listener, final ChannelWrapper ch,
|
||||
private static final Property[] NO_PROPERTIES = new Property[0];
|
||||
|
||||
public EaglerInitialHandler(BungeeCord bungee, EaglerListenerConfig listener, final ChannelWrapper ch,
|
||||
int gameProtocolVersion, String username, UUID playerUUID, InetSocketAddress address, String host,
|
||||
String origin, ClientCertificateHolder clientCertificate) {
|
||||
super(bungee, listener);
|
||||
|
@ -84,6 +90,8 @@ public class EaglerInitialHandler extends InitialHandler {
|
|||
this.skinLookupRateLimiter = new SimpleRateLimiter();
|
||||
this.skinUUIDLookupRateLimiter = new SimpleRateLimiter();
|
||||
this.skinTextureDownloadRateLimiter = new SimpleRateLimiter();
|
||||
this.capeLookupRateLimiter = new SimpleRateLimiter();
|
||||
this.voiceConnectRateLimiter = new SimpleRateLimiter();
|
||||
this.clientCertificate = clientCertificate;
|
||||
this.certificatesToSend = new HashSet();
|
||||
this.certificatesSent = new TIntHashSet();
|
||||
|
@ -108,7 +116,11 @@ public class EaglerInitialHandler extends InitialHandler {
|
|||
ch.getHandle().writeAndFlush(arg0);
|
||||
}
|
||||
};
|
||||
setLoginProfile(new LoginResult(playerUUID.toString(), username, new Property[] { EaglerBungeeConfig.isEaglerProperty }));
|
||||
Property[] profileProperties = NO_PROPERTIES;
|
||||
if(EaglerXBungee.getEagler().getConfig().getEnableIsEaglerPlayerProperty()) {
|
||||
profileProperties = new Property[] { EaglerBungeeConfig.isEaglerProperty };
|
||||
}
|
||||
setLoginProfile(new LoginResult(playerUUID.toString(), username, profileProperties));
|
||||
try {
|
||||
super.connected(ch);
|
||||
} catch (Exception e) {
|
||||
|
@ -251,4 +263,7 @@ public class EaglerInitialHandler extends InitialHandler {
|
|||
return origin;
|
||||
}
|
||||
|
||||
public EaglerListenerConfig getEaglerListenerConfig() {
|
||||
return (EaglerListenerConfig)getListener();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,6 +56,7 @@ import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerInit
|
|||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.bungeeprotocol.EaglerBungeeProtocol;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.query.MOTDQueryHandler;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.query.QueryManager;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.CapePackets;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.SkinPackets;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.SkinService;
|
||||
import net.md_5.bungee.BungeeCord;
|
||||
|
@ -949,7 +950,21 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
|||
}
|
||||
}
|
||||
|
||||
bungee.getPluginManager().callEvent(new PostLoginEvent(userCon));
|
||||
if(profileData.containsKey("cape_v1")) {
|
||||
try {
|
||||
CapePackets.registerEaglerPlayer(clientUUID, profileData.get("cape_v1"),
|
||||
EaglerXBungee.getEagler().getCapeService());
|
||||
} catch (Throwable ex) {
|
||||
CapePackets.registerEaglerPlayerFallback(clientUUID, EaglerXBungee.getEagler().getCapeService());
|
||||
EaglerXBungee.logger().info("[" + ctx.channel().remoteAddress() + "]: Invalid cape packet: " + ex.toString());
|
||||
}
|
||||
}else {
|
||||
CapePackets.registerEaglerPlayerFallback(clientUUID, EaglerXBungee.getEagler().getCapeService());
|
||||
}
|
||||
|
||||
if(conf.getEnableVoiceChat()) {
|
||||
EaglerXBungee.getEagler().getVoiceService().handlePlayerLoggedIn(userCon);
|
||||
}
|
||||
|
||||
ServerInfo server;
|
||||
if (bungee.getReconnectHandler() != null) {
|
||||
|
@ -962,8 +977,25 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
|||
server = bungee.getServerInfo(conf.getDefaultServer());
|
||||
}
|
||||
|
||||
final ServerInfo server2 = server;
|
||||
Callback<PostLoginEvent> complete = new Callback<PostLoginEvent>() {
|
||||
@Override
|
||||
public void done(PostLoginEvent result, Throwable error) {
|
||||
eaglerCon.hasBeenForwarded = true;
|
||||
userCon.connect(server, null, true, ServerConnectEvent.Reason.JOIN_PROXY);
|
||||
if (ch.isClosed()) {
|
||||
return;
|
||||
}
|
||||
userCon.connect(server2, null, true, ServerConnectEvent.Reason.JOIN_PROXY);
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
PostLoginEvent login = new PostLoginEvent(userCon);
|
||||
bungee.getPluginManager().callEvent(login);
|
||||
complete.done(login, null);
|
||||
}catch(NoSuchMethodError err) {
|
||||
bungee.getPluginManager().callEvent(PostLoginEvent.class.getDeclaredConstructor(ProxiedPlayer.class, ServerInfo.class, Callback.class).newInstance(userCon, server, complete));
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
@ -38,7 +38,6 @@ public class CompatWarning {
|
|||
":> ",
|
||||
":> - BungeeCord: " + EaglerXBungee.NATIVE_BUNGEECORD_BUILD,
|
||||
":> - Waterfall: " + EaglerXBungee.NATIVE_WATERFALL_BUILD,
|
||||
":> - FlameCord: " + EaglerXBungee.NATIVE_FLAMECORD_BUILD,
|
||||
":> ",
|
||||
":> This is not a Bukkit/Spigot plugin!",
|
||||
":> ",
|
||||
|
|
|
@ -108,7 +108,7 @@ public class AsyncSkinProvider {
|
|||
byte[] loadedPixels = new byte[16384];
|
||||
image.getRGB(0, 0, 64, 64, tmp, 0, 64);
|
||||
SkinRescaler.convertToBytes(tmp, loadedPixels);
|
||||
SkinPackets.setAlphaForChest(loadedPixels, (byte)255);
|
||||
SkinPackets.setAlphaForChest(loadedPixels, (byte)255, 0);
|
||||
doAccept(loadedPixels);
|
||||
return;
|
||||
}else if(srcWidth == 64 && srcHeight == 32) {
|
||||
|
@ -116,7 +116,7 @@ public class AsyncSkinProvider {
|
|||
byte[] loadedPixels = new byte[16384];
|
||||
image.getRGB(0, 0, 64, 32, tmp1, 0, 64);
|
||||
SkinRescaler.convert64x32To64x64(tmp1, loadedPixels);
|
||||
SkinPackets.setAlphaForChest(loadedPixels, (byte)255);
|
||||
SkinPackets.setAlphaForChest(loadedPixels, (byte)255, 0);
|
||||
doAccept(loadedPixels);
|
||||
return;
|
||||
}else {
|
||||
|
|
|
@ -0,0 +1,110 @@
|
|||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.UUID;
|
||||
|
||||
import net.md_5.bungee.UserConnection;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public class CapePackets {
|
||||
|
||||
public static final int PACKET_MY_CAPE_PRESET = 0x01;
|
||||
public static final int PACKET_MY_CAPE_CUSTOM = 0x02;
|
||||
public static final int PACKET_GET_OTHER_CAPE = 0x03;
|
||||
public static final int PACKET_OTHER_CAPE_PRESET = 0x04;
|
||||
public static final int PACKET_OTHER_CAPE_CUSTOM = 0x05;
|
||||
|
||||
public static void processPacket(byte[] data, UserConnection sender, CapeServiceOffline capeService) throws IOException {
|
||||
if(data.length == 0) {
|
||||
throw new IOException("Zero-length packet recieved");
|
||||
}
|
||||
int packetId = (int)data[0] & 0xFF;
|
||||
try {
|
||||
switch(packetId) {
|
||||
case PACKET_GET_OTHER_CAPE:
|
||||
processGetOtherCape(data, sender, capeService);
|
||||
break;
|
||||
default:
|
||||
throw new IOException("Unknown packet type " + packetId);
|
||||
}
|
||||
}catch(IOException ex) {
|
||||
throw ex;
|
||||
}catch(Throwable t) {
|
||||
throw new IOException("Unhandled exception handling cape packet type " + packetId, t);
|
||||
}
|
||||
}
|
||||
|
||||
private static void processGetOtherCape(byte[] data, UserConnection sender, CapeServiceOffline capeService) throws IOException {
|
||||
if(data.length != 17) {
|
||||
throw new IOException("Invalid length " + data.length + " for skin request packet");
|
||||
}
|
||||
UUID searchUUID = SkinPackets.bytesToUUID(data, 1);
|
||||
capeService.processGetOtherCape(searchUUID, sender);
|
||||
}
|
||||
|
||||
public static void registerEaglerPlayer(UUID clientUUID, byte[] bs, CapeServiceOffline capeService) throws IOException {
|
||||
if(bs.length == 0) {
|
||||
throw new IOException("Zero-length packet recieved");
|
||||
}
|
||||
byte[] generatedPacket;
|
||||
int packetType = (int)bs[0] & 0xFF;
|
||||
switch(packetType) {
|
||||
case PACKET_MY_CAPE_PRESET:
|
||||
if(bs.length != 5) {
|
||||
throw new IOException("Invalid length " + bs.length + " for preset cape packet");
|
||||
}
|
||||
generatedPacket = CapePackets.makePresetResponse(clientUUID, (bs[1] << 24) | (bs[2] << 16) | (bs[3] << 8) | (bs[4] & 0xFF));
|
||||
break;
|
||||
case PACKET_MY_CAPE_CUSTOM:
|
||||
if(bs.length != 1174) {
|
||||
throw new IOException("Invalid length " + bs.length + " for custom cape packet");
|
||||
}
|
||||
generatedPacket = CapePackets.makeCustomResponse(clientUUID, bs, 1, 1173);
|
||||
break;
|
||||
default:
|
||||
throw new IOException("Unknown skin packet type: " + packetType);
|
||||
}
|
||||
capeService.registerEaglercraftPlayer(clientUUID, generatedPacket);
|
||||
}
|
||||
|
||||
public static void registerEaglerPlayerFallback(UUID clientUUID, CapeServiceOffline capeService) {
|
||||
capeService.registerEaglercraftPlayer(clientUUID, CapePackets.makePresetResponse(clientUUID, 0));
|
||||
}
|
||||
|
||||
public static byte[] makePresetResponse(UUID uuid, int presetId) {
|
||||
byte[] ret = new byte[1 + 16 + 4];
|
||||
ret[0] = (byte)PACKET_OTHER_CAPE_PRESET;
|
||||
SkinPackets.UUIDToBytes(uuid, ret, 1);
|
||||
ret[17] = (byte)(presetId >> 24);
|
||||
ret[18] = (byte)(presetId >> 16);
|
||||
ret[19] = (byte)(presetId >> 8);
|
||||
ret[20] = (byte)(presetId & 0xFF);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static byte[] makeCustomResponse(UUID uuid, byte[] pixels) {
|
||||
return makeCustomResponse(uuid, pixels, 0, pixels.length);
|
||||
}
|
||||
|
||||
public static byte[] makeCustomResponse(UUID uuid, byte[] pixels, int offset, int length) {
|
||||
byte[] ret = new byte[1 + 16 + length];
|
||||
ret[0] = (byte)PACKET_OTHER_CAPE_CUSTOM;
|
||||
SkinPackets.UUIDToBytes(uuid, ret, 1);
|
||||
System.arraycopy(pixels, offset, ret, 17, length);
|
||||
return ret;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerInitialHandler;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public class CapeServiceOffline {
|
||||
|
||||
public static final int masterRateLimitPerPlayer = 250;
|
||||
|
||||
public static final String CHANNEL = "EAG|Capes-1.8";
|
||||
|
||||
private final Map<UUID, byte[]> capesCache = new HashMap();
|
||||
|
||||
public void registerEaglercraftPlayer(UUID playerUUID, byte[] capePacket) {
|
||||
synchronized(capesCache) {
|
||||
capesCache.put(playerUUID, capePacket);
|
||||
}
|
||||
}
|
||||
|
||||
public void processGetOtherCape(UUID searchUUID, UserConnection sender) {
|
||||
if(((EaglerInitialHandler)sender.getPendingConnection()).skinLookupRateLimiter.rateLimit(masterRateLimitPerPlayer)) {
|
||||
byte[] maybeCape;
|
||||
synchronized(capesCache) {
|
||||
maybeCape = capesCache.get(searchUUID);
|
||||
}
|
||||
if(maybeCape != null) {
|
||||
sender.sendData(CapeServiceOffline.CHANNEL, maybeCape);
|
||||
}else {
|
||||
sender.sendData(CapeServiceOffline.CHANNEL, CapePackets.makePresetResponse(searchUUID, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void unregisterPlayer(UUID playerUUID) {
|
||||
synchronized(capesCache) {
|
||||
capesCache.remove(playerUUID);
|
||||
}
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
synchronized(capesCache) {
|
||||
capesCache.clear();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -9,7 +9,7 @@ import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee;
|
|||
import net.md_5.bungee.UserConnection;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022-2023 lax1dude. All Rights Reserved.
|
||||
* Copyright (c) 2022-2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
|
@ -110,13 +110,11 @@ public class SkinPackets {
|
|||
generatedPacket = SkinPackets.makePresetResponse(clientUUID, (bs[1] << 24) | (bs[2] << 16) | (bs[3] << 8) | (bs[4] & 0xFF));
|
||||
break;
|
||||
case PACKET_MY_SKIN_CUSTOM:
|
||||
byte[] pixels = new byte[16384];
|
||||
if(bs.length != 2 + pixels.length) {
|
||||
if(bs.length != 2 + 16384) {
|
||||
throw new IOException("Invalid length " + bs.length + " for custom skin packet");
|
||||
}
|
||||
setAlphaForChest(pixels, (byte)255);
|
||||
System.arraycopy(bs, 2, pixels, 0, pixels.length);
|
||||
generatedPacket = SkinPackets.makeCustomResponse(clientUUID, (skinModel = (int)bs[1] & 0xFF), pixels);
|
||||
setAlphaForChest(bs, (byte)255, 2);
|
||||
generatedPacket = SkinPackets.makeCustomResponse(clientUUID, (skinModel = (int)bs[1] & 0xFF), bs, 2, 16384);
|
||||
break;
|
||||
default:
|
||||
throw new IOException("Unknown skin packet type: " + packetType);
|
||||
|
@ -130,13 +128,13 @@ public class SkinPackets {
|
|||
skinService.registerEaglercraftPlayer(clientUUID, generatedPacket, skinModel);
|
||||
}
|
||||
|
||||
public static void setAlphaForChest(byte[] skin64x64, byte alpha) {
|
||||
if(skin64x64.length != 16384) {
|
||||
public static void setAlphaForChest(byte[] skin64x64, byte alpha, int offset) {
|
||||
if(skin64x64.length - offset != 16384) {
|
||||
throw new IllegalArgumentException("Skin is not 64x64!");
|
||||
}
|
||||
for(int y = 20; y < 32; ++y) {
|
||||
for(int x = 16; x < 40; ++x) {
|
||||
skin64x64[(y << 8) | (x << 2)] = alpha;
|
||||
skin64x64[offset + ((y << 8) | (x << 2))] = alpha;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -157,11 +155,15 @@ public class SkinPackets {
|
|||
}
|
||||
|
||||
public static byte[] makeCustomResponse(UUID uuid, int model, byte[] pixels) {
|
||||
byte[] ret = new byte[1 + 16 + 1 + pixels.length];
|
||||
return makeCustomResponse(uuid, model, pixels, 0, pixels.length);
|
||||
}
|
||||
|
||||
public static byte[] makeCustomResponse(UUID uuid, int model, byte[] pixels, int offset, int length) {
|
||||
byte[] ret = new byte[1 + 16 + 1 + length];
|
||||
ret[0] = (byte)PACKET_OTHER_SKIN_CUSTOM;
|
||||
UUIDToBytes(uuid, ret, 1);
|
||||
ret[17] = (byte)model;
|
||||
System.arraycopy(pixels, 0, ret, 18, pixels.length);
|
||||
System.arraycopy(pixels, offset, ret, 18, length);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
|
|
@ -52,12 +52,6 @@ public class SkinService implements ISkinService {
|
|||
|
||||
public static final int masterRateLimitPerPlayer = 250;
|
||||
|
||||
public static final int PACKET_MY_SKIN_PRESET = 0x01;
|
||||
public static final int PACKET_MY_SKIN_CUSTOM = 0x02;
|
||||
public static final int PACKET_GET_OTHER_SKIN = 0x03;
|
||||
public static final int PACKET_OTHER_SKIN_PRESET = 0x04;
|
||||
public static final int PACKET_OTHER_SKIN_CUSTOM = 0x05;
|
||||
|
||||
public static final String CHANNEL = "EAG|Skins-1.8";
|
||||
|
||||
private final Map<UUID, CachedPlayerSkin> onlinePlayersCache = new HashMap();
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.voice;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022 ayunami2000. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public class ExpiringSet<T> extends HashSet<T> {
|
||||
private final long expiration;
|
||||
private final ExpiringEvent<T> event;
|
||||
|
||||
private final Map<T, Long> timestamps = new HashMap<>();
|
||||
|
||||
public ExpiringSet(long expiration) {
|
||||
this.expiration = expiration;
|
||||
this.event = null;
|
||||
}
|
||||
|
||||
public ExpiringSet(long expiration, ExpiringEvent<T> event) {
|
||||
this.expiration = expiration;
|
||||
this.event = event;
|
||||
}
|
||||
|
||||
public interface ExpiringEvent<T> {
|
||||
void onExpiration(T item);
|
||||
}
|
||||
|
||||
public void checkForExpirations() {
|
||||
Iterator<T> iterator = this.timestamps.keySet().iterator();
|
||||
long now = System.currentTimeMillis();
|
||||
while (iterator.hasNext()) {
|
||||
T element = iterator.next();
|
||||
if (super.contains(element)) {
|
||||
if (this.timestamps.get(element) + this.expiration < now) {
|
||||
if (this.event != null) this.event.onExpiration(element);
|
||||
iterator.remove();
|
||||
super.remove(element);
|
||||
}
|
||||
} else {
|
||||
iterator.remove();
|
||||
super.remove(element);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean add(T o) {
|
||||
checkForExpirations();
|
||||
boolean success = super.add(o);
|
||||
if (success) timestamps.put(o, System.currentTimeMillis());
|
||||
return success;
|
||||
}
|
||||
|
||||
public boolean remove(Object o) {
|
||||
checkForExpirations();
|
||||
boolean success = super.remove(o);
|
||||
if (success) timestamps.remove(o);
|
||||
return success;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
this.timestamps.clear();
|
||||
super.clear();
|
||||
}
|
||||
|
||||
public boolean contains(Object o) {
|
||||
checkForExpirations();
|
||||
return super.contains(o);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,243 @@
|
|||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.voice;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.EaglerInitialHandler;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2022-2024 lax1dude, ayunami2000. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public class VoiceServerImpl {
|
||||
|
||||
private final ServerInfo server;
|
||||
private final byte[] iceServersPacket;
|
||||
|
||||
private final Map<UUID, UserConnection> voicePlayers = new HashMap<>();
|
||||
private final Map<UUID, ExpiringSet<UUID>> voiceRequests = new HashMap<>();
|
||||
private final Set<VoicePair> voicePairs = new HashSet<>();
|
||||
|
||||
private static final int VOICE_CONNECT_RATELIMIT = 15;
|
||||
|
||||
private static class VoicePair {
|
||||
|
||||
private final UUID uuid1;
|
||||
private final UUID uuid2;
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return uuid1.hashCode() ^ uuid2.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (obj == null)
|
||||
return false;
|
||||
if (getClass() != obj.getClass())
|
||||
return false;
|
||||
VoicePair other = (VoicePair) obj;
|
||||
return (uuid1.equals(other.uuid1) && uuid2.equals(other.uuid2))
|
||||
|| (uuid1.equals(other.uuid2) && uuid2.equals(other.uuid1));
|
||||
}
|
||||
|
||||
private VoicePair(UUID uuid1, UUID uuid2) {
|
||||
this.uuid1 = uuid1;
|
||||
this.uuid2 = uuid2;
|
||||
}
|
||||
|
||||
private boolean anyEquals(UUID uuid) {
|
||||
return uuid1.equals(uuid) || uuid2.equals(uuid);
|
||||
}
|
||||
}
|
||||
|
||||
VoiceServerImpl(ServerInfo server, byte[] iceServersPacket) {
|
||||
this.server = server;
|
||||
this.iceServersPacket = iceServersPacket;
|
||||
}
|
||||
|
||||
public void handlePlayerLoggedIn(UserConnection player) {
|
||||
player.sendData(VoiceService.CHANNEL, iceServersPacket);
|
||||
}
|
||||
|
||||
public void handlePlayerLoggedOut(UserConnection player) {
|
||||
removeUser(player.getUniqueId());
|
||||
}
|
||||
|
||||
void handleVoiceSignalPacketTypeRequest(UUID player, UserConnection sender) {
|
||||
synchronized (voicePlayers) {
|
||||
UUID senderUUID = sender.getUniqueId();
|
||||
if (senderUUID.equals(player))
|
||||
return; // prevent duplicates
|
||||
if (!voicePlayers.containsKey(senderUUID))
|
||||
return;
|
||||
UserConnection targetPlayerCon = voicePlayers.get(player);
|
||||
if (targetPlayerCon == null)
|
||||
return;
|
||||
VoicePair newPair = new VoicePair(player, senderUUID);
|
||||
if (voicePairs.contains(newPair))
|
||||
return; // already paired
|
||||
ExpiringSet<UUID> senderRequestSet = voiceRequests.get(senderUUID);
|
||||
if (senderRequestSet == null) {
|
||||
voiceRequests.put(senderUUID, senderRequestSet = new ExpiringSet<>(2000));
|
||||
}
|
||||
if (!senderRequestSet.add(player)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// check if other has requested earlier
|
||||
ExpiringSet<UUID> theSet;
|
||||
if ((theSet = voiceRequests.get(player)) != null && theSet.contains(senderUUID)) {
|
||||
theSet.remove(senderUUID);
|
||||
if (theSet.isEmpty())
|
||||
voiceRequests.remove(player);
|
||||
senderRequestSet.remove(player);
|
||||
if (senderRequestSet.isEmpty())
|
||||
voiceRequests.remove(senderUUID);
|
||||
// send each other add data
|
||||
voicePairs.add(newPair);
|
||||
targetPlayerCon.sendData(VoiceService.CHANNEL,
|
||||
VoiceSignalPackets.makeVoiceSignalPacketConnect(senderUUID, false));
|
||||
sender.sendData(VoiceService.CHANNEL, VoiceSignalPackets.makeVoiceSignalPacketConnect(player, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleVoiceSignalPacketTypeConnect(UserConnection sender) {
|
||||
if(!((EaglerInitialHandler)sender.getPendingConnection()).voiceConnectRateLimiter.rateLimit(VOICE_CONNECT_RATELIMIT)) {
|
||||
return;
|
||||
}
|
||||
synchronized (voicePlayers) {
|
||||
if (voicePlayers.containsKey(sender.getUniqueId())) {
|
||||
return;
|
||||
}
|
||||
boolean hasNoOtherPlayers = voicePlayers.isEmpty();
|
||||
voicePlayers.put(sender.getUniqueId(), sender);
|
||||
if (hasNoOtherPlayers) {
|
||||
return;
|
||||
}
|
||||
byte[] packetToBroadcast = VoiceSignalPackets.makeVoiceSignalPacketGlobal(voicePlayers.values());
|
||||
for (UserConnection userCon : voicePlayers.values()) {
|
||||
userCon.sendData(VoiceService.CHANNEL, packetToBroadcast);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleVoiceSignalPacketTypeICE(UUID player, String str, UserConnection sender) {
|
||||
UserConnection pass;
|
||||
VoicePair pair = new VoicePair(player, sender.getUniqueId());
|
||||
synchronized (voicePlayers) {
|
||||
pass = voicePairs.contains(pair) ? voicePlayers.get(player) : null;
|
||||
}
|
||||
if (pass != null) {
|
||||
pass.sendData(VoiceService.CHANNEL, VoiceSignalPackets.makeVoiceSignalPacketICE(sender.getUniqueId(), str));
|
||||
}
|
||||
}
|
||||
|
||||
void handleVoiceSignalPacketTypeDesc(UUID player, String str, UserConnection sender) {
|
||||
UserConnection pass;
|
||||
VoicePair pair = new VoicePair(player, sender.getUniqueId());
|
||||
synchronized (voicePlayers) {
|
||||
pass = voicePairs.contains(pair) ? voicePlayers.get(player) : null;
|
||||
}
|
||||
if (pass != null) {
|
||||
pass.sendData(VoiceService.CHANNEL,
|
||||
VoiceSignalPackets.makeVoiceSignalPacketDesc(sender.getUniqueId(), str));
|
||||
}
|
||||
}
|
||||
|
||||
void handleVoiceSignalPacketTypeDisconnect(UUID player, UserConnection sender) {
|
||||
if (player != null) {
|
||||
synchronized (voicePlayers) {
|
||||
if (!voicePlayers.containsKey(player)) {
|
||||
return;
|
||||
}
|
||||
byte[] userDisconnectPacket = null;
|
||||
Iterator<VoicePair> pairsItr = voicePairs.iterator();
|
||||
while (pairsItr.hasNext()) {
|
||||
VoicePair voicePair = pairsItr.next();
|
||||
UUID target = null;
|
||||
if (voicePair.uuid1.equals(player)) {
|
||||
target = voicePair.uuid2;
|
||||
} else if (voicePair.uuid2.equals(player)) {
|
||||
target = voicePair.uuid1;
|
||||
}
|
||||
if (target != null) {
|
||||
pairsItr.remove();
|
||||
UserConnection conn = voicePlayers.get(target);
|
||||
if (conn != null) {
|
||||
if (userDisconnectPacket == null) {
|
||||
userDisconnectPacket = VoiceSignalPackets.makeVoiceSignalPacketDisconnect(player);
|
||||
}
|
||||
conn.sendData(VoiceService.CHANNEL, userDisconnectPacket);
|
||||
}
|
||||
sender.sendData(VoiceService.CHANNEL,
|
||||
VoiceSignalPackets.makeVoiceSignalPacketDisconnect(target));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
removeUser(sender.getUniqueId());
|
||||
}
|
||||
}
|
||||
|
||||
public void removeUser(UUID user) {
|
||||
synchronized (voicePlayers) {
|
||||
if (voicePlayers.remove(user) == null) {
|
||||
return;
|
||||
}
|
||||
voiceRequests.remove(user);
|
||||
if (voicePlayers.size() > 0) {
|
||||
byte[] voicePlayersPkt = VoiceSignalPackets.makeVoiceSignalPacketGlobal(voicePlayers.values());
|
||||
for (UserConnection userCon : voicePlayers.values()) {
|
||||
if (!user.equals(userCon.getUniqueId())) {
|
||||
userCon.sendData(VoiceService.CHANNEL, voicePlayersPkt);
|
||||
}
|
||||
}
|
||||
}
|
||||
byte[] userDisconnectPacket = null;
|
||||
Iterator<VoicePair> pairsItr = voicePairs.iterator();
|
||||
while (pairsItr.hasNext()) {
|
||||
VoicePair voicePair = pairsItr.next();
|
||||
UUID target = null;
|
||||
if (voicePair.uuid1.equals(user)) {
|
||||
target = voicePair.uuid2;
|
||||
} else if (voicePair.uuid2.equals(user)) {
|
||||
target = voicePair.uuid1;
|
||||
}
|
||||
if (target != null) {
|
||||
pairsItr.remove();
|
||||
if (voicePlayers.size() > 0) {
|
||||
UserConnection conn = voicePlayers.get(target);
|
||||
if (conn != null) {
|
||||
if (userDisconnectPacket == null) {
|
||||
userDisconnectPacket = VoiceSignalPackets.makeVoiceSignalPacketDisconnect(user);
|
||||
}
|
||||
conn.sendData(VoiceService.CHANNEL, userDisconnectPacket);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.voice;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import gnu.trove.map.TMap;
|
||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerBungeeConfig;
|
||||
import net.md_5.bungee.BungeeCord;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
import net.md_5.bungee.api.config.ServerInfo;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public class VoiceService {
|
||||
|
||||
public static final String CHANNEL = "EAG|Voice-1.8";
|
||||
|
||||
private final Map<String, VoiceServerImpl> serverMap = new HashMap();
|
||||
private final byte[] disableVoicePacket;
|
||||
|
||||
public VoiceService(EaglerBungeeConfig conf) {
|
||||
this.disableVoicePacket = VoiceSignalPackets.makeVoiceSignalPacketAllowed(false, null);
|
||||
String[] iceServers = conf.getICEServers().toArray(new String[conf.getICEServers().size()]);
|
||||
byte[] iceServersPacket = VoiceSignalPackets.makeVoiceSignalPacketAllowed(true, iceServers);
|
||||
TMap<String,ServerInfo> servers = BungeeCord.getInstance().config.getServers();
|
||||
Set<String> keySet = new HashSet(servers.keySet());
|
||||
keySet.removeAll(conf.getDisableVoiceOnServersSet());
|
||||
for(String s : keySet) {
|
||||
serverMap.put(s, new VoiceServerImpl(servers.get(s), iceServersPacket));
|
||||
}
|
||||
}
|
||||
|
||||
public void handlePlayerLoggedIn(UserConnection player) {
|
||||
|
||||
}
|
||||
|
||||
public void handlePlayerLoggedOut(UserConnection player) {
|
||||
|
||||
}
|
||||
|
||||
public void handleServerConnected(UserConnection player, ServerInfo server) {
|
||||
VoiceServerImpl svr = serverMap.get(server.getName());
|
||||
if(svr != null) {
|
||||
svr.handlePlayerLoggedIn(player);
|
||||
}else {
|
||||
player.sendData(CHANNEL, disableVoicePacket);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleServerDisconnected(UserConnection player, ServerInfo server) {
|
||||
VoiceServerImpl svr = serverMap.get(server.getName());
|
||||
if(svr != null) {
|
||||
svr.handlePlayerLoggedOut(player);
|
||||
}
|
||||
}
|
||||
|
||||
void handleVoiceSignalPacketTypeRequest(UUID player, UserConnection sender) {
|
||||
if(sender.getServer() != null) {
|
||||
VoiceServerImpl svr = serverMap.get(sender.getServer().getInfo().getName());
|
||||
if(svr != null) {
|
||||
svr.handleVoiceSignalPacketTypeRequest(player, sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleVoiceSignalPacketTypeConnect(UserConnection sender) {
|
||||
if(sender.getServer() != null) {
|
||||
VoiceServerImpl svr = serverMap.get(sender.getServer().getInfo().getName());
|
||||
if(svr != null) {
|
||||
svr.handleVoiceSignalPacketTypeConnect(sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleVoiceSignalPacketTypeICE(UUID player, String str, UserConnection sender) {
|
||||
if(sender.getServer() != null) {
|
||||
VoiceServerImpl svr = serverMap.get(sender.getServer().getInfo().getName());
|
||||
if(svr != null) {
|
||||
svr.handleVoiceSignalPacketTypeICE(player, str, sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleVoiceSignalPacketTypeDesc(UUID player, String str, UserConnection sender) {
|
||||
if(sender.getServer() != null) {
|
||||
VoiceServerImpl svr = serverMap.get(sender.getServer().getInfo().getName());
|
||||
if(svr != null) {
|
||||
svr.handleVoiceSignalPacketTypeDesc(player, str, sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void handleVoiceSignalPacketTypeDisconnect(UUID player, UserConnection sender) {
|
||||
if(sender.getServer() != null) {
|
||||
VoiceServerImpl svr = serverMap.get(sender.getServer().getInfo().getName());
|
||||
if(svr != null) {
|
||||
svr.handleVoiceSignalPacketTypeDisconnect(player, sender);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,194 @@
|
|||
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.voice;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.md_5.bungee.UserConnection;
|
||||
import net.md_5.bungee.protocol.DefinedPacket;
|
||||
|
||||
/**
|
||||
* Copyright (c) 2024 lax1dude. All Rights Reserved.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
||||
* IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
|
||||
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
||||
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
* POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
*/
|
||||
public class VoiceSignalPackets {
|
||||
|
||||
static final int VOICE_SIGNAL_ALLOWED = 0;
|
||||
static final int VOICE_SIGNAL_REQUEST = 0;
|
||||
static final int VOICE_SIGNAL_CONNECT = 1;
|
||||
static final int VOICE_SIGNAL_DISCONNECT = 2;
|
||||
static final int VOICE_SIGNAL_ICE = 3;
|
||||
static final int VOICE_SIGNAL_DESC = 4;
|
||||
static final int VOICE_SIGNAL_GLOBAL = 5;
|
||||
|
||||
public static void processPacket(byte[] data, UserConnection sender, VoiceService voiceService) throws IOException {
|
||||
int packetId = -1;
|
||||
if(data.length == 0) {
|
||||
throw new IOException("Zero-length packet recieved");
|
||||
}
|
||||
try {
|
||||
ByteBuf buffer = Unpooled.wrappedBuffer(data).writerIndex(data.length);
|
||||
packetId = buffer.readUnsignedByte();
|
||||
switch(packetId) {
|
||||
case VOICE_SIGNAL_REQUEST: {
|
||||
voiceService.handleVoiceSignalPacketTypeRequest(DefinedPacket.readUUID(buffer), sender);
|
||||
break;
|
||||
}
|
||||
case VOICE_SIGNAL_CONNECT: {
|
||||
voiceService.handleVoiceSignalPacketTypeConnect(sender);
|
||||
break;
|
||||
}
|
||||
case VOICE_SIGNAL_ICE: {
|
||||
voiceService.handleVoiceSignalPacketTypeICE(DefinedPacket.readUUID(buffer), DefinedPacket.readString(buffer, 32767), sender);
|
||||
break;
|
||||
}
|
||||
case VOICE_SIGNAL_DESC: {
|
||||
voiceService.handleVoiceSignalPacketTypeDesc(DefinedPacket.readUUID(buffer), DefinedPacket.readString(buffer, 32767), sender);
|
||||
break;
|
||||
}
|
||||
case VOICE_SIGNAL_DISCONNECT: {
|
||||
voiceService.handleVoiceSignalPacketTypeDisconnect(buffer.readableBytes() > 0 ? DefinedPacket.readUUID(buffer) : null, sender);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new IOException("Unknown packet type " + packetId);
|
||||
}
|
||||
}
|
||||
if(buffer.readableBytes() > 0) {
|
||||
throw new IOException("Voice packet is too long!");
|
||||
}
|
||||
}catch(IOException ex) {
|
||||
throw ex;
|
||||
}catch(Throwable t) {
|
||||
throw new IOException("Unhandled exception handling voice packet type " + packetId, t);
|
||||
}
|
||||
}
|
||||
|
||||
static byte[] makeVoiceSignalPacketAllowed(boolean allowed, String[] iceServers) {
|
||||
if (iceServers == null) {
|
||||
byte[] ret = new byte[2];
|
||||
ByteBuf wrappedBuffer = Unpooled.wrappedBuffer(ret).writerIndex(0);
|
||||
wrappedBuffer.writeByte(VOICE_SIGNAL_ALLOWED);
|
||||
wrappedBuffer.writeBoolean(allowed);
|
||||
return ret;
|
||||
}
|
||||
byte[][] iceServersBytes = new byte[iceServers.length][];
|
||||
int totalLen = 2 + getVarIntSize(iceServers.length);
|
||||
for(int i = 0; i < iceServers.length; ++i) {
|
||||
byte[] b = iceServersBytes[i] = iceServers[i].getBytes(StandardCharsets.UTF_8);
|
||||
totalLen += getVarIntSize(b.length) + b.length;
|
||||
}
|
||||
byte[] ret = new byte[totalLen];
|
||||
ByteBuf wrappedBuffer = Unpooled.wrappedBuffer(ret).writerIndex(0);
|
||||
wrappedBuffer.writeByte(VOICE_SIGNAL_ALLOWED);
|
||||
wrappedBuffer.writeBoolean(allowed);
|
||||
DefinedPacket.writeVarInt(iceServersBytes.length, wrappedBuffer);
|
||||
for(int i = 0; i < iceServersBytes.length; ++i) {
|
||||
byte[] b = iceServersBytes[i];
|
||||
DefinedPacket.writeVarInt(b.length, wrappedBuffer);
|
||||
wrappedBuffer.writeBytes(b);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static byte[] makeVoiceSignalPacketGlobal(Collection<UserConnection> users) {
|
||||
int cnt = users.size();
|
||||
byte[][] displayNames = new byte[cnt][];
|
||||
int i = 0;
|
||||
for(UserConnection user : users) {
|
||||
String name = user.getDisplayName();
|
||||
if(name.length() > 16) name = name.substring(0, 16);
|
||||
displayNames[i++] = name.getBytes(StandardCharsets.UTF_8);
|
||||
}
|
||||
int totalLength = 1 + getVarIntSize(cnt) + (cnt << 4);
|
||||
for(i = 0; i < cnt; ++i) {
|
||||
totalLength += getVarIntSize(displayNames[i].length) + displayNames[i].length;
|
||||
}
|
||||
byte[] ret = new byte[totalLength];
|
||||
ByteBuf wrappedBuffer = Unpooled.wrappedBuffer(ret).writerIndex(0);
|
||||
wrappedBuffer.writeByte(VOICE_SIGNAL_GLOBAL);
|
||||
DefinedPacket.writeVarInt(cnt, wrappedBuffer);
|
||||
for(UserConnection user : users) {
|
||||
DefinedPacket.writeUUID(user.getUniqueId(), wrappedBuffer);
|
||||
}
|
||||
for(i = 0; i < cnt; ++i) {
|
||||
DefinedPacket.writeVarInt(displayNames[i].length, wrappedBuffer);
|
||||
wrappedBuffer.writeBytes(displayNames[i]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static byte[] makeVoiceSignalPacketConnect(UUID player, boolean offer) {
|
||||
byte[] ret = new byte[18];
|
||||
ByteBuf wrappedBuffer = Unpooled.wrappedBuffer(ret).writerIndex(0);
|
||||
wrappedBuffer.writeByte(VOICE_SIGNAL_CONNECT);
|
||||
DefinedPacket.writeUUID(player, wrappedBuffer);
|
||||
wrappedBuffer.writeBoolean(offer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static byte[] makeVoiceSignalPacketConnectAnnounce(UUID player) {
|
||||
byte[] ret = new byte[17];
|
||||
ByteBuf wrappedBuffer = Unpooled.wrappedBuffer(ret).writerIndex(0);
|
||||
wrappedBuffer.writeByte(VOICE_SIGNAL_CONNECT);
|
||||
DefinedPacket.writeUUID(player, wrappedBuffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static byte[] makeVoiceSignalPacketDisconnect(UUID player) {
|
||||
if(player == null) {
|
||||
return new byte[] { (byte)VOICE_SIGNAL_DISCONNECT };
|
||||
}
|
||||
byte[] ret = new byte[17];
|
||||
ByteBuf wrappedBuffer = Unpooled.wrappedBuffer(ret).writerIndex(0);
|
||||
wrappedBuffer.writeByte(VOICE_SIGNAL_DISCONNECT);
|
||||
DefinedPacket.writeUUID(player, wrappedBuffer);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static byte[] makeVoiceSignalPacketICE(UUID player, String str) {
|
||||
byte[] strBytes = str.getBytes(StandardCharsets.UTF_8);
|
||||
byte[] ret = new byte[17 + getVarIntSize(strBytes.length) + strBytes.length];
|
||||
ByteBuf wrappedBuffer = Unpooled.wrappedBuffer(ret).writerIndex(0);
|
||||
wrappedBuffer.writeByte(VOICE_SIGNAL_ICE);
|
||||
DefinedPacket.writeUUID(player, wrappedBuffer);
|
||||
DefinedPacket.writeVarInt(strBytes.length, wrappedBuffer);
|
||||
wrappedBuffer.writeBytes(strBytes);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static byte[] makeVoiceSignalPacketDesc(UUID player, String str) {
|
||||
byte[] strBytes = str.getBytes(StandardCharsets.UTF_8);
|
||||
byte[] ret = new byte[17 + getVarIntSize(strBytes.length) + strBytes.length];
|
||||
ByteBuf wrappedBuffer = Unpooled.wrappedBuffer(ret).writerIndex(0);
|
||||
wrappedBuffer.writeByte(VOICE_SIGNAL_DESC);
|
||||
DefinedPacket.writeUUID(player, wrappedBuffer);
|
||||
DefinedPacket.writeVarInt(strBytes.length, wrappedBuffer);
|
||||
wrappedBuffer.writeBytes(strBytes);
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static int getVarIntSize(int input) {
|
||||
for (int i = 1; i < 5; ++i) {
|
||||
if ((input & -1 << i * 7) == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
||||
return 5;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
voice_stun_servers:
|
||||
- 'stun:stun.l.google.com:19302'
|
||||
- 'stun:stun1.l.google.com:19302'
|
||||
- 'stun:stun2.l.google.com:19302'
|
||||
- 'stun:stun3.l.google.com:19302'
|
||||
- 'stun:stun4.l.google.com:19302'
|
||||
- 'stun:openrelay.metered.ca:80'
|
||||
voice_turn_servers:
|
||||
openrelay1:
|
||||
url: 'turn:openrelay.metered.ca:80'
|
||||
username: 'openrelayproject'
|
||||
password: 'openrelayproject'
|
||||
openrelay2:
|
||||
url: 'turn:openrelay.metered.ca:443'
|
||||
username: 'openrelayproject'
|
||||
password: 'openrelayproject'
|
||||
openrelay3:
|
||||
url: 'turn:openrelay.metered.ca:443?transport=tcp'
|
||||
username: 'openrelayproject'
|
||||
password: 'openrelayproject'
|
|
@ -26,6 +26,7 @@ listener_01:
|
|||
page_index_name:
|
||||
- 'index.html'
|
||||
- 'index.htm'
|
||||
allow_voice: false
|
||||
ratelimit:
|
||||
ip:
|
||||
enable: true
|
||||
|
|
|
@ -19,3 +19,7 @@ skin_cache_antagonists_ratelimit: 15
|
|||
sql_driver_class: 'internal'
|
||||
sql_driver_path: 'internal'
|
||||
eagler_players_vanilla_skin: ''
|
||||
enable_is_eagler_player_property: true
|
||||
disable_voice_chat_on_servers: []
|
||||
disable_fnaw_skins_everywhere: false
|
||||
disable_fnaw_skins_on_servers: []
|
|
@ -1,5 +1,5 @@
|
|||
name: EaglercraftXBungee
|
||||
main: net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee
|
||||
version: 1.0.10
|
||||
version: 1.1.0
|
||||
description: Plugin to allow EaglercraftX 1.8 players to join your network, or allow EaglercraftX 1.8 players to use your network as a proxy to join other networks
|
||||
author: lax1dude
|
|
@ -1 +1 @@
|
|||
1.0.10
|
||||
1.1.0
|
Loading…
Reference in New Issue
Block a user