diff --git a/gateway/EaglercraftXVelocity/EaglerXVelocity-Latest.jar b/gateway/EaglercraftXVelocity/EaglerXVelocity-Latest.jar index c2f3cb0..03b6f74 100644 Binary files a/gateway/EaglercraftXVelocity/EaglerXVelocity-Latest.jar and b/gateway/EaglercraftXVelocity/EaglerXVelocity-Latest.jar differ diff --git a/gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/EaglerXVelocityVersion.java b/gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/EaglerXVelocityVersion.java index 83d4d03..f3f1f5b 100644 --- a/gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/EaglerXVelocityVersion.java +++ b/gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/EaglerXVelocityVersion.java @@ -17,13 +17,14 @@ package net.lax1dude.eaglercraft.v1_8.plugin.gateway_velocity; */ public class EaglerXVelocityVersion { - public static final String NATIVE_VELOCITY_BUILD = "3.4.0-SNAPSHOT:a33f2d1a:b452"; + public static final String NATIVE_VELOCITY_BUILD = "3.4.0-SNAPSHOT:71feb11b:b461"; + public static final String NATIVE_VELOCITY_BUILD_DL = "https://api.papermc.io/v2/projects/velocity/versions/3.4.0-SNAPSHOT/builds/461/downloads/velocity-3.4.0-SNAPSHOT-461.jar"; public static final String ID = "EaglerXVelocity"; public static final String PLUGIN_ID = "eaglerxvelocity"; public static final String NAME = "EaglercraftXVelocity"; public static final String 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"; - public static final String VERSION = "1.1.5"; + public static final String VERSION = "1.1.6"; public static final String[] AUTHORS = new String[] { "lax1dude", "ayunami2000" }; } diff --git a/gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/auth/AuthLoadingCache.java b/gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/auth/AuthLoadingCache.java index 2c74e8b..7b98709 100644 --- a/gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/auth/AuthLoadingCache.java +++ b/gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/auth/AuthLoadingCache.java @@ -4,6 +4,8 @@ import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Map.Entry; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; import net.lax1dude.eaglercraft.v1_8.plugin.gateway_velocity.api.EaglerXVelocityAPIHelper; @@ -44,6 +46,7 @@ public class AuthLoadingCache { boolean shouldEvict(K key, V value); } + private final ReadWriteLock cacheMapLock; private final Map> cacheMap; private final CacheLoader provider; private final long cacheTTL; @@ -51,6 +54,7 @@ public class AuthLoadingCache { private long cacheTimer; public AuthLoadingCache(CacheLoader provider, long cacheTTL) { + this.cacheMapLock = new ReentrantReadWriteLock(); this.cacheMap = new HashMap<>(); this.provider = provider; this.cacheTTL = cacheTTL; @@ -58,13 +62,19 @@ public class AuthLoadingCache { public V get(K key) { CacheEntry etr; - synchronized(cacheMap) { + cacheMapLock.readLock().lock(); + try { etr = cacheMap.get(key); + }finally { + cacheMapLock.readLock().unlock(); } if(etr == null) { + cacheMapLock.writeLock().lock(); V loaded = provider.load(key); - synchronized(cacheMap) { + try { cacheMap.put(key, new CacheEntry<>(loaded)); + }finally { + cacheMapLock.writeLock().unlock(); } return loaded; }else { @@ -74,13 +84,17 @@ public class AuthLoadingCache { } public void evict(K key) { - synchronized(cacheMap) { + cacheMapLock.writeLock().lock(); + try { cacheMap.remove(key); + }finally { + cacheMapLock.writeLock().unlock(); } } public void evictAll(CacheVisitor visitor) { - synchronized(cacheMap) { + cacheMapLock.writeLock().lock(); + try { Iterator>> itr = cacheMap.entrySet().iterator(); while(itr.hasNext()) { Entry> etr = itr.next(); @@ -88,6 +102,8 @@ public class AuthLoadingCache { itr.remove(); } } + }finally { + cacheMapLock.writeLock().unlock(); } } @@ -95,7 +111,8 @@ public class AuthLoadingCache { long millis = EaglerXVelocityAPIHelper.steadyTimeMillis(); if(millis - cacheTimer > (cacheTTL / 2L)) { cacheTimer = millis; - synchronized(cacheMap) { + cacheMapLock.writeLock().lock(); + try { Iterator> mapItr = cacheMap.values().iterator(); while(mapItr.hasNext()) { CacheEntry etr = mapItr.next(); @@ -103,13 +120,18 @@ public class AuthLoadingCache { mapItr.remove(); } } + }finally { + cacheMapLock.writeLock().unlock(); } } } public void flush() { - synchronized(cacheMap) { + cacheMapLock.writeLock().lock(); + try { cacheMap.clear(); + }finally { + cacheMapLock.writeLock().unlock(); } } diff --git a/gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/server/HttpWebSocketHandler.java b/gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/server/HttpWebSocketHandler.java index b40324b..38c1d0b 100644 --- a/gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/server/HttpWebSocketHandler.java +++ b/gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/server/HttpWebSocketHandler.java @@ -118,6 +118,7 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter { private static final Method loginEventFiredMethod; private static final Constructor stupid3Constructor; private static final Constructor stupid3Constructor_new; + private static final Constructor stupid3Constructor_new_new; private static final Method setPermissionFunctionMethod; private static final Field defaultPermissionsField; private static final Constructor stupid4Constructor; @@ -137,16 +138,24 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter { loginEventFiredMethod.setAccessible(true); Constructor c3 = null; Constructor c3_new = null; + Constructor c3_new_new = null; try { - c3_new = ConnectedPlayer.class.getDeclaredConstructor(VelocityServer.class, GameProfile.class, MinecraftConnection.class, InetSocketAddress.class, String.class, boolean.class, IdentifiedKey.class); - c3_new.setAccessible(true); + c3_new_new = ConnectedPlayer.class.getDeclaredConstructor(VelocityServer.class, GameProfile.class, MinecraftConnection.class, InetSocketAddress.class, String.class, boolean.class, HandshakeIntent.class, IdentifiedKey.class); + c3_new_new.setAccessible(true); } catch (NoSuchMethodException e) { - c3 = ConnectedPlayer.class.getDeclaredConstructor(VelocityServer.class, GameProfile.class, MinecraftConnection.class, InetSocketAddress.class, boolean.class, IdentifiedKey.class); - c3.setAccessible(true); - c3_new = null; + try { + c3_new_new = null; + c3_new = ConnectedPlayer.class.getDeclaredConstructor(VelocityServer.class, GameProfile.class, MinecraftConnection.class, InetSocketAddress.class, String.class, boolean.class, IdentifiedKey.class); + c3_new.setAccessible(true); + } catch (NoSuchMethodException ee) { + c3_new = null; + c3 = ConnectedPlayer.class.getDeclaredConstructor(VelocityServer.class, GameProfile.class, MinecraftConnection.class, InetSocketAddress.class, boolean.class, IdentifiedKey.class); + c3.setAccessible(true); + } } stupid3Constructor = c3; stupid3Constructor_new = c3_new; + stupid3Constructor_new_new = c3_new_new; setPermissionFunctionMethod = ConnectedPlayer.class.getDeclaredMethod("setPermissionFunction", PermissionFunction.class); setPermissionFunctionMethod.setAccessible(true); defaultPermissionsField = ConnectedPlayer.class.getDeclaredField("DEFAULT_PERMISSIONS"); @@ -1080,7 +1089,13 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter { } ConnectedPlayer player; - if(stupid3Constructor_new != null) { + if(stupid3Constructor_new_new != null) { + try { + player = stupid3Constructor_new_new.newInstance(bungee, gp, con, lic.getVirtualHost().orElse(null), lic.getRawVirtualHost().orElse(null), false, HandshakeIntent.LOGIN, lic.getIdentifiedKey()); + } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + }else if(stupid3Constructor_new != null) { try { player = stupid3Constructor_new.newInstance(bungee, gp, con, lic.getVirtualHost().orElse(null), lic.getRawVirtualHost().orElse(null), false, lic.getIdentifiedKey()); } catch (InstantiationException | IllegalAccessException | InvocationTargetException e) { @@ -1309,14 +1324,11 @@ public class HttpWebSocketHandler extends ChannelInboundHandlerAdapter { pp.remove(HttpWebSocketHandler.this); - pp - .addLast("EaglerMinecraftByteBufDecoder", new EaglerMinecraftDecoder()) - .addLast(LEGACY_PING_DECODER, new LegacyPingDecoder()) + pp.addLast("EaglerMinecraftByteBufDecoder", new EaglerMinecraftDecoder()) .addLast(READ_TIMEOUT, new ReadTimeoutHandler(bungee.getConfiguration().getReadTimeout(), TimeUnit.MILLISECONDS)) .addLast("EaglerMinecraftByteBufEncoder", new EaglerMinecraftEncoder()) - .addLast(LEGACY_PING_ENCODER, LegacyPingEncoder.INSTANCE) .addLast(MINECRAFT_DECODER, new MinecraftDecoder(ProtocolUtils.Direction.SERVERBOUND)) .addLast(MINECRAFT_ENCODER, new MinecraftEncoder(ProtocolUtils.Direction.CLIENTBOUND)); diff --git a/gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/server/protocol/GameProtocolMessageController.java b/gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/server/protocol/GameProtocolMessageController.java index 96caab5..f027d19 100644 --- a/gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/server/protocol/GameProtocolMessageController.java +++ b/gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/server/protocol/GameProtocolMessageController.java @@ -1,6 +1,7 @@ package net.lax1dude.eaglercraft.v1_8.plugin.gateway_velocity.server.protocol; import java.io.IOException; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.concurrent.Callable; @@ -241,10 +242,12 @@ public class GameProtocolMessageController { while(!sendQueueV4.isEmpty()) { sendCount = 0; totalLen = 0; + Iterator itr = sendQueueV4.iterator(); do { - i = sendQueueV4.get(sendCount++).length; + i = itr.next().length; totalLen += GamePacketOutputBuffer.getVarIntSize(i) + i; - }while(totalLen < 32760 && sendCount < sendQueueV4.size()); + ++sendCount; + }while(totalLen < 32760 && itr.hasNext()); if(totalLen >= 32760) { --sendCount; } diff --git a/gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/server/web/HttpWebServer.java b/gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/server/web/HttpWebServer.java index 356937e..1edc972 100644 --- a/gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/server/web/HttpWebServer.java +++ b/gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/server/web/HttpWebServer.java @@ -41,7 +41,6 @@ public class HttpWebServer { private final String page404; private static HttpMemoryCache default404Page; private static HttpMemoryCache default404UpgradePage; - private static final Object cacheClearLock = new Object(); public HttpWebServer(File directory, Map contentTypes, List index, String page404) { this.directory = directory; @@ -53,15 +52,13 @@ public class HttpWebServer { public void flushCache() { long millis = EaglerXVelocityAPIHelper.steadyTimeMillis(); - synchronized(cacheClearLock) { - synchronized(filesCache) { - Iterator itr = filesCache.values().iterator(); - while(itr.hasNext()) { - HttpMemoryCache i = itr.next(); - if(i.contentType.fileBrowserCacheTTL != Long.MAX_VALUE && millis - i.lastCacheHit > 900000l) { - i.fileData.release(); - itr.remove(); - } + synchronized(filesCache) { + Iterator itr = filesCache.values().iterator(); + while(itr.hasNext()) { + HttpMemoryCache i = itr.next(); + if(i.contentType.fileBrowserCacheTTL != Long.MAX_VALUE && millis - i.lastCacheHit > 900000l) { + i.fileData.release(); + itr.remove(); } } } @@ -94,20 +91,17 @@ public class HttpWebServer { } String joinedPath = String.join("/", pathList); - - synchronized(cacheClearLock) { - synchronized(filesCache) { - cached = filesCache.get(joinedPath); - } + + //TODO: Rewrite this to cause less lock contention + synchronized(filesCache) { + cached = filesCache.get(joinedPath); if(cached != null) { cached = validateCache(cached); if(cached != null) { return cached; }else { - synchronized(filesCache) { - filesCache.remove(joinedPath); - } + filesCache.remove(joinedPath); } } @@ -124,19 +118,13 @@ public class HttpWebServer { if(f.isDirectory()) { for(int i = 0, l = index.size(); i < l; ++i) { String p = joinedPath + "/" + index.get(i); - synchronized(filesCache) { - cached = filesCache.get(p); - } + cached = filesCache.get(p); if(cached != null) { cached = validateCache(cached); if(cached != null) { - synchronized(filesCache) { - filesCache.put(joinedPath, cached); - } + filesCache.put(joinedPath, cached); }else { - synchronized(filesCache) { - filesCache.remove(p); - } + filesCache.remove(p); if(page404 == null || path.equals(page404)) { return default404Page; }else { @@ -152,9 +140,7 @@ public class HttpWebServer { if(ff.isFile()) { HttpMemoryCache memCache = retrieveFile(ff, p); if(memCache != null) { - synchronized(filesCache) { - filesCache.put(joinedPath, memCache); - } + filesCache.put(joinedPath, memCache); return memCache; } } @@ -167,9 +153,7 @@ public class HttpWebServer { }else { HttpMemoryCache memCache = retrieveFile(f, joinedPath); if(memCache != null) { - synchronized(filesCache) { - filesCache.put(joinedPath, memCache); - } + filesCache.put(joinedPath, memCache); return memCache; }else { if(page404 == null || path.equals(page404)) { diff --git a/gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/shit/CompatWarning.java b/gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/shit/CompatWarning.java index e27b828..54b8102 100644 --- a/gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/shit/CompatWarning.java +++ b/gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/shit/CompatWarning.java @@ -34,6 +34,7 @@ public class CompatWarning { ":> apart from the versions listed below:", ":> ", ":> - Velocity: " + EaglerXVelocityVersion.NATIVE_VELOCITY_BUILD, + ":> - " + EaglerXVelocityVersion.NATIVE_VELOCITY_BUILD_DL, ":> ", ":> This is not a Bukkit/Spigot plugin!", ":> ", diff --git a/gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/skins/CapeServiceOffline.java b/gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/skins/CapeServiceOffline.java index 6044c7d..0522bf5 100644 --- a/gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/skins/CapeServiceOffline.java +++ b/gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/skins/CapeServiceOffline.java @@ -1,8 +1,8 @@ package net.lax1dude.eaglercraft.v1_8.plugin.gateway_velocity.skins; -import java.util.HashMap; -import java.util.Map; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import net.lax1dude.eaglercraft.v1_8.plugin.gateway_velocity.server.EaglerPlayerData; import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket; @@ -12,7 +12,7 @@ import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherCape import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherCapePresetEAG; /** - * Copyright (c) 2024 lax1dude. All Rights Reserved. + * Copyright (c) 2024-2025 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 @@ -30,20 +30,15 @@ public class CapeServiceOffline { public static final int masterRateLimitPerPlayer = 250; - private final Map capesCache = new HashMap<>(); + private final ConcurrentMap capesCache = new ConcurrentHashMap<>(); public void registerEaglercraftPlayer(UUID playerUUID, GameMessagePacket capePacket) { - synchronized(capesCache) { - capesCache.put(playerUUID, capePacket); - } + capesCache.put(playerUUID, capePacket); } public void processGetOtherCape(UUID searchUUID, EaglerPlayerData sender) { if(sender.skinLookupRateLimiter.rateLimit(masterRateLimitPerPlayer)) { - GameMessagePacket maybeCape; - synchronized(capesCache) { - maybeCape = capesCache.get(searchUUID); - } + GameMessagePacket maybeCape = capesCache.get(searchUUID); if(maybeCape != null) { sender.sendEaglerMessage(maybeCape); }else { @@ -54,10 +49,7 @@ public class CapeServiceOffline { } public void processForceCape(UUID clientUUID, EaglerPlayerData initialHandler) { - GameMessagePacket maybeCape; - synchronized(capesCache) { - maybeCape = capesCache.get(clientUUID); - } + GameMessagePacket maybeCape = capesCache.get(clientUUID); if(maybeCape != null) { if (maybeCape instanceof SPacketOtherCapePresetEAG) { initialHandler.sendEaglerMessage( @@ -70,15 +62,11 @@ public class CapeServiceOffline { } public void unregisterPlayer(UUID playerUUID) { - synchronized(capesCache) { - capesCache.remove(playerUUID); - } + capesCache.remove(playerUUID); } public GameMessagePacket getCape(UUID clientUUID) { - synchronized(capesCache) { - return capesCache.get(clientUUID); - } + return capesCache.get(clientUUID); } public byte[] getCapeHandshakeData(UUID clientUUID) { @@ -107,8 +95,6 @@ public class CapeServiceOffline { } public void shutdown() { - synchronized(capesCache) { - capesCache.clear(); - } + capesCache.clear(); } } diff --git a/gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/skins/SkinService.java b/gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/skins/SkinService.java index b8bed99..615d790 100644 --- a/gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/skins/SkinService.java +++ b/gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/skins/SkinService.java @@ -1,6 +1,7 @@ package net.lax1dude.eaglercraft.v1_8.plugin.gateway_velocity.skins; import java.net.URI; +import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; @@ -10,6 +11,10 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.function.Consumer; import com.google.common.collect.Multimap; @@ -36,7 +41,7 @@ import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherSkin import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.SkinPacketVersionCache; /** - * Copyright (c) 2022-2024 lax1dude. All Rights Reserved. + * Copyright (c) 2022-2025 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 @@ -54,16 +59,20 @@ public class SkinService implements ISkinService { public static final int masterRateLimitPerPlayer = 250; - private final Map onlinePlayersCache = new HashMap<>(); + private final ConcurrentMap onlinePlayersCache = new ConcurrentHashMap<>(); + private final ConcurrentMap onlinePlayersToTexturesMap = new ConcurrentHashMap<>(); + private final ConcurrentMap foreignSkinCache = new ConcurrentHashMap<>(); + + private final ReadWriteLock onlinePlayersFromTexturesMapLock = new ReentrantReadWriteLock(); private final Multimap onlinePlayersFromTexturesMap = MultimapBuilder.hashKeys().hashSetValues().build(); - private final Map onlinePlayersToTexturesMap = new HashMap<>(); - private final Map foreignSkinCache = new HashMap<>(); private final Map pendingTextures = new HashMap<>(); private final Map pendingUUIDs = new HashMap<>(); private final Map pendingNameLookups = new HashMap<>(); + private final ReadWriteLock antagonistsLock = new ReentrantReadWriteLock(); private final Object2IntMap antagonists = new Object2IntOpenHashMap<>(); + private long antagonistCooldown = EaglerXVelocityAPIHelper.steadyTimeMillis(); private final Consumer> antagonistLogger = new Consumer>() { @@ -73,7 +82,8 @@ public class SkinService implements ISkinService { if(t.size() == 1) { int limit = EaglerXVelocity.getEagler().getConfig().getAntagonistsRateLimit() << 1; UUID offender = t.iterator().next(); - synchronized(antagonists) { + antagonistsLock.writeLock().lock(); + try { int v = antagonists.getInt(offender); if(v == antagonists.defaultReturnValue()) { antagonists.put(offender, 1); @@ -82,6 +92,8 @@ public class SkinService implements ISkinService { antagonists.put(offender, v + 1); } } + }finally { + antagonistsLock.writeLock().unlock(); } } } @@ -95,7 +107,7 @@ public class SkinService implements ISkinService { protected final UUID uuid; protected final SkinPacketVersionCache data; protected final int modelKnown; - protected long lastHit; + protected volatile long lastHit; protected CachedForeignSkin(UUID uuid, SkinPacketVersionCache data, int modelKnown) { this.uuid = uuid; @@ -130,7 +142,7 @@ public class SkinService implements ISkinService { protected final Consumer> antagonistsCallback; protected final long initializedTime; - protected boolean finalized; + protected volatile boolean finalized; protected PendingTextureDownload(UUID textureUUID, String textureURL, UUID caller, Consumer callback, Consumer> antagonistsCallback) { @@ -172,7 +184,7 @@ public class SkinService implements ISkinService { protected final Consumer> antagonistsCallback; protected final long initializedTime; - protected boolean finalized; + protected volatile boolean finalized; protected PendingProfileUUIDLookup(UUID profileUUID, UUID caller, Consumer callback, Consumer> antagonistsCallback) { @@ -213,7 +225,7 @@ public class SkinService implements ISkinService { protected final Consumer> antagonistsCallback; protected final long initializedTime; - protected boolean finalized; + protected volatile boolean finalized; protected PendingProfileNameLookup(String profileName, UUID caller, Consumer callback, Consumer> antagonistsCallback) { @@ -260,60 +272,46 @@ public class SkinService implements ISkinService { return; } - CachedPlayerSkin maybeCachedPacket; - synchronized(onlinePlayersCache) { - maybeCachedPacket = onlinePlayersCache.get(searchUUID); - } + CachedPlayerSkin maybeCachedPacket = onlinePlayersCache.get(searchUUID); if(maybeCachedPacket != null) { sender.sendEaglerMessage(maybeCachedPacket.data.get(sender.getEaglerProtocol())); }else { Player player = EaglerXVelocity.proxy().getPlayer(searchUUID).orElse(null); - UUID playerTexture; - synchronized(onlinePlayersToTexturesMap) { - playerTexture = onlinePlayersToTexturesMap.get(searchUUID); - } + UUID playerTexture = onlinePlayersToTexturesMap.get(searchUUID); if(playerTexture != null) { Collection possiblePlayers; - synchronized(onlinePlayersFromTexturesMap) { - possiblePlayers = onlinePlayersFromTexturesMap.get(playerTexture); + onlinePlayersFromTexturesMapLock.readLock().lock(); + try { + possiblePlayers = new ArrayList<>(onlinePlayersFromTexturesMap.get(playerTexture)); + }finally { + onlinePlayersFromTexturesMapLock.readLock().unlock(); } boolean playersExist = possiblePlayers.size() > 0; if(playersExist) { for(UUID uuid : possiblePlayers) { - synchronized(onlinePlayersCache) { - maybeCachedPacket = onlinePlayersCache.get(uuid); - } + maybeCachedPacket = onlinePlayersCache.get(uuid); if(maybeCachedPacket != null) { SkinPacketVersionCache rewritten = SkinPacketVersionCache.rewriteUUID( maybeCachedPacket.data, searchUUID.getMostSignificantBits(), searchUUID.getLeastSignificantBits()); if(player != null) { - synchronized(onlinePlayersCache) { - onlinePlayersCache.put(searchUUID, new CachedPlayerSkin(rewritten, - maybeCachedPacket.textureUUID, maybeCachedPacket.modelId)); - } + onlinePlayersCache.put(searchUUID, new CachedPlayerSkin(rewritten, + maybeCachedPacket.textureUUID, maybeCachedPacket.modelId)); } sender.sendEaglerMessage(rewritten.get(sender.getEaglerProtocol())); return; } } } - CachedForeignSkin foreignSkin; - synchronized(foreignSkinCache) { - foreignSkin = foreignSkinCache.get(playerTexture); - } + CachedForeignSkin foreignSkin = foreignSkinCache.get(playerTexture); if(foreignSkin != null && foreignSkin.modelKnown != -1) { if(player != null) { - synchronized(onlinePlayersCache) { - onlinePlayersCache.put(searchUUID, - new CachedPlayerSkin(SkinPacketVersionCache.rewriteUUID(foreignSkin.data, - searchUUID.getMostSignificantBits(), searchUUID.getLeastSignificantBits()), - playerTexture, foreignSkin.modelKnown)); - } - synchronized(foreignSkinCache) { - foreignSkinCache.remove(playerTexture); - } + onlinePlayersCache.put(searchUUID, + new CachedPlayerSkin(SkinPacketVersionCache.rewriteUUID(foreignSkin.data, + searchUUID.getMostSignificantBits(), searchUUID.getLeastSignificantBits()), + playerTexture, foreignSkin.modelKnown)); + foreignSkinCache.remove(playerTexture); }else { foreignSkin.lastHit = EaglerXVelocityAPIHelper.steadyTimeMillis(); } @@ -336,7 +334,7 @@ public class SkinService implements ISkinService { if(skinObj != null) { JsonElement url = json.get("url"); if(url != null) { - String urlStr = SkinService.sanitizeTextureURL(url.getAsString()); + String urlStr = sanitizeTextureURL(url.getAsString()); if(urlStr == null) { break; } @@ -350,19 +348,14 @@ public class SkinService implements ISkinService { } UUID skinUUID = SkinPackets.createEaglerURLSkinUUID(urlStr); - CachedForeignSkin foreignSkin; - synchronized(foreignSkinCache) { - foreignSkin = foreignSkinCache.remove(skinUUID); - } + CachedForeignSkin foreignSkin = foreignSkinCache.remove(skinUUID); if(foreignSkin != null) { registerTextureToPlayerAssociation(skinUUID, searchUUID); SkinPacketVersionCache rewrite = SkinPacketVersionCache .rewriteUUIDModel(foreignSkin.data, searchUUID.getMostSignificantBits(), searchUUID.getLeastSignificantBits(), model); - synchronized(onlinePlayersCache) { - onlinePlayersCache.put(searchUUID, new CachedPlayerSkin(rewrite, skinUUID, model)); - } + onlinePlayersCache.put(searchUUID, new CachedPlayerSkin(rewrite, skinUUID, model)); sender.sendEaglerMessage(rewrite.get(sender.getEaglerProtocol())); return; } @@ -395,10 +388,7 @@ public class SkinService implements ISkinService { }); } }else { - CachedForeignSkin foreignSkin; - synchronized(foreignSkinCache) { - foreignSkin = foreignSkinCache.get(searchUUID); - } + CachedForeignSkin foreignSkin = foreignSkinCache.get(searchUUID); if(foreignSkin != null) { foreignSkin.lastHit = EaglerXVelocityAPIHelper.steadyTimeMillis(); sender.sendEaglerMessage(foreignSkin.data.get(sender.getEaglerProtocol())); @@ -426,25 +416,22 @@ public class SkinService implements ISkinService { if(!sender.skinLookupRateLimiter.rateLimit(masterRateLimitPerPlayer)) { return; } - CachedForeignSkin foreignSkin; - synchronized(foreignSkinCache) { - foreignSkin = foreignSkinCache.get(searchUUID); - } + CachedForeignSkin foreignSkin = foreignSkinCache.get(searchUUID); if(foreignSkin != null) { foreignSkin.lastHit = EaglerXVelocityAPIHelper.steadyTimeMillis(); sender.sendEaglerMessage(foreignSkin.data.get(sender.getEaglerProtocol())); }else { Collection possiblePlayers; - synchronized(onlinePlayersFromTexturesMap) { - possiblePlayers = onlinePlayersFromTexturesMap.get(searchUUID); + onlinePlayersFromTexturesMapLock.readLock().lock(); + try { + possiblePlayers = new ArrayList<>(onlinePlayersFromTexturesMap.get(searchUUID)); + }finally { + onlinePlayersFromTexturesMapLock.readLock().unlock(); } boolean playersExist = possiblePlayers.size() > 0; if(playersExist) { for(UUID uuid : possiblePlayers) { - CachedPlayerSkin maybeCachedPacket; - synchronized(onlinePlayersCache) { - maybeCachedPacket = onlinePlayersCache.get(uuid); - } + CachedPlayerSkin maybeCachedPacket = onlinePlayersCache.get(uuid); if(maybeCachedPacket != null) { sender.sendEaglerMessage(maybeCachedPacket.data.get(sender.getEaglerProtocol(), searchUUID.getMostSignificantBits(), searchUUID.getLeastSignificantBits())); @@ -457,10 +444,17 @@ public class SkinService implements ISkinService { searchUUID.getLeastSignificantBits(), 0)); return; } - if(sender.skinTextureDownloadRateLimiter.rateLimit(config.getSkinRateLimitPlayer()) && !isLimitedAsAntagonist(sender.getUniqueId())) { - doAsync(() -> { - processResolveURLTextureForForeign(sender, searchUUID, searchUUID, skinURL, -1); - }); + skinURL = sanitizeTextureURL(skinURL); + if(skinURL != null) { + final String skinURL_ = skinURL; + if(sender.skinTextureDownloadRateLimiter.rateLimit(config.getSkinRateLimitPlayer()) && !isLimitedAsAntagonist(sender.getUniqueId())) { + doAsync(() -> { + processResolveURLTextureForForeign(sender, searchUUID, searchUUID, skinURL_, -1); + }); + } + }else { + sender.sendEaglerMessage(new SPacketOtherSkinPresetEAG(searchUUID.getMostSignificantBits(), + searchUUID.getLeastSignificantBits(), 0)); } } } @@ -475,10 +469,7 @@ public class SkinService implements ISkinService { @Override public void accept(byte[] t) { - CachedPlayerSkin skin; - synchronized(onlinePlayersCache) { - skin = onlinePlayersCache.get(onlineCacheUUID); - } + CachedPlayerSkin skin = onlinePlayersCache.get(onlineCacheUUID); if(skin != null) { initiator.sendEaglerMessage(skin.data.get(initiator.getEaglerProtocol())); } @@ -505,9 +496,7 @@ public class SkinService implements ISkinService { onlineCacheUUID.getMostSignificantBits(), onlineCacheUUID.getLeastSignificantBits()), null, -1); } - synchronized (onlinePlayersCache) { - onlinePlayersCache.put(onlineCacheUUID, skin); - } + onlinePlayersCache.put(onlineCacheUUID, skin); initiator.sendEaglerMessage(skin.data.get(initiator.getEaglerProtocol())); } @@ -532,10 +521,7 @@ public class SkinService implements ISkinService { @Override public void accept(byte[] t) { - CachedForeignSkin skin; - synchronized(foreignSkinCache) { - skin = foreignSkinCache.get(foreignCacheUUID); - } + CachedForeignSkin skin = foreignSkinCache.get(foreignCacheUUID); if(skin != null) { initiator.sendEaglerMessage(skin.data.get(initiator.getEaglerProtocol())); } @@ -563,9 +549,7 @@ public class SkinService implements ISkinService { foreignCacheUUID.getLeastSignificantBits()), -1); } - synchronized (foreignSkinCache) { - foreignSkinCache.put(foreignCacheUUID, skin); - } + foreignSkinCache.put(foreignCacheUUID, skin); initiator.sendEaglerMessage(skin.data.get(initiator.getEaglerProtocol())); } @@ -590,10 +574,7 @@ public class SkinService implements ISkinService { @Override public void accept(CacheFetchedProfile t) { if(t == null || t.texture == null) { - CachedPlayerSkin skin; - synchronized(onlinePlayersCache) { - skin = onlinePlayersCache.get(playerUUID); - } + CachedPlayerSkin skin = onlinePlayersCache.get(playerUUID); if(skin != null) { initiator.sendEaglerMessage(skin.data.get(initiator.getEaglerProtocol())); } @@ -625,9 +606,7 @@ public class SkinService implements ISkinService { SkinPackets.getModelId(t.model) == 1 ? 1 : 0), null, -1); } - synchronized(onlinePlayersCache) { - onlinePlayersCache.put(playerUUID, skin); - } + onlinePlayersCache.put(playerUUID, skin); initiator.sendEaglerMessage(skin.data.get(initiator.getEaglerProtocol())); }else { processResolveURLTextureForOnline(initiator, playerUUID, t.textureUUID, t.texture, @@ -656,10 +635,7 @@ public class SkinService implements ISkinService { @Override public void accept(CacheFetchedProfile t) { if(t == null || t.texture == null) { - CachedPlayerSkin skin; - synchronized(onlinePlayersCache) { - skin = onlinePlayersCache.get(t.uuid); - } + CachedPlayerSkin skin = onlinePlayersCache.get(t.uuid); if(skin != null) { initiator.sendEaglerMessage(skin.data.get(initiator.getEaglerProtocol())); } @@ -689,9 +665,7 @@ public class SkinService implements ISkinService { mapUUID.getMostSignificantBits(), mapUUID.getLeastSignificantBits(), SkinPackets.getModelId(t.model) == 1 ? 1 : 0), null, -1); } - synchronized(onlinePlayersCache) { - onlinePlayersCache.put(mapUUID, skin); - } + onlinePlayersCache.put(mapUUID, skin); initiator.sendEaglerMessage(skin.data.get(initiator.getEaglerProtocol())); }else { processResolveURLTextureForOnline(initiator, mapUUID, t.textureUUID, t.texture, @@ -720,10 +694,7 @@ public class SkinService implements ISkinService { @Override public void accept(CacheFetchedProfile t) { if(t == null || t.texture == null) { - CachedForeignSkin skin; - synchronized(foreignSkinCache) { - skin = foreignSkinCache.get(playerUUID); - } + CachedForeignSkin skin = foreignSkinCache.get(playerUUID); if(skin != null) { initiator.sendEaglerMessage(skin.data.get(initiator.getEaglerProtocol())); } @@ -755,9 +726,7 @@ public class SkinService implements ISkinService { SkinPackets.getModelId(t.model) == 1 ? 1 : 0), -1); } - synchronized(foreignSkinCache) { - foreignSkinCache.put(playerUUID, skin); - } + foreignSkinCache.put(playerUUID, skin); initiator.sendEaglerMessage(skin.data.get(initiator.getEaglerProtocol())); }else { processResolveURLTextureForForeign(initiator, playerUUID, t.textureUUID, t.texture, @@ -777,27 +746,16 @@ public class SkinService implements ISkinService { } public void registerEaglercraftPlayer(UUID clientUUID, SkinPacketVersionCache generatedPacket, int modelId) { - synchronized(foreignSkinCache) { - foreignSkinCache.remove(clientUUID); - } - synchronized(onlinePlayersCache) { - onlinePlayersCache.put(clientUUID, new CachedPlayerSkin(generatedPacket, null, modelId)); - } + foreignSkinCache.remove(clientUUID); + onlinePlayersCache.put(clientUUID, new CachedPlayerSkin(generatedPacket, null, modelId)); } public void unregisterPlayer(UUID clientUUID) { - CachedPlayerSkin data; - synchronized(onlinePlayersCache) { - data = onlinePlayersCache.remove(clientUUID); - } + CachedPlayerSkin data = onlinePlayersCache.remove(clientUUID); if(data != null) { - synchronized(foreignSkinCache) { - foreignSkinCache.put(clientUUID, new CachedForeignSkin(clientUUID, data.data, data.modelId)); - } + foreignSkinCache.put(clientUUID, new CachedForeignSkin(clientUUID, data.data, data.modelId)); if(data.textureUUID != null) { - synchronized(foreignSkinCache) { - foreignSkinCache.put(data.textureUUID, new CachedForeignSkin(data.textureUUID, data.data, data.modelId)); - } + foreignSkinCache.put(data.textureUUID, new CachedForeignSkin(data.textureUUID, data.data, data.modelId)); } deletePlayerTextureAssociation(clientUUID, data.textureUUID); }else { @@ -807,94 +765,77 @@ public class SkinService implements ISkinService { private void deletePlayerTextureAssociation(UUID clientUUID, UUID textureUUID) { if(textureUUID != null) { - synchronized(onlinePlayersToTexturesMap) { - onlinePlayersToTexturesMap.remove(clientUUID); - } - synchronized(onlinePlayersFromTexturesMap) { + onlinePlayersToTexturesMap.remove(clientUUID); + onlinePlayersFromTexturesMapLock.writeLock().lock(); + try { onlinePlayersFromTexturesMap.remove(textureUUID, clientUUID); + }finally { + onlinePlayersFromTexturesMapLock.writeLock().unlock(); } }else { - UUID removedUUID; - synchronized(onlinePlayersToTexturesMap) { - removedUUID = onlinePlayersToTexturesMap.remove(clientUUID); - } + UUID removedUUID = onlinePlayersToTexturesMap.remove(clientUUID); if(removedUUID != null) { - synchronized(onlinePlayersFromTexturesMap) { + onlinePlayersFromTexturesMapLock.writeLock().lock(); + try { onlinePlayersFromTexturesMap.remove(removedUUID, clientUUID); + }finally { + onlinePlayersFromTexturesMapLock.writeLock().unlock(); } } } } public void registerTextureToPlayerAssociation(UUID textureUUID, UUID playerUUID) { - synchronized(onlinePlayersFromTexturesMap) { + onlinePlayersFromTexturesMapLock.writeLock().lock(); + try { onlinePlayersFromTexturesMap.put(textureUUID, playerUUID); + }finally { + onlinePlayersFromTexturesMapLock.writeLock().unlock(); } - synchronized(onlinePlayersToTexturesMap) { - onlinePlayersToTexturesMap.put(playerUUID, textureUUID); - } - CachedForeignSkin foreign; - synchronized(foreignSkinCache) { - foreign = foreignSkinCache.remove(textureUUID); - } + onlinePlayersToTexturesMap.put(playerUUID, textureUUID); + CachedForeignSkin foreign = foreignSkinCache.remove(textureUUID); if(foreign != null) { - synchronized(onlinePlayersCache) { - onlinePlayersCache.put(playerUUID, new CachedPlayerSkin(foreign.data, textureUUID, foreign.modelKnown)); - } + onlinePlayersCache.put(playerUUID, new CachedPlayerSkin(foreign.data, textureUUID, foreign.modelKnown)); } } public void processForceSkin(UUID playerUUID, EaglerPlayerData eaglerHandler) { - CachedPlayerSkin maybeCachedPacket; - synchronized(onlinePlayersCache) { - maybeCachedPacket = onlinePlayersCache.get(playerUUID); - } + CachedPlayerSkin maybeCachedPacket = onlinePlayersCache.get(playerUUID); if(maybeCachedPacket != null) { eaglerHandler.sendEaglerMessage(maybeCachedPacket.data.getForceClientV4()); }else { - UUID playerTexture; - synchronized(onlinePlayersToTexturesMap) { - playerTexture = onlinePlayersToTexturesMap.get(playerUUID); - } + UUID playerTexture = onlinePlayersToTexturesMap.get(playerUUID); if(playerTexture != null) { Collection possiblePlayers; - synchronized(onlinePlayersFromTexturesMap) { - possiblePlayers = onlinePlayersFromTexturesMap.get(playerTexture); + onlinePlayersFromTexturesMapLock.readLock().lock(); + try { + possiblePlayers = new ArrayList<>(onlinePlayersFromTexturesMap.get(playerTexture)); + }finally { + onlinePlayersFromTexturesMapLock.readLock().unlock(); } boolean playersExist = possiblePlayers.size() > 0; if(playersExist) { for(UUID uuid : possiblePlayers) { - synchronized(onlinePlayersCache) { - maybeCachedPacket = onlinePlayersCache.get(uuid); - } + maybeCachedPacket = onlinePlayersCache.get(uuid); if(maybeCachedPacket != null) { SkinPacketVersionCache rewritten = SkinPacketVersionCache.rewriteUUID( maybeCachedPacket.data, playerUUID.getMostSignificantBits(), playerUUID.getLeastSignificantBits()); - synchronized(onlinePlayersCache) { - onlinePlayersCache.put(playerUUID, new CachedPlayerSkin(rewritten, - maybeCachedPacket.textureUUID, maybeCachedPacket.modelId)); - } + onlinePlayersCache.put(playerUUID, new CachedPlayerSkin(rewritten, + maybeCachedPacket.textureUUID, maybeCachedPacket.modelId)); eaglerHandler.sendEaglerMessage(rewritten.getForceClientV4()); return; } } } - CachedForeignSkin foreignSkin; - synchronized(foreignSkinCache) { - foreignSkin = foreignSkinCache.get(playerTexture); - } + CachedForeignSkin foreignSkin = foreignSkinCache.get(playerTexture); if(foreignSkin != null && foreignSkin.modelKnown != -1) { - synchronized(onlinePlayersCache) { - onlinePlayersCache.put(playerUUID, - new CachedPlayerSkin(SkinPacketVersionCache.rewriteUUID(foreignSkin.data, - playerUUID.getMostSignificantBits(), playerUUID.getLeastSignificantBits()), - playerTexture, foreignSkin.modelKnown)); - } - synchronized(foreignSkinCache) { - foreignSkinCache.remove(playerTexture); - } + onlinePlayersCache.put(playerUUID, + new CachedPlayerSkin(SkinPacketVersionCache.rewriteUUID(foreignSkin.data, + playerUUID.getMostSignificantBits(), playerUUID.getLeastSignificantBits()), + playerTexture, foreignSkin.modelKnown)); + foreignSkinCache.remove(playerTexture); eaglerHandler.sendEaglerMessage(foreignSkin.data.getForceClientV4()); return; } @@ -913,7 +854,7 @@ public class SkinService implements ISkinService { if(skinObj != null) { JsonElement url = json.get("url"); if(url != null) { - String urlStr = SkinService.sanitizeTextureURL(url.getAsString()); + String urlStr = sanitizeTextureURL(url.getAsString()); if(urlStr == null) { break; } @@ -927,19 +868,14 @@ public class SkinService implements ISkinService { } UUID skinUUID = SkinPackets.createEaglerURLSkinUUID(urlStr); - CachedForeignSkin foreignSkin; - synchronized(foreignSkinCache) { - foreignSkin = foreignSkinCache.remove(skinUUID); - } + CachedForeignSkin foreignSkin = foreignSkinCache.remove(skinUUID); if(foreignSkin != null) { registerTextureToPlayerAssociation(skinUUID, playerUUID); SkinPacketVersionCache rewrite = SkinPacketVersionCache .rewriteUUIDModel(foreignSkin.data, playerUUID.getMostSignificantBits(), playerUUID.getLeastSignificantBits(), model); - synchronized(onlinePlayersCache) { - onlinePlayersCache.put(playerUUID, new CachedPlayerSkin(rewrite, skinUUID, model)); - } + onlinePlayersCache.put(playerUUID, new CachedPlayerSkin(rewrite, skinUUID, model)); eaglerHandler.sendEaglerMessage(rewrite.getForceClientV4()); return; } @@ -967,10 +903,7 @@ public class SkinService implements ISkinService { } }); }else { - CachedForeignSkin foreignSkin; - synchronized(foreignSkinCache) { - foreignSkin = foreignSkinCache.get(playerUUID); - } + CachedForeignSkin foreignSkin = foreignSkinCache.get(playerUUID); if(foreignSkin != null) { foreignSkin.lastHit = EaglerXVelocityAPIHelper.steadyTimeMillis(); eaglerHandler.sendEaglerMessage(foreignSkin.data.getForceClientV4()); @@ -997,10 +930,7 @@ public class SkinService implements ISkinService { @Override public void accept(byte[] t) { - CachedPlayerSkin skin; - synchronized(onlinePlayersCache) { - skin = onlinePlayersCache.get(onlineCacheUUID); - } + CachedPlayerSkin skin = onlinePlayersCache.get(onlineCacheUUID); if(skin != null) { initiator.sendEaglerMessage(skin.data.getForceClientV4()); } @@ -1027,9 +957,7 @@ public class SkinService implements ISkinService { onlineCacheUUID.getMostSignificantBits(), onlineCacheUUID.getLeastSignificantBits()), null, -1); } - synchronized (onlinePlayersCache) { - onlinePlayersCache.put(onlineCacheUUID, skin); - } + onlinePlayersCache.put(onlineCacheUUID, skin); initiator.sendEaglerMessage(skin.data.getForceClientV4()); } @@ -1054,10 +982,7 @@ public class SkinService implements ISkinService { @Override public void accept(byte[] t) { - CachedForeignSkin skin; - synchronized(foreignSkinCache) { - skin = foreignSkinCache.get(foreignCacheUUID); - } + CachedForeignSkin skin = foreignSkinCache.get(foreignCacheUUID); if(skin != null) { initiator.sendEaglerMessage(skin.data.getForceClientV4()); } @@ -1085,9 +1010,7 @@ public class SkinService implements ISkinService { foreignCacheUUID.getLeastSignificantBits()), -1); } - synchronized (foreignSkinCache) { - foreignSkinCache.put(foreignCacheUUID, skin); - } + foreignSkinCache.put(foreignCacheUUID, skin); initiator.sendEaglerMessage(skin.data.getForceClientV4()); } @@ -1112,10 +1035,7 @@ public class SkinService implements ISkinService { @Override public void accept(CacheFetchedProfile t) { if(t == null || t.texture == null) { - CachedPlayerSkin skin; - synchronized(onlinePlayersCache) { - skin = onlinePlayersCache.get(playerUUID); - } + CachedPlayerSkin skin = onlinePlayersCache.get(playerUUID); if(skin != null) { initiator.sendEaglerMessage(skin.data.getForceClientV4()); } @@ -1147,9 +1067,7 @@ public class SkinService implements ISkinService { SkinPackets.getModelId(t.model) == 1 ? 1 : 0), null, -1); } - synchronized(onlinePlayersCache) { - onlinePlayersCache.put(playerUUID, skin); - } + onlinePlayersCache.put(playerUUID, skin); initiator.sendEaglerMessage(skin.data.getForceClientV4()); }else { processResolveURLTextureForOnlineToForce(initiator, playerUUID, t.textureUUID, t.texture, @@ -1178,10 +1096,7 @@ public class SkinService implements ISkinService { @Override public void accept(CacheFetchedProfile t) { if(t == null || t.texture == null) { - CachedPlayerSkin skin; - synchronized(onlinePlayersCache) { - skin = onlinePlayersCache.get(t.uuid); - } + CachedPlayerSkin skin = onlinePlayersCache.get(t.uuid); if(skin != null) { initiator.sendEaglerMessage(skin.data.getForceClientV4()); } @@ -1211,9 +1126,7 @@ public class SkinService implements ISkinService { mapUUID.getMostSignificantBits(), mapUUID.getLeastSignificantBits(), SkinPackets.getModelId(t.model) == 1 ? 1 : 0), null, -1); } - synchronized(onlinePlayersCache) { - onlinePlayersCache.put(mapUUID, skin); - } + onlinePlayersCache.put(mapUUID, skin); initiator.sendEaglerMessage(skin.data.getForceClientV4()); }else { processResolveURLTextureForOnlineToForce(initiator, mapUUID, t.textureUUID, t.texture, @@ -1242,10 +1155,7 @@ public class SkinService implements ISkinService { @Override public void accept(CacheFetchedProfile t) { if(t == null || t.texture == null) { - CachedForeignSkin skin; - synchronized(foreignSkinCache) { - skin = foreignSkinCache.get(playerUUID); - } + CachedForeignSkin skin = foreignSkinCache.get(playerUUID); if(skin != null) { initiator.sendEaglerMessage(skin.data.getForceClientV4()); } @@ -1277,9 +1187,7 @@ public class SkinService implements ISkinService { SkinPackets.getModelId(t.model) == 1 ? 1 : 0), -1); } - synchronized(foreignSkinCache) { - foreignSkinCache.put(playerUUID, skin); - } + foreignSkinCache.put(playerUUID, skin); initiator.sendEaglerMessage(skin.data.getForceClientV4()); }else { processResolveURLTextureForForeignToForce(initiator, playerUUID, t.textureUUID, t.texture, @@ -1301,12 +1209,16 @@ public class SkinService implements ISkinService { public void flush() { long millis = EaglerXVelocityAPIHelper.steadyTimeMillis(); - synchronized(foreignSkinCache) { - Iterator itr = foreignSkinCache.values().iterator(); - while(itr.hasNext()) { - if(millis - itr.next().lastHit > 900000l) { // 15 minutes - itr.remove(); - } + final List foreignSkinCleanup = new ArrayList<>(4); + foreignSkinCache.entrySet().forEach((etr) -> { + if(millis - etr.getValue().lastHit > 900000l) { // 15 minutes + foreignSkinCleanup.add(etr.getKey()); + } + }); + + if(!foreignSkinCleanup.isEmpty()) { + for(UUID uuid : foreignSkinCleanup) { + foreignSkinCache.remove(uuid); } } @@ -1357,7 +1269,8 @@ public class SkinService implements ISkinService { elapsedCooldown /= cooldownPeriod; if(elapsedCooldown > 0) { antagonistCooldown += elapsedCooldown * cooldownPeriod; - synchronized(antagonists) { + antagonistsLock.writeLock().lock(); + try { Iterator itr = antagonists.keySet().iterator(); while(itr.hasNext()) { UUID key = itr.next(); @@ -1368,6 +1281,8 @@ public class SkinService implements ISkinService { antagonists.put(key, i); } } + }finally { + antagonistsLock.writeLock().unlock(); } } @@ -1375,10 +1290,7 @@ public class SkinService implements ISkinService { } public SkinPacketVersionCache getSkin(UUID playerUUID) { - CachedPlayerSkin skin; - synchronized(onlinePlayersCache) { - skin = onlinePlayersCache.get(playerUUID); - } + CachedPlayerSkin skin = onlinePlayersCache.get(playerUUID); return skin != null ? skin.data : null; } @@ -1393,25 +1305,26 @@ public class SkinService implements ISkinService { private boolean isLimitedAsAntagonist(UUID uuid) { int limit = EaglerXVelocity.getEagler().getConfig().getAntagonistsRateLimit(); limit += limit >> 1; - synchronized(antagonists) { - int i = antagonists.getInt(uuid); - return i != antagonists.defaultReturnValue() && i > limit; + int i; + antagonistsLock.readLock().lock(); + try { + i = antagonists.getInt(uuid); + }finally { + antagonistsLock.readLock().unlock(); } + return i != antagonists.defaultReturnValue() && i > limit; } private void resetMaps() { - synchronized(onlinePlayersCache) { - onlinePlayersCache.clear(); - } - synchronized(onlinePlayersFromTexturesMap) { + onlinePlayersCache.clear(); + onlinePlayersFromTexturesMapLock.writeLock().lock(); + try { onlinePlayersFromTexturesMap.clear(); + }finally { + onlinePlayersFromTexturesMapLock.writeLock().unlock(); } - synchronized(onlinePlayersToTexturesMap) { - onlinePlayersToTexturesMap.clear(); - } - synchronized(foreignSkinCache) { - foreignSkinCache.clear(); - } + onlinePlayersToTexturesMap.clear(); + foreignSkinCache.clear(); synchronized(pendingTextures) { pendingTextures.clear(); } @@ -1421,8 +1334,11 @@ public class SkinService implements ISkinService { synchronized(pendingNameLookups) { pendingNameLookups.clear(); } - synchronized(antagonists) { + antagonistsLock.writeLock().lock(); + try { antagonists.clear(); + }finally { + antagonistsLock.writeLock().unlock(); } } @@ -1439,7 +1355,7 @@ public class SkinService implements ISkinService { return null; } String host = uri.getHost(); - if(host == null) { + if(host == null || !EaglerXVelocity.getEagler().getConfig().isValidSkinHost(host)) { return null; } scheme = scheme.toLowerCase(); diff --git a/gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/skins/SkinServiceOffline.java b/gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/skins/SkinServiceOffline.java index 6591dfc..b26af62 100644 --- a/gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/skins/SkinServiceOffline.java +++ b/gateway/EaglercraftXVelocity/src/main/java/net/lax1dude/eaglercraft/v1_8/plugin/gateway_velocity/skins/SkinServiceOffline.java @@ -1,20 +1,15 @@ package net.lax1dude.eaglercraft.v1_8.plugin.gateway_velocity.skins; -import java.util.Collection; -import java.util.HashMap; -import java.util.Iterator; -import java.util.Map; import java.util.UUID; - -import com.google.common.collect.Multimap; -import com.google.common.collect.MultimapBuilder; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; import net.lax1dude.eaglercraft.v1_8.plugin.gateway_velocity.server.EaglerPlayerData; import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherSkinPresetEAG; import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.SkinPacketVersionCache; /** - * Copyright (c) 2022-2023 lax1dude. All Rights Reserved. + * Copyright (c) 2022-2025 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 @@ -44,22 +39,15 @@ public class SkinServiceOffline implements ISkinService { } - private final Map skinCache = new HashMap<>(); - - private final Multimap onlinePlayersFromTexturesMap = MultimapBuilder.hashKeys().hashSetValues().build(); + private final ConcurrentMap skinCache = new ConcurrentHashMap<>(); public void init(String uri, String driverClass, String driverPath, int keepObjectsDays, int keepProfilesDays, int maxObjects, int maxProfiles) { - synchronized(skinCache) { - skinCache.clear(); - } + skinCache.clear(); } public void processGetOtherSkin(UUID searchUUID, EaglerPlayerData sender) { - CachedSkin cached; - synchronized(skinCache) { - cached = skinCache.get(searchUUID); - } + CachedSkin cached = skinCache.get(searchUUID); if(cached != null) { sender.sendEaglerMessage(cached.packet.get(sender.getEaglerProtocol())); }else { @@ -69,24 +57,6 @@ public class SkinServiceOffline implements ISkinService { } public void processGetOtherSkin(UUID searchUUID, String skinURL, EaglerPlayerData sender) { - Collection uuids; - synchronized(onlinePlayersFromTexturesMap) { - uuids = onlinePlayersFromTexturesMap.get(searchUUID); - } - if(uuids.size() > 0) { - CachedSkin cached; - synchronized(skinCache) { - Iterator uuidItr = uuids.iterator(); - while(uuidItr.hasNext()) { - cached = skinCache.get(uuidItr.next()); - if(cached != null) { - sender.sendEaglerMessage(cached.packet.get(sender.getEaglerProtocol(), - searchUUID.getMostSignificantBits(), searchUUID.getLeastSignificantBits())); - return; - } - } - } - } if(skinURL.startsWith("eagler://")) { // customs skulls from exported singleplayer worlds sender.sendEaglerMessage(new SPacketOtherSkinPresetEAG(searchUUID.getMostSignificantBits(), searchUUID.getLeastSignificantBits(), 0)); @@ -97,28 +67,21 @@ public class SkinServiceOffline implements ISkinService { } public void registerEaglercraftPlayer(UUID clientUUID, SkinPacketVersionCache generatedPacket, int modelId) { - synchronized(skinCache) { - skinCache.put(clientUUID, new CachedSkin(clientUUID, generatedPacket)); - } + skinCache.put(clientUUID, new CachedSkin(clientUUID, generatedPacket)); } public void unregisterPlayer(UUID clientUUID) { - synchronized(skinCache) { - skinCache.remove(clientUUID); - } + skinCache.remove(clientUUID); + } + + public void registerTextureToPlayerAssociation(String textureURL, UUID playerUUID) { } public void registerTextureToPlayerAssociation(UUID textureUUID, UUID playerUUID) { - synchronized(onlinePlayersFromTexturesMap) { - onlinePlayersFromTexturesMap.put(textureUUID, playerUUID); - } } public void processForceSkin(UUID playerUUID, EaglerPlayerData initialHandler) { - CachedSkin cached; - synchronized(skinCache) { - cached = skinCache.get(playerUUID); - } + CachedSkin cached = skinCache.get(playerUUID); if(cached != null) { initialHandler.sendEaglerMessage(cached.packet.getForceClientV4()); } @@ -129,16 +92,11 @@ public class SkinServiceOffline implements ISkinService { } public void shutdown() { - synchronized(skinCache) { - skinCache.clear(); - } + skinCache.clear(); } public SkinPacketVersionCache getSkin(UUID playerUUID) { - CachedSkin cached; - synchronized(skinCache) { - cached = skinCache.get(playerUUID); - } + CachedSkin cached = skinCache.get(playerUUID); return cached != null ? cached.packet : null; } diff --git a/gateway/EaglercraftXVelocity/src/main/resources/velocity-plugin.json b/gateway/EaglercraftXVelocity/src/main/resources/velocity-plugin.json index 6d8420e..0794334 100644 --- a/gateway/EaglercraftXVelocity/src/main/resources/velocity-plugin.json +++ b/gateway/EaglercraftXVelocity/src/main/resources/velocity-plugin.json @@ -1 +1 @@ -{"id":"eaglerxvelocity","name":"EaglercraftXVelocity","version":"1.1.5","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","authors":["lax1dude", "ayunami2000"],"dependencies":[],"main":"net.lax1dude.eaglercraft.v1_8.plugin.gateway_velocity.EaglerXVelocity"} \ No newline at end of file +{"id":"eaglerxvelocity","name":"EaglercraftXVelocity","version":"1.1.6","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","authors":["lax1dude", "ayunami2000"],"dependencies":[],"main":"net.lax1dude.eaglercraft.v1_8.plugin.gateway_velocity.EaglerXVelocity"} \ No newline at end of file diff --git a/gateway_version_velocity b/gateway_version_velocity index 314c3d7..ab67981 100644 --- a/gateway_version_velocity +++ b/gateway_version_velocity @@ -1 +1 @@ -1.1.5 \ No newline at end of file +1.1.6 \ No newline at end of file