(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.server.web.HttpWebServer;
|
||||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.shit.CompatWarning;
|
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.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.ISkinService;
|
||||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.SkinService;
|
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.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.Plugin;
|
||||||
import net.md_5.bungee.api.plugin.PluginManager;
|
import net.md_5.bungee.api.plugin.PluginManager;
|
||||||
import net.md_5.bungee.netty.PipelineUtils;
|
import net.md_5.bungee.netty.PipelineUtils;
|
||||||
|
@ -55,9 +57,8 @@ import net.md_5.bungee.BungeeCord;
|
||||||
*/
|
*/
|
||||||
public class EaglerXBungee extends Plugin {
|
public class EaglerXBungee extends Plugin {
|
||||||
|
|
||||||
public static final String NATIVE_BUNGEECORD_BUILD = "1.20-R0.3-SNAPSHOT:eda268b:1797";
|
public static final String NATIVE_BUNGEECORD_BUILD = "1.20-R0.3-SNAPSHOT:67c65e0:1828";
|
||||||
public static final String NATIVE_WATERFALL_BUILD = "1.20-R0.2-SNAPSHOT:92b5149:562";
|
public static final String NATIVE_WATERFALL_BUILD = "1.20-R0.3-SNAPSHOT:da6aaf6:572";
|
||||||
public static final String NATIVE_FLAMECORD_BUILD = "1.1.1";
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
CompatWarning.displayCompatWarning();
|
CompatWarning.displayCompatWarning();
|
||||||
|
@ -72,6 +73,8 @@ public class EaglerXBungee extends Plugin {
|
||||||
private Timer authServiceTasks = null;
|
private Timer authServiceTasks = null;
|
||||||
private final ChannelFutureListener newChannelListener;
|
private final ChannelFutureListener newChannelListener;
|
||||||
private ISkinService skinService;
|
private ISkinService skinService;
|
||||||
|
private CapeServiceOffline capeService;
|
||||||
|
private VoiceService voiceService;
|
||||||
private DefaultAuthSystem defaultAuthSystem;
|
private DefaultAuthSystem defaultAuthSystem;
|
||||||
|
|
||||||
public EaglerXBungee() {
|
public EaglerXBungee() {
|
||||||
|
@ -128,7 +131,10 @@ public class EaglerXBungee extends Plugin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getProxy().registerChannel(SkinService.CHANNEL);
|
getProxy().registerChannel(SkinService.CHANNEL);
|
||||||
|
getProxy().registerChannel(CapeServiceOffline.CHANNEL);
|
||||||
getProxy().registerChannel(EaglerPipeline.UPDATE_CERT_CHANNEL);
|
getProxy().registerChannel(EaglerPipeline.UPDATE_CERT_CHANNEL);
|
||||||
|
getProxy().registerChannel(VoiceService.CHANNEL);
|
||||||
|
getProxy().registerChannel(EaglerPacketEventListener.FNAW_SKIN_ENABLE_CHANNEL);
|
||||||
startListeners();
|
startListeners();
|
||||||
if(skinServiceTasks != null) {
|
if(skinServiceTasks != null) {
|
||||||
skinServiceTasks.cancel();
|
skinServiceTasks.cancel();
|
||||||
|
@ -165,6 +171,7 @@ public class EaglerXBungee extends Plugin {
|
||||||
}
|
}
|
||||||
}, 1000l, 1000l);
|
}, 1000l, 1000l);
|
||||||
}
|
}
|
||||||
|
capeService = new CapeServiceOffline();
|
||||||
if(authConf.isEnableAuthentication() && authConf.isUseBuiltInAuthentication()) {
|
if(authConf.isEnableAuthentication() && authConf.isUseBuiltInAuthentication()) {
|
||||||
try {
|
try {
|
||||||
defaultAuthSystem = DefaultAuthSystem.initializeAuthSystem(authConf);
|
defaultAuthSystem = DefaultAuthSystem.initializeAuthSystem(authConf);
|
||||||
|
@ -185,6 +192,12 @@ public class EaglerXBungee extends Plugin {
|
||||||
}, 60000l, 60000l);
|
}, 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
|
@Override
|
||||||
|
@ -193,13 +206,19 @@ public class EaglerXBungee extends Plugin {
|
||||||
mgr.unregisterListeners(this);
|
mgr.unregisterListeners(this);
|
||||||
mgr.unregisterCommands(this);
|
mgr.unregisterCommands(this);
|
||||||
getProxy().unregisterChannel(SkinService.CHANNEL);
|
getProxy().unregisterChannel(SkinService.CHANNEL);
|
||||||
|
getProxy().unregisterChannel(CapeServiceOffline.CHANNEL);
|
||||||
getProxy().unregisterChannel(EaglerPipeline.UPDATE_CERT_CHANNEL);
|
getProxy().unregisterChannel(EaglerPipeline.UPDATE_CERT_CHANNEL);
|
||||||
|
getProxy().unregisterChannel(VoiceService.CHANNEL);
|
||||||
|
getProxy().unregisterChannel(EaglerPacketEventListener.FNAW_SKIN_ENABLE_CHANNEL);
|
||||||
stopListeners();
|
stopListeners();
|
||||||
if(skinServiceTasks != null) {
|
if(skinServiceTasks != null) {
|
||||||
skinServiceTasks.cancel();
|
skinServiceTasks.cancel();
|
||||||
skinServiceTasks = null;
|
skinServiceTasks = null;
|
||||||
}
|
}
|
||||||
skinService.shutdown();
|
skinService.shutdown();
|
||||||
|
skinService = null;
|
||||||
|
capeService.shutdown();
|
||||||
|
capeService = null;
|
||||||
if(defaultAuthSystem != null) {
|
if(defaultAuthSystem != null) {
|
||||||
defaultAuthSystem.destroy();
|
defaultAuthSystem.destroy();
|
||||||
defaultAuthSystem = null;
|
defaultAuthSystem = null;
|
||||||
|
@ -208,6 +227,7 @@ public class EaglerXBungee extends Plugin {
|
||||||
authServiceTasks = null;
|
authServiceTasks = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
voiceService = null;
|
||||||
BinaryHttpClient.killEventLoop();
|
BinaryHttpClient.killEventLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -278,10 +298,18 @@ public class EaglerXBungee extends Plugin {
|
||||||
return skinService;
|
return skinService;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public CapeServiceOffline getCapeService() {
|
||||||
|
return capeService;
|
||||||
|
}
|
||||||
|
|
||||||
public DefaultAuthSystem getAuthService() {
|
public DefaultAuthSystem getAuthService() {
|
||||||
return defaultAuthSystem;
|
return defaultAuthSystem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public VoiceService getVoiceService() {
|
||||||
|
return voiceService;
|
||||||
|
}
|
||||||
|
|
||||||
public static EaglerXBungee getEagler() {
|
public static EaglerXBungee getEagler() {
|
||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,12 +9,14 @@ import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.io.PrintWriter;
|
import java.io.PrintWriter;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
import com.google.gson.JsonArray;
|
import com.google.gson.JsonArray;
|
||||||
|
@ -31,7 +33,7 @@ import net.md_5.bungee.config.YamlConfiguration;
|
||||||
import net.md_5.bungee.protocol.Property;
|
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
|
* 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
|
* 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"));
|
Configuration listenerYml = prov.load(getConfigFile(directory, "listeners.yml"));
|
||||||
Iterator<String> listeners = listenerYml.getKeys().iterator();
|
Iterator<String> listeners = listenerYml.getKeys().iterator();
|
||||||
Map<String, EaglerListenerConfig> serverListeners = new HashMap();
|
Map<String, EaglerListenerConfig> serverListeners = new HashMap();
|
||||||
|
boolean voiceChat = false;
|
||||||
|
|
||||||
while(listeners.hasNext()) {
|
while(listeners.hasNext()) {
|
||||||
String name = listeners.next();
|
String name = listeners.next();
|
||||||
EaglerListenerConfig conf = EaglerListenerConfig.loadConfig(listenerYml.getSection(name), contentTypes);
|
EaglerListenerConfig conf = EaglerListenerConfig.loadConfig(listenerYml.getSection(name), contentTypes);
|
||||||
if(conf != null) {
|
if(conf != null) {
|
||||||
serverListeners.put(name, conf);
|
serverListeners.put(name, conf);
|
||||||
|
voiceChat |= conf.getEnableVoiceChat();
|
||||||
}else {
|
}else {
|
||||||
EaglerXBungee.logger().severe("Invalid listener config: " + name);
|
EaglerXBungee.logger().severe("Invalid listener config: " + name);
|
||||||
}
|
}
|
||||||
|
@ -105,6 +109,9 @@ public class EaglerBungeeConfig {
|
||||||
Configuration updatesYml = prov.load(getConfigFile(directory, "updates.yml"));
|
Configuration updatesYml = prov.load(getConfigFile(directory, "updates.yml"));
|
||||||
EaglerUpdateConfig updatesConfig = EaglerUpdateConfig.loadConfig(updatesYml);
|
EaglerUpdateConfig updatesConfig = EaglerUpdateConfig.loadConfig(updatesYml);
|
||||||
|
|
||||||
|
Configuration iceServersYml = prov.load(getConfigFile(directory, "ice_servers.yml"));
|
||||||
|
Collection<String> iceServers = loadICEServers(iceServersYml);
|
||||||
|
|
||||||
if(authConfig.isEnableAuthentication()) {
|
if(authConfig.isEnableAuthentication()) {
|
||||||
for(EaglerListenerConfig lst : serverListeners.values()) {
|
for(EaglerListenerConfig lst : serverListeners.values()) {
|
||||||
if(lst.getRatelimitLogin() != null) lst.getRatelimitLogin().setDivisor(2);
|
if(lst.getRatelimitLogin() != null) lst.getRatelimitLogin().setDivisor(2);
|
||||||
|
@ -135,12 +142,18 @@ public class EaglerBungeeConfig {
|
||||||
if(eaglerPlayersVanillaSkin != null && eaglerPlayersVanillaSkin.length() == 0) {
|
if(eaglerPlayersVanillaSkin != null && eaglerPlayersVanillaSkin.length() == 0) {
|
||||||
eaglerPlayersVanillaSkin = null;
|
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,
|
final EaglerBungeeConfig ret = new EaglerBungeeConfig(serverName, serverUUID, websocketKeepAliveTimeout,
|
||||||
websocketHandshakeTimeout, websocketCompressionLevel, serverListeners, contentTypes,
|
websocketHandshakeTimeout, websocketCompressionLevel, serverListeners, contentTypes,
|
||||||
downloadVanillaSkins, validSkinUrls, uuidRateLimitPlayer, uuidRateLimitGlobal, skinRateLimitPlayer,
|
downloadVanillaSkins, validSkinUrls, uuidRateLimitPlayer, uuidRateLimitGlobal, skinRateLimitPlayer,
|
||||||
skinRateLimitGlobal, skinCacheURI, keepObjectsDays, keepProfilesDays, maxObjects, maxProfiles,
|
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) {
|
if(eaglerPlayersVanillaSkin != null) {
|
||||||
VanillaDefaultSkinProfileLoader.lookupVanillaSkinUser(ret);
|
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")
|
@SuppressWarnings("deprecation")
|
||||||
private static JsonObject parseJsonObject(InputStream file) throws IOException {
|
private static JsonObject parseJsonObject(InputStream file) throws IOException {
|
||||||
StringBuilder str = new StringBuilder();
|
StringBuilder str = new StringBuilder();
|
||||||
|
@ -243,8 +268,14 @@ public class EaglerBungeeConfig {
|
||||||
private final String sqliteDriverClass;
|
private final String sqliteDriverClass;
|
||||||
private final String sqliteDriverPath;
|
private final String sqliteDriverPath;
|
||||||
private final String eaglerPlayersVanillaSkin;
|
private final String eaglerPlayersVanillaSkin;
|
||||||
|
private final boolean enableIsEaglerPlayerProperty;
|
||||||
private final EaglerAuthConfig authConfig;
|
private final EaglerAuthConfig authConfig;
|
||||||
private final EaglerUpdateConfig updateConfig;
|
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 };
|
Property[] eaglerPlayersVanillaSkinCached = new Property[] { isEaglerProperty };
|
||||||
|
|
||||||
public String getServerName() {
|
public String getServerName() {
|
||||||
|
@ -362,6 +393,10 @@ public class EaglerBungeeConfig {
|
||||||
return eaglerPlayersVanillaSkin;
|
return eaglerPlayersVanillaSkin;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean getEnableIsEaglerPlayerProperty() {
|
||||||
|
return enableIsEaglerPlayerProperty;
|
||||||
|
}
|
||||||
|
|
||||||
public Property[] getEaglerPlayersVanillaSkinProperties() {
|
public Property[] getEaglerPlayersVanillaSkinProperties() {
|
||||||
return eaglerPlayersVanillaSkinCached;
|
return eaglerPlayersVanillaSkinCached;
|
||||||
}
|
}
|
||||||
|
@ -378,6 +413,26 @@ public class EaglerBungeeConfig {
|
||||||
return updateConfig;
|
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,
|
private EaglerBungeeConfig(String serverName, UUID serverUUID, long websocketKeepAliveTimeout,
|
||||||
long websocketHandshakeTimeout, int httpWebsocketCompressionLevel,
|
long websocketHandshakeTimeout, int httpWebsocketCompressionLevel,
|
||||||
Map<String, EaglerListenerConfig> serverListeners, Map<String, HttpContentType> contentTypes,
|
Map<String, EaglerListenerConfig> serverListeners, Map<String, HttpContentType> contentTypes,
|
||||||
|
@ -385,7 +440,9 @@ public class EaglerBungeeConfig {
|
||||||
int uuidRateLimitGlobal, int skinRateLimitPlayer, int skinRateLimitGlobal, String skinCacheURI,
|
int uuidRateLimitGlobal, int skinRateLimitPlayer, int skinRateLimitGlobal, String skinCacheURI,
|
||||||
int keepObjectsDays, int keepProfilesDays, int maxObjects, int maxProfiles, int antagonistsRateLimit,
|
int keepObjectsDays, int keepProfilesDays, int maxObjects, int maxProfiles, int antagonistsRateLimit,
|
||||||
String sqliteDriverClass, String sqliteDriverPath, String eaglerPlayersVanillaSkin,
|
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.serverName = serverName;
|
||||||
this.serverUUID = serverUUID;
|
this.serverUUID = serverUUID;
|
||||||
this.serverListeners = serverListeners;
|
this.serverListeners = serverListeners;
|
||||||
|
@ -408,8 +465,14 @@ public class EaglerBungeeConfig {
|
||||||
this.sqliteDriverClass = sqliteDriverClass;
|
this.sqliteDriverClass = sqliteDriverClass;
|
||||||
this.sqliteDriverPath = sqliteDriverPath;
|
this.sqliteDriverPath = sqliteDriverPath;
|
||||||
this.eaglerPlayersVanillaSkin = eaglerPlayersVanillaSkin;
|
this.eaglerPlayersVanillaSkin = eaglerPlayersVanillaSkin;
|
||||||
|
this.enableIsEaglerPlayerProperty = enableIsEaglerPlayerProperty;
|
||||||
this.authConfig = authConfig;
|
this.authConfig = authConfig;
|
||||||
this.updateConfig = updateConfig;
|
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);
|
contentTypes, indexPage, page404);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean enableVoiceChat = config.getBoolean("allow_voice", false);
|
||||||
|
|
||||||
EaglerRateLimiter ratelimitIp = null;
|
EaglerRateLimiter ratelimitIp = null;
|
||||||
EaglerRateLimiter ratelimitLogin = null;
|
EaglerRateLimiter ratelimitLogin = null;
|
||||||
EaglerRateLimiter ratelimitMOTD = null;
|
EaglerRateLimiter ratelimitMOTD = null;
|
||||||
|
@ -149,7 +151,7 @@ public class EaglerListenerConfig extends ListenerInfo {
|
||||||
cacheTrending, cachePortfolios);
|
cacheTrending, cachePortfolios);
|
||||||
return new EaglerListenerConfig(hostv4, hostv6, maxPlayer, tabListType, defaultServer, forceDefaultServer,
|
return new EaglerListenerConfig(hostv4, hostv6, maxPlayer, tabListType, defaultServer, forceDefaultServer,
|
||||||
forwardIp, forwardIpHeader, redirectLegacyClientsTo, serverIcon, serverMOTD, allowMOTD, allowQuery,
|
forwardIp, forwardIpHeader, redirectLegacyClientsTo, serverIcon, serverMOTD, allowMOTD, allowQuery,
|
||||||
cacheConfig, httpServer, ratelimitIp, ratelimitLogin, ratelimitMOTD, ratelimitQuery);
|
cacheConfig, httpServer, enableVoiceChat, ratelimitIp, ratelimitLogin, ratelimitMOTD, ratelimitQuery);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final InetSocketAddress address;
|
private final InetSocketAddress address;
|
||||||
|
@ -169,6 +171,7 @@ public class EaglerListenerConfig extends ListenerInfo {
|
||||||
private final HttpWebServer webServer;
|
private final HttpWebServer webServer;
|
||||||
private boolean serverIconSet = false;
|
private boolean serverIconSet = false;
|
||||||
private int[] serverIconPixels = null;
|
private int[] serverIconPixels = null;
|
||||||
|
private final boolean enableVoiceChat;
|
||||||
private final EaglerRateLimiter ratelimitIp;
|
private final EaglerRateLimiter ratelimitIp;
|
||||||
private final EaglerRateLimiter ratelimitLogin;
|
private final EaglerRateLimiter ratelimitLogin;
|
||||||
private final EaglerRateLimiter ratelimitMOTD;
|
private final EaglerRateLimiter ratelimitMOTD;
|
||||||
|
@ -178,8 +181,8 @@ public class EaglerListenerConfig extends ListenerInfo {
|
||||||
String tabListType, String defaultServer, boolean forceDefaultServer, boolean forwardIp,
|
String tabListType, String defaultServer, boolean forceDefaultServer, boolean forwardIp,
|
||||||
String forwardIpHeader, String redirectLegacyClientsTo, String serverIcon, List<String> serverMOTD,
|
String forwardIpHeader, String redirectLegacyClientsTo, String serverIcon, List<String> serverMOTD,
|
||||||
boolean allowMOTD, boolean allowQuery, MOTDCacheConfiguration motdCacheConfig, HttpWebServer webServer,
|
boolean allowMOTD, boolean allowQuery, MOTDCacheConfiguration motdCacheConfig, HttpWebServer webServer,
|
||||||
EaglerRateLimiter ratelimitIp, EaglerRateLimiter ratelimitLogin, EaglerRateLimiter ratelimitMOTD,
|
boolean enableVoiceChat, EaglerRateLimiter ratelimitIp, EaglerRateLimiter ratelimitLogin,
|
||||||
EaglerRateLimiter ratelimitQuery) {
|
EaglerRateLimiter ratelimitMOTD, EaglerRateLimiter ratelimitQuery) {
|
||||||
super(address, String.join("\n", serverMOTD), maxPlayer, 60, Arrays.asList(defaultServer), forceDefaultServer,
|
super(address, String.join("\n", serverMOTD), maxPlayer, 60, Arrays.asList(defaultServer), forceDefaultServer,
|
||||||
Collections.emptyMap(), tabListType, false, false, 0, false, false);
|
Collections.emptyMap(), tabListType, false, false, 0, false, false);
|
||||||
this.address = address;
|
this.address = address;
|
||||||
|
@ -197,6 +200,7 @@ public class EaglerListenerConfig extends ListenerInfo {
|
||||||
this.allowQuery = allowQuery;
|
this.allowQuery = allowQuery;
|
||||||
this.motdCacheConfig = motdCacheConfig;
|
this.motdCacheConfig = motdCacheConfig;
|
||||||
this.webServer = webServer;
|
this.webServer = webServer;
|
||||||
|
this.enableVoiceChat = enableVoiceChat;
|
||||||
this.ratelimitIp = ratelimitIp;
|
this.ratelimitIp = ratelimitIp;
|
||||||
this.ratelimitLogin = ratelimitLogin;
|
this.ratelimitLogin = ratelimitLogin;
|
||||||
this.ratelimitMOTD = ratelimitMOTD;
|
this.ratelimitMOTD = ratelimitMOTD;
|
||||||
|
@ -285,6 +289,10 @@ public class EaglerListenerConfig extends ListenerInfo {
|
||||||
return redirectLegacyClientsTo;
|
return redirectLegacyClientsTo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean getEnableVoiceChat() {
|
||||||
|
return enableVoiceChat;
|
||||||
|
}
|
||||||
|
|
||||||
public EaglerRateLimiter getRatelimitIp() {
|
public EaglerRateLimiter getRatelimitIp() {
|
||||||
return ratelimitIp;
|
return ratelimitIp;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.handlers;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.UUID;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
import org.apache.commons.codec.binary.Base64;
|
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.auth.DefaultAuthSystem;
|
||||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.config.EaglerAuthConfig;
|
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.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.SkinPackets;
|
||||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.SkinService;
|
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.UserConnection;
|
||||||
import net.md_5.bungee.api.ProxyServer;
|
import net.md_5.bungee.api.ProxyServer;
|
||||||
import net.md_5.bungee.api.chat.TextComponent;
|
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.ProxiedPlayer;
|
||||||
import net.md_5.bungee.api.connection.Server;
|
import net.md_5.bungee.api.connection.Server;
|
||||||
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
|
import net.md_5.bungee.api.event.PlayerDisconnectEvent;
|
||||||
import net.md_5.bungee.api.event.PluginMessageEvent;
|
import net.md_5.bungee.api.event.PluginMessageEvent;
|
||||||
import net.md_5.bungee.api.event.PostLoginEvent;
|
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.api.plugin.Listener;
|
||||||
import net.md_5.bungee.connection.InitialHandler;
|
import net.md_5.bungee.connection.InitialHandler;
|
||||||
import net.md_5.bungee.connection.LoginResult;
|
import net.md_5.bungee.connection.LoginResult;
|
||||||
|
@ -47,6 +55,8 @@ import net.md_5.bungee.protocol.Property;
|
||||||
*/
|
*/
|
||||||
public class EaglerPacketEventListener implements Listener {
|
public class EaglerPacketEventListener implements Listener {
|
||||||
|
|
||||||
|
public static final String FNAW_SKIN_ENABLE_CHANNEL = "EAG|FNAWSEn-1.8";
|
||||||
|
|
||||||
public final EaglerXBungee plugin;
|
public final EaglerXBungee plugin;
|
||||||
|
|
||||||
public EaglerPacketEventListener(EaglerXBungee plugin) {
|
public EaglerPacketEventListener(EaglerXBungee plugin) {
|
||||||
|
@ -56,28 +66,46 @@ public class EaglerPacketEventListener implements Listener {
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onPluginMessage(final PluginMessageEvent event) {
|
public void onPluginMessage(final PluginMessageEvent event) {
|
||||||
if(event.getSender() instanceof UserConnection) {
|
if(event.getSender() instanceof UserConnection) {
|
||||||
if(SkinService.CHANNEL.equals(event.getTag())) {
|
final UserConnection player = (UserConnection)event.getSender();
|
||||||
event.setCancelled(true);
|
if(player.getPendingConnection() instanceof EaglerInitialHandler) {
|
||||||
final UserConnection sender = (UserConnection)event.getSender();
|
if(SkinService.CHANNEL.equals(event.getTag())) {
|
||||||
if(sender.getPendingConnection() instanceof EaglerInitialHandler) {
|
event.setCancelled(true);
|
||||||
ProxyServer.getInstance().getScheduler().runAsync(plugin, new Runnable() {
|
ProxyServer.getInstance().getScheduler().runAsync(plugin, new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
SkinPackets.processPacket(event.getData(), sender, plugin.getSkinService());
|
SkinPackets.processPacket(event.getData(), player, plugin.getSkinService());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
event.getSender().disconnect(new TextComponent("Skin packet error!"));
|
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 {
|
}else if(CapeServiceOffline.CHANNEL.equals(event.getTag())) {
|
||||||
event.getSender().disconnect(new TextComponent("Cannot send \"" + SkinService.CHANNEL + "\" on a non-eagler connection!"));
|
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) {
|
}else if(event.getSender() instanceof Server && event.getReceiver() instanceof UserConnection) {
|
||||||
UserConnection player = (UserConnection)event.getReceiver();
|
UserConnection player = (UserConnection)event.getReceiver();
|
||||||
if("EAG|GetDomain".equals(event.getTag()) && player.getPendingConnection() instanceof EaglerInitialHandler) {
|
if("EAG|GetDomain".equals(event.getTag()) && player.getPendingConnection() instanceof EaglerInitialHandler) {
|
||||||
|
event.setCancelled(true);
|
||||||
String domain = ((EaglerInitialHandler)player.getPendingConnection()).getOrigin();
|
String domain = ((EaglerInitialHandler)player.getPendingConnection()).getOrigin();
|
||||||
if(domain == null) {
|
if(domain == null) {
|
||||||
((Server)event.getSender()).sendData("EAG|Domain", new byte[] { 0 });
|
((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()) {
|
if(authConf.isEnableAuthentication() && authConf.isUseBuiltInAuthentication()) {
|
||||||
DefaultAuthSystem srv = EaglerXBungee.getEagler().getAuthService();
|
DefaultAuthSystem srv = plugin.getAuthService();
|
||||||
if(srv != null) {
|
if(srv != null) {
|
||||||
srv.handleVanillaLogin(event);
|
srv.handleVanillaLogin(event);
|
||||||
}
|
}
|
||||||
|
@ -131,6 +159,47 @@ public class EaglerPacketEventListener implements Listener {
|
||||||
|
|
||||||
@EventHandler
|
@EventHandler
|
||||||
public void onConnectionLost(PlayerDisconnectEvent event) {
|
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.TIntSet;
|
||||||
import gnu.trove.set.hash.TIntHashSet;
|
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.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.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.SimpleRateLimiter;
|
||||||
import net.md_5.bungee.BungeeCord;
|
import net.md_5.bungee.BungeeCord;
|
||||||
import net.md_5.bungee.api.chat.BaseComponent;
|
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.InitialHandler;
|
||||||
import net.md_5.bungee.connection.LoginResult;
|
import net.md_5.bungee.connection.LoginResult;
|
||||||
import net.md_5.bungee.netty.ChannelWrapper;
|
import net.md_5.bungee.netty.ChannelWrapper;
|
||||||
|
@ -67,12 +68,17 @@ public class EaglerInitialHandler extends InitialHandler {
|
||||||
public final SimpleRateLimiter skinLookupRateLimiter;
|
public final SimpleRateLimiter skinLookupRateLimiter;
|
||||||
public final SimpleRateLimiter skinUUIDLookupRateLimiter;
|
public final SimpleRateLimiter skinUUIDLookupRateLimiter;
|
||||||
public final SimpleRateLimiter skinTextureDownloadRateLimiter;
|
public final SimpleRateLimiter skinTextureDownloadRateLimiter;
|
||||||
|
public final SimpleRateLimiter capeLookupRateLimiter;
|
||||||
|
public final SimpleRateLimiter voiceConnectRateLimiter;
|
||||||
public final String origin;
|
public final String origin;
|
||||||
public final ClientCertificateHolder clientCertificate;
|
public final ClientCertificateHolder clientCertificate;
|
||||||
public final Set<ClientCertificateHolder> certificatesToSend;
|
public final Set<ClientCertificateHolder> certificatesToSend;
|
||||||
public final TIntSet certificatesSent;
|
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,
|
int gameProtocolVersion, String username, UUID playerUUID, InetSocketAddress address, String host,
|
||||||
String origin, ClientCertificateHolder clientCertificate) {
|
String origin, ClientCertificateHolder clientCertificate) {
|
||||||
super(bungee, listener);
|
super(bungee, listener);
|
||||||
|
@ -84,6 +90,8 @@ public class EaglerInitialHandler extends InitialHandler {
|
||||||
this.skinLookupRateLimiter = new SimpleRateLimiter();
|
this.skinLookupRateLimiter = new SimpleRateLimiter();
|
||||||
this.skinUUIDLookupRateLimiter = new SimpleRateLimiter();
|
this.skinUUIDLookupRateLimiter = new SimpleRateLimiter();
|
||||||
this.skinTextureDownloadRateLimiter = new SimpleRateLimiter();
|
this.skinTextureDownloadRateLimiter = new SimpleRateLimiter();
|
||||||
|
this.capeLookupRateLimiter = new SimpleRateLimiter();
|
||||||
|
this.voiceConnectRateLimiter = new SimpleRateLimiter();
|
||||||
this.clientCertificate = clientCertificate;
|
this.clientCertificate = clientCertificate;
|
||||||
this.certificatesToSend = new HashSet();
|
this.certificatesToSend = new HashSet();
|
||||||
this.certificatesSent = new TIntHashSet();
|
this.certificatesSent = new TIntHashSet();
|
||||||
|
@ -108,7 +116,11 @@ public class EaglerInitialHandler extends InitialHandler {
|
||||||
ch.getHandle().writeAndFlush(arg0);
|
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 {
|
try {
|
||||||
super.connected(ch);
|
super.connected(ch);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -251,4 +263,7 @@ public class EaglerInitialHandler extends InitialHandler {
|
||||||
return origin;
|
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.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.MOTDQueryHandler;
|
||||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.query.QueryManager;
|
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.SkinPackets;
|
||||||
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.SkinService;
|
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins.SkinService;
|
||||||
import net.md_5.bungee.BungeeCord;
|
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;
|
ServerInfo server;
|
||||||
if (bungee.getReconnectHandler() != null) {
|
if (bungee.getReconnectHandler() != null) {
|
||||||
|
@ -962,8 +977,25 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter {
|
||||||
server = bungee.getServerInfo(conf.getDefaultServer());
|
server = bungee.getServerInfo(conf.getDefaultServer());
|
||||||
}
|
}
|
||||||
|
|
||||||
eaglerCon.hasBeenForwarded = true;
|
final ServerInfo server2 = server;
|
||||||
userCon.connect(server, null, true, ServerConnectEvent.Reason.JOIN_PROXY);
|
Callback<PostLoginEvent> complete = new Callback<PostLoginEvent>() {
|
||||||
|
@Override
|
||||||
|
public void done(PostLoginEvent result, Throwable error) {
|
||||||
|
eaglerCon.hasBeenForwarded = true;
|
||||||
|
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,
|
":> - BungeeCord: " + EaglerXBungee.NATIVE_BUNGEECORD_BUILD,
|
||||||
":> - Waterfall: " + EaglerXBungee.NATIVE_WATERFALL_BUILD,
|
":> - Waterfall: " + EaglerXBungee.NATIVE_WATERFALL_BUILD,
|
||||||
":> - FlameCord: " + EaglerXBungee.NATIVE_FLAMECORD_BUILD,
|
|
||||||
":> ",
|
":> ",
|
||||||
":> This is not a Bukkit/Spigot plugin!",
|
":> This is not a Bukkit/Spigot plugin!",
|
||||||
":> ",
|
":> ",
|
||||||
|
|
|
@ -108,7 +108,7 @@ public class AsyncSkinProvider {
|
||||||
byte[] loadedPixels = new byte[16384];
|
byte[] loadedPixels = new byte[16384];
|
||||||
image.getRGB(0, 0, 64, 64, tmp, 0, 64);
|
image.getRGB(0, 0, 64, 64, tmp, 0, 64);
|
||||||
SkinRescaler.convertToBytes(tmp, loadedPixels);
|
SkinRescaler.convertToBytes(tmp, loadedPixels);
|
||||||
SkinPackets.setAlphaForChest(loadedPixels, (byte)255);
|
SkinPackets.setAlphaForChest(loadedPixels, (byte)255, 0);
|
||||||
doAccept(loadedPixels);
|
doAccept(loadedPixels);
|
||||||
return;
|
return;
|
||||||
}else if(srcWidth == 64 && srcHeight == 32) {
|
}else if(srcWidth == 64 && srcHeight == 32) {
|
||||||
|
@ -116,7 +116,7 @@ public class AsyncSkinProvider {
|
||||||
byte[] loadedPixels = new byte[16384];
|
byte[] loadedPixels = new byte[16384];
|
||||||
image.getRGB(0, 0, 64, 32, tmp1, 0, 64);
|
image.getRGB(0, 0, 64, 32, tmp1, 0, 64);
|
||||||
SkinRescaler.convert64x32To64x64(tmp1, loadedPixels);
|
SkinRescaler.convert64x32To64x64(tmp1, loadedPixels);
|
||||||
SkinPackets.setAlphaForChest(loadedPixels, (byte)255);
|
SkinPackets.setAlphaForChest(loadedPixels, (byte)255, 0);
|
||||||
doAccept(loadedPixels);
|
doAccept(loadedPixels);
|
||||||
return;
|
return;
|
||||||
}else {
|
}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;
|
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
|
* 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
|
* 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));
|
generatedPacket = SkinPackets.makePresetResponse(clientUUID, (bs[1] << 24) | (bs[2] << 16) | (bs[3] << 8) | (bs[4] & 0xFF));
|
||||||
break;
|
break;
|
||||||
case PACKET_MY_SKIN_CUSTOM:
|
case PACKET_MY_SKIN_CUSTOM:
|
||||||
byte[] pixels = new byte[16384];
|
if(bs.length != 2 + 16384) {
|
||||||
if(bs.length != 2 + pixels.length) {
|
|
||||||
throw new IOException("Invalid length " + bs.length + " for custom skin packet");
|
throw new IOException("Invalid length " + bs.length + " for custom skin packet");
|
||||||
}
|
}
|
||||||
setAlphaForChest(pixels, (byte)255);
|
setAlphaForChest(bs, (byte)255, 2);
|
||||||
System.arraycopy(bs, 2, pixels, 0, pixels.length);
|
generatedPacket = SkinPackets.makeCustomResponse(clientUUID, (skinModel = (int)bs[1] & 0xFF), bs, 2, 16384);
|
||||||
generatedPacket = SkinPackets.makeCustomResponse(clientUUID, (skinModel = (int)bs[1] & 0xFF), pixels);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new IOException("Unknown skin packet type: " + packetType);
|
throw new IOException("Unknown skin packet type: " + packetType);
|
||||||
|
@ -130,13 +128,13 @@ public class SkinPackets {
|
||||||
skinService.registerEaglercraftPlayer(clientUUID, generatedPacket, skinModel);
|
skinService.registerEaglercraftPlayer(clientUUID, generatedPacket, skinModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setAlphaForChest(byte[] skin64x64, byte alpha) {
|
public static void setAlphaForChest(byte[] skin64x64, byte alpha, int offset) {
|
||||||
if(skin64x64.length != 16384) {
|
if(skin64x64.length - offset != 16384) {
|
||||||
throw new IllegalArgumentException("Skin is not 64x64!");
|
throw new IllegalArgumentException("Skin is not 64x64!");
|
||||||
}
|
}
|
||||||
for(int y = 20; y < 32; ++y) {
|
for(int y = 20; y < 32; ++y) {
|
||||||
for(int x = 16; x < 40; ++x) {
|
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) {
|
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;
|
ret[0] = (byte)PACKET_OTHER_SKIN_CUSTOM;
|
||||||
UUIDToBytes(uuid, ret, 1);
|
UUIDToBytes(uuid, ret, 1);
|
||||||
ret[17] = (byte)model;
|
ret[17] = (byte)model;
|
||||||
System.arraycopy(pixels, 0, ret, 18, pixels.length);
|
System.arraycopy(pixels, offset, ret, 18, length);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -52,12 +52,6 @@ public class SkinService implements ISkinService {
|
||||||
|
|
||||||
public static final int masterRateLimitPerPlayer = 250;
|
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";
|
public static final String CHANNEL = "EAG|Skins-1.8";
|
||||||
|
|
||||||
private final Map<UUID, CachedPlayerSkin> onlinePlayersCache = new HashMap();
|
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:
|
page_index_name:
|
||||||
- 'index.html'
|
- 'index.html'
|
||||||
- 'index.htm'
|
- 'index.htm'
|
||||||
|
allow_voice: false
|
||||||
ratelimit:
|
ratelimit:
|
||||||
ip:
|
ip:
|
||||||
enable: true
|
enable: true
|
||||||
|
|
|
@ -19,3 +19,7 @@ skin_cache_antagonists_ratelimit: 15
|
||||||
sql_driver_class: 'internal'
|
sql_driver_class: 'internal'
|
||||||
sql_driver_path: 'internal'
|
sql_driver_path: 'internal'
|
||||||
eagler_players_vanilla_skin: ''
|
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
|
name: EaglercraftXBungee
|
||||||
main: net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.EaglerXBungee
|
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
|
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
|
author: lax1dude
|
|
@ -1 +1 @@
|
||||||
1.0.10
|
1.1.0
|
Loading…
Reference in New Issue
Block a user