(1.3.5) Reduce lock contention in EaglerXBungee

This commit is contained in:
lax1dude 2025-01-19 12:04:27 -08:00
parent 713ab652f8
commit 1321b5d680
11 changed files with 238 additions and 363 deletions

View File

@ -64,8 +64,11 @@ import net.md_5.bungee.BungeeCord;
*/ */
public class EaglerXBungee extends Plugin { public class EaglerXBungee extends Plugin {
public static final String NATIVE_BUNGEECORD_BUILD = "1.21-R0.1-SNAPSHOT:20a71b0:1887"; public static final String NATIVE_BUNGEECORD_BUILD = "1.21-R0.1-SNAPSHOT:1265a99:1892";
public static final String NATIVE_WATERFALL_BUILD = "1.21-R0.1-SNAPSHOT:bf1be7e:581"; public static final String NATIVE_BUNGEECORD_BUILD_DL = "https://ci.md-5.net/job/BungeeCord/1892/artifact/bootstrap/target/BungeeCord.jar";
public static final String NATIVE_WATERFALL_BUILD = "1.21-R0.1-SNAPSHOT:9ab9e2b:582";
public static final String NATIVE_WATERFALL_BUILD_DL = "https://api.papermc.io/v2/projects/waterfall/versions/1.21/builds/582/downloads/waterfall-1.21-582.jar";
static { static {
CompatWarning.displayCompatWarning(); CompatWarning.displayCompatWarning();

View File

@ -4,6 +4,8 @@ import java.util.HashMap;
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.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EaglerXBungeeAPIHelper; import net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.api.EaglerXBungeeAPIHelper;
@ -44,6 +46,7 @@ public class AuthLoadingCache<K, V> {
boolean shouldEvict(K key, V value); boolean shouldEvict(K key, V value);
} }
private final ReadWriteLock cacheMapLock;
private final Map<K, CacheEntry<V>> cacheMap; private final Map<K, CacheEntry<V>> cacheMap;
private final CacheLoader<K, V> provider; private final CacheLoader<K, V> provider;
private final long cacheTTL; private final long cacheTTL;
@ -51,6 +54,7 @@ public class AuthLoadingCache<K, V> {
private long cacheTimer; private long cacheTimer;
public AuthLoadingCache(CacheLoader<K, V> provider, long cacheTTL) { public AuthLoadingCache(CacheLoader<K, V> provider, long cacheTTL) {
this.cacheMapLock = new ReentrantReadWriteLock();
this.cacheMap = new HashMap<>(); this.cacheMap = new HashMap<>();
this.provider = provider; this.provider = provider;
this.cacheTTL = cacheTTL; this.cacheTTL = cacheTTL;
@ -58,13 +62,19 @@ public class AuthLoadingCache<K, V> {
public V get(K key) { public V get(K key) {
CacheEntry<V> etr; CacheEntry<V> etr;
synchronized(cacheMap) { cacheMapLock.readLock().lock();
try {
etr = cacheMap.get(key); etr = cacheMap.get(key);
}finally {
cacheMapLock.readLock().unlock();
} }
if(etr == null) { if(etr == null) {
cacheMapLock.writeLock().lock();
V loaded = provider.load(key); V loaded = provider.load(key);
synchronized(cacheMap) { try {
cacheMap.put(key, new CacheEntry<>(loaded)); cacheMap.put(key, new CacheEntry<>(loaded));
}finally {
cacheMapLock.writeLock().unlock();
} }
return loaded; return loaded;
}else { }else {
@ -74,13 +84,17 @@ public class AuthLoadingCache<K, V> {
} }
public void evict(K key) { public void evict(K key) {
synchronized(cacheMap) { cacheMapLock.writeLock().lock();
try {
cacheMap.remove(key); cacheMap.remove(key);
}finally {
cacheMapLock.writeLock().unlock();
} }
} }
public void evictAll(CacheVisitor<K, V> visitor) { public void evictAll(CacheVisitor<K, V> visitor) {
synchronized(cacheMap) { cacheMapLock.writeLock().lock();
try {
Iterator<Entry<K,CacheEntry<V>>> itr = cacheMap.entrySet().iterator(); Iterator<Entry<K,CacheEntry<V>>> itr = cacheMap.entrySet().iterator();
while(itr.hasNext()) { while(itr.hasNext()) {
Entry<K,CacheEntry<V>> etr = itr.next(); Entry<K,CacheEntry<V>> etr = itr.next();
@ -88,6 +102,8 @@ public class AuthLoadingCache<K, V> {
itr.remove(); itr.remove();
} }
} }
}finally {
cacheMapLock.writeLock().unlock();
} }
} }
@ -95,7 +111,8 @@ public class AuthLoadingCache<K, V> {
long millis = EaglerXBungeeAPIHelper.steadyTimeMillis(); long millis = EaglerXBungeeAPIHelper.steadyTimeMillis();
if(millis - cacheTimer > (cacheTTL / 2L)) { if(millis - cacheTimer > (cacheTTL / 2L)) {
cacheTimer = millis; cacheTimer = millis;
synchronized(cacheMap) { cacheMapLock.writeLock().lock();
try {
Iterator<CacheEntry<V>> mapItr = cacheMap.values().iterator(); Iterator<CacheEntry<V>> mapItr = cacheMap.values().iterator();
while(mapItr.hasNext()) { while(mapItr.hasNext()) {
CacheEntry<V> etr = mapItr.next(); CacheEntry<V> etr = mapItr.next();
@ -103,13 +120,18 @@ public class AuthLoadingCache<K, V> {
mapItr.remove(); mapItr.remove();
} }
} }
}finally {
cacheMapLock.writeLock().unlock();
} }
} }
} }
public void flush() { public void flush() {
synchronized(cacheMap) { cacheMapLock.writeLock().lock();
try {
cacheMap.clear(); cacheMap.clear();
}finally {
cacheMapLock.writeLock().unlock();
} }
} }

View File

@ -1,6 +1,7 @@
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.protocol; package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.server.protocol;
import java.io.IOException; import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
@ -238,10 +239,12 @@ public class GameProtocolMessageController {
while(!sendQueueV4.isEmpty()) { while(!sendQueueV4.isEmpty()) {
sendCount = 0; sendCount = 0;
totalLen = 0; totalLen = 0;
Iterator<byte[]> itr = sendQueueV4.iterator();
do { do {
i = sendQueueV4.get(sendCount++).length; i = itr.next().length;
totalLen += GamePacketOutputBuffer.getVarIntSize(i) + i; totalLen += GamePacketOutputBuffer.getVarIntSize(i) + i;
}while(totalLen < 32760 && sendCount < sendQueueV4.size()); ++sendCount;
}while(totalLen < 32760 && itr.hasNext());
if(totalLen >= 32760) { if(totalLen >= 32760) {
--sendCount; --sendCount;
} }

View File

@ -40,7 +40,6 @@ public class HttpWebServer {
private final String page404; private final String page404;
private static HttpMemoryCache default404Page; private static HttpMemoryCache default404Page;
private static HttpMemoryCache default404UpgradePage; private static HttpMemoryCache default404UpgradePage;
private static final Object cacheClearLock = new Object();
public HttpWebServer(File directory, Map<String,HttpContentType> contentTypes, List<String> index, String page404) { public HttpWebServer(File directory, Map<String,HttpContentType> contentTypes, List<String> index, String page404) {
this.directory = directory; this.directory = directory;
@ -52,15 +51,13 @@ public class HttpWebServer {
public void flushCache() { public void flushCache() {
long millis = EaglerXBungeeAPIHelper.steadyTimeMillis(); long millis = EaglerXBungeeAPIHelper.steadyTimeMillis();
synchronized(cacheClearLock) { synchronized(filesCache) {
synchronized(filesCache) { Iterator<HttpMemoryCache> itr = filesCache.values().iterator();
Iterator<HttpMemoryCache> itr = filesCache.values().iterator(); while(itr.hasNext()) {
while(itr.hasNext()) { HttpMemoryCache i = itr.next();
HttpMemoryCache i = itr.next(); if(i.contentType.fileBrowserCacheTTL != Long.MAX_VALUE && millis - i.lastCacheHit > 900000l) {
if(i.contentType.fileBrowserCacheTTL != Long.MAX_VALUE && millis - i.lastCacheHit > 900000l) { i.fileData.release();
i.fileData.release(); itr.remove();
itr.remove();
}
} }
} }
} }
@ -94,19 +91,16 @@ public class HttpWebServer {
String joinedPath = String.join("/", pathList); String joinedPath = String.join("/", pathList);
synchronized(cacheClearLock) { //TODO: Rewrite this to cause less lock contention
synchronized(filesCache) { synchronized(filesCache) {
cached = filesCache.get(joinedPath); cached = filesCache.get(joinedPath);
}
if(cached != null) { if(cached != null) {
cached = validateCache(cached); cached = validateCache(cached);
if(cached != null) { if(cached != null) {
return cached; return cached;
}else { }else {
synchronized(filesCache) { filesCache.remove(joinedPath);
filesCache.remove(joinedPath);
}
} }
} }
@ -123,19 +117,13 @@ public class HttpWebServer {
if(f.isDirectory()) { if(f.isDirectory()) {
for(int i = 0, l = index.size(); i < l; ++i) { for(int i = 0, l = index.size(); i < l; ++i) {
String p = joinedPath + "/" + index.get(i); String p = joinedPath + "/" + index.get(i);
synchronized(filesCache) { cached = filesCache.get(p);
cached = filesCache.get(p);
}
if(cached != null) { if(cached != null) {
cached = validateCache(cached); cached = validateCache(cached);
if(cached != null) { if(cached != null) {
synchronized(filesCache) { filesCache.put(joinedPath, cached);
filesCache.put(joinedPath, cached);
}
}else { }else {
synchronized(filesCache) { filesCache.remove(p);
filesCache.remove(p);
}
if(page404 == null || path.equals(page404)) { if(page404 == null || path.equals(page404)) {
return default404Page; return default404Page;
}else { }else {
@ -151,9 +139,7 @@ public class HttpWebServer {
if(ff.isFile()) { if(ff.isFile()) {
HttpMemoryCache memCache = retrieveFile(ff, p); HttpMemoryCache memCache = retrieveFile(ff, p);
if(memCache != null) { if(memCache != null) {
synchronized(filesCache) { filesCache.put(joinedPath, memCache);
filesCache.put(joinedPath, memCache);
}
return memCache; return memCache;
} }
} }
@ -166,9 +152,7 @@ public class HttpWebServer {
}else { }else {
HttpMemoryCache memCache = retrieveFile(f, joinedPath); HttpMemoryCache memCache = retrieveFile(f, joinedPath);
if(memCache != null) { if(memCache != null) {
synchronized(filesCache) { filesCache.put(joinedPath, memCache);
filesCache.put(joinedPath, memCache);
}
return memCache; return memCache;
}else { }else {
if(page404 == null || path.equals(page404)) { if(page404 == null || path.equals(page404)) {

View File

@ -37,7 +37,10 @@ public class CompatWarning {
":> apart from the versions listed below:", ":> apart from the versions listed below:",
":> ", ":> ",
":> - BungeeCord: " + EaglerXBungee.NATIVE_BUNGEECORD_BUILD, ":> - BungeeCord: " + EaglerXBungee.NATIVE_BUNGEECORD_BUILD,
":> - " + EaglerXBungee.NATIVE_BUNGEECORD_BUILD_DL,
":> ",
":> - Waterfall: " + EaglerXBungee.NATIVE_WATERFALL_BUILD, ":> - Waterfall: " + EaglerXBungee.NATIVE_WATERFALL_BUILD,
":> - " + EaglerXBungee.NATIVE_WATERFALL_BUILD_DL,
":> ", ":> ",
":> This is not a Bukkit/Spigot plugin!", ":> This is not a Bukkit/Spigot plugin!",
":> ", ":> ",

View File

@ -1,8 +1,8 @@
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins; package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
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.socket.protocol.pkt.GameMessagePacket; import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.GameMessagePacket;
@ -13,7 +13,7 @@ import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherCape
import net.md_5.bungee.UserConnection; import net.md_5.bungee.UserConnection;
/** /**
* 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 * 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
@ -31,21 +31,16 @@ public class CapeServiceOffline {
public static final int masterRateLimitPerPlayer = 250; public static final int masterRateLimitPerPlayer = 250;
private final Map<UUID, GameMessagePacket> capesCache = new HashMap<>(); private final ConcurrentMap<UUID, GameMessagePacket> capesCache = new ConcurrentHashMap<>();
public void registerEaglercraftPlayer(UUID playerUUID, GameMessagePacket capePacket) { public void registerEaglercraftPlayer(UUID playerUUID, GameMessagePacket capePacket) {
synchronized(capesCache) { capesCache.put(playerUUID, capePacket);
capesCache.put(playerUUID, capePacket);
}
} }
public void processGetOtherCape(UUID searchUUID, UserConnection sender) { public void processGetOtherCape(UUID searchUUID, UserConnection sender) {
EaglerInitialHandler initialHandler = (EaglerInitialHandler)sender.getPendingConnection(); EaglerInitialHandler initialHandler = (EaglerInitialHandler)sender.getPendingConnection();
if(initialHandler.skinLookupRateLimiter.rateLimit(masterRateLimitPerPlayer)) { if(initialHandler.skinLookupRateLimiter.rateLimit(masterRateLimitPerPlayer)) {
GameMessagePacket maybeCape; GameMessagePacket maybeCape = capesCache.get(searchUUID);
synchronized(capesCache) {
maybeCape = capesCache.get(searchUUID);
}
if(maybeCape != null) { if(maybeCape != null) {
initialHandler.sendEaglerMessage(maybeCape); initialHandler.sendEaglerMessage(maybeCape);
}else { }else {
@ -56,10 +51,7 @@ public class CapeServiceOffline {
} }
public void processForceCape(UUID clientUUID, EaglerInitialHandler initialHandler) { public void processForceCape(UUID clientUUID, EaglerInitialHandler initialHandler) {
GameMessagePacket maybeCape; GameMessagePacket maybeCape = capesCache.get(clientUUID);
synchronized(capesCache) {
maybeCape = capesCache.get(clientUUID);
}
if(maybeCape != null) { if(maybeCape != null) {
if (maybeCape instanceof SPacketOtherCapePresetEAG) { if (maybeCape instanceof SPacketOtherCapePresetEAG) {
initialHandler.sendEaglerMessage( initialHandler.sendEaglerMessage(
@ -72,15 +64,11 @@ public class CapeServiceOffline {
} }
public void unregisterPlayer(UUID playerUUID) { public void unregisterPlayer(UUID playerUUID) {
synchronized(capesCache) { capesCache.remove(playerUUID);
capesCache.remove(playerUUID);
}
} }
public GameMessagePacket getCape(UUID clientUUID) { public GameMessagePacket getCape(UUID clientUUID) {
synchronized(capesCache) { return capesCache.get(clientUUID);
return capesCache.get(clientUUID);
}
} }
public byte[] getCapeHandshakeData(UUID clientUUID) { public byte[] getCapeHandshakeData(UUID clientUUID) {
@ -109,8 +97,6 @@ public class CapeServiceOffline {
} }
public void shutdown() { public void shutdown() {
synchronized(capesCache) { capesCache.clear();
capesCache.clear();
}
} }
} }

View File

@ -1,6 +1,7 @@
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins; package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins;
import java.net.URI; import java.net.URI;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
@ -10,6 +11,10 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID; 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 java.util.function.Consumer;
import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Base64;
@ -38,7 +43,7 @@ import net.md_5.bungee.connection.LoginResult;
import net.md_5.bungee.protocol.Property; import net.md_5.bungee.protocol.Property;
/** /**
* 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 * 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
@ -56,16 +61,20 @@ public class SkinService implements ISkinService {
public static final int masterRateLimitPerPlayer = 250; public static final int masterRateLimitPerPlayer = 250;
private final Map<UUID, CachedPlayerSkin> onlinePlayersCache = new HashMap<>(); private final ConcurrentMap<UUID, CachedPlayerSkin> onlinePlayersCache = new ConcurrentHashMap<>();
private final ConcurrentMap<UUID, UUID> onlinePlayersToTexturesMap = new ConcurrentHashMap<>();
private final ConcurrentMap<UUID, CachedForeignSkin> foreignSkinCache = new ConcurrentHashMap<>();
private final ReadWriteLock onlinePlayersFromTexturesMapLock = new ReentrantReadWriteLock();
private final Multimap<UUID, UUID> onlinePlayersFromTexturesMap = MultimapBuilder.hashKeys().hashSetValues().build(); private final Multimap<UUID, UUID> onlinePlayersFromTexturesMap = MultimapBuilder.hashKeys().hashSetValues().build();
private final Map<UUID, UUID> onlinePlayersToTexturesMap = new HashMap<>();
private final Map<UUID, CachedForeignSkin> foreignSkinCache = new HashMap<>();
private final Map<UUID, PendingTextureDownload> pendingTextures = new HashMap<>(); private final Map<UUID, PendingTextureDownload> pendingTextures = new HashMap<>();
private final Map<UUID, PendingProfileUUIDLookup> pendingUUIDs = new HashMap<>(); private final Map<UUID, PendingProfileUUIDLookup> pendingUUIDs = new HashMap<>();
private final Map<String, PendingProfileNameLookup> pendingNameLookups = new HashMap<>(); private final Map<String, PendingProfileNameLookup> pendingNameLookups = new HashMap<>();
private final ReadWriteLock antagonistsLock = new ReentrantReadWriteLock();
private final TObjectIntMap<UUID> antagonists = new TObjectIntHashMap<>(); private final TObjectIntMap<UUID> antagonists = new TObjectIntHashMap<>();
private long antagonistCooldown = EaglerXBungeeAPIHelper.steadyTimeMillis(); private long antagonistCooldown = EaglerXBungeeAPIHelper.steadyTimeMillis();
private final Consumer<Set<UUID>> antagonistLogger = new Consumer<Set<UUID>>() { private final Consumer<Set<UUID>> antagonistLogger = new Consumer<Set<UUID>>() {
@ -75,7 +84,8 @@ public class SkinService implements ISkinService {
if(t.size() == 1) { if(t.size() == 1) {
int limit = EaglerXBungee.getEagler().getConfig().getAntagonistsRateLimit() << 1; int limit = EaglerXBungee.getEagler().getConfig().getAntagonistsRateLimit() << 1;
UUID offender = t.iterator().next(); UUID offender = t.iterator().next();
synchronized(antagonists) { antagonistsLock.writeLock().lock();
try {
int v = antagonists.get(offender); int v = antagonists.get(offender);
if(v == antagonists.getNoEntryValue()) { if(v == antagonists.getNoEntryValue()) {
antagonists.put(offender, 1); antagonists.put(offender, 1);
@ -84,6 +94,8 @@ public class SkinService implements ISkinService {
antagonists.put(offender, v + 1); antagonists.put(offender, v + 1);
} }
} }
}finally {
antagonistsLock.writeLock().unlock();
} }
} }
} }
@ -97,7 +109,7 @@ public class SkinService implements ISkinService {
protected final UUID uuid; protected final UUID uuid;
protected final SkinPacketVersionCache data; protected final SkinPacketVersionCache data;
protected final int modelKnown; protected final int modelKnown;
protected long lastHit; protected volatile long lastHit;
protected CachedForeignSkin(UUID uuid, SkinPacketVersionCache data, int modelKnown) { protected CachedForeignSkin(UUID uuid, SkinPacketVersionCache data, int modelKnown) {
this.uuid = uuid; this.uuid = uuid;
@ -132,7 +144,7 @@ public class SkinService implements ISkinService {
protected final Consumer<Set<UUID>> antagonistsCallback; protected final Consumer<Set<UUID>> antagonistsCallback;
protected final long initializedTime; protected final long initializedTime;
protected boolean finalized; protected volatile boolean finalized;
protected PendingTextureDownload(UUID textureUUID, String textureURL, UUID caller, Consumer<byte[]> callback, protected PendingTextureDownload(UUID textureUUID, String textureURL, UUID caller, Consumer<byte[]> callback,
Consumer<Set<UUID>> antagonistsCallback) { Consumer<Set<UUID>> antagonistsCallback) {
@ -174,7 +186,7 @@ public class SkinService implements ISkinService {
protected final Consumer<Set<UUID>> antagonistsCallback; protected final Consumer<Set<UUID>> antagonistsCallback;
protected final long initializedTime; protected final long initializedTime;
protected boolean finalized; protected volatile boolean finalized;
protected PendingProfileUUIDLookup(UUID profileUUID, UUID caller, Consumer<CacheFetchedProfile> callback, protected PendingProfileUUIDLookup(UUID profileUUID, UUID caller, Consumer<CacheFetchedProfile> callback,
Consumer<Set<UUID>> antagonistsCallback) { Consumer<Set<UUID>> antagonistsCallback) {
@ -215,7 +227,7 @@ public class SkinService implements ISkinService {
protected final Consumer<Set<UUID>> antagonistsCallback; protected final Consumer<Set<UUID>> antagonistsCallback;
protected final long initializedTime; protected final long initializedTime;
protected boolean finalized; protected volatile boolean finalized;
protected PendingProfileNameLookup(String profileName, UUID caller, Consumer<CacheFetchedProfile> callback, protected PendingProfileNameLookup(String profileName, UUID caller, Consumer<CacheFetchedProfile> callback,
Consumer<Set<UUID>> antagonistsCallback) { Consumer<Set<UUID>> antagonistsCallback) {
@ -263,60 +275,46 @@ public class SkinService implements ISkinService {
return; return;
} }
CachedPlayerSkin maybeCachedPacket; CachedPlayerSkin maybeCachedPacket = onlinePlayersCache.get(searchUUID);
synchronized(onlinePlayersCache) {
maybeCachedPacket = onlinePlayersCache.get(searchUUID);
}
if(maybeCachedPacket != null) { if(maybeCachedPacket != null) {
eaglerHandler.sendEaglerMessage(maybeCachedPacket.data.get(eaglerHandler.getEaglerProtocol())); eaglerHandler.sendEaglerMessage(maybeCachedPacket.data.get(eaglerHandler.getEaglerProtocol()));
}else { }else {
ProxiedPlayer player = BungeeCord.getInstance().getPlayer(searchUUID); ProxiedPlayer player = BungeeCord.getInstance().getPlayer(searchUUID);
UUID playerTexture; UUID playerTexture = onlinePlayersToTexturesMap.get(searchUUID);
synchronized(onlinePlayersToTexturesMap) {
playerTexture = onlinePlayersToTexturesMap.get(searchUUID);
}
if(playerTexture != null) { if(playerTexture != null) {
Collection<UUID> possiblePlayers; Collection<UUID> possiblePlayers;
synchronized(onlinePlayersFromTexturesMap) { onlinePlayersFromTexturesMapLock.readLock().lock();
possiblePlayers = onlinePlayersFromTexturesMap.get(playerTexture); try {
possiblePlayers = new ArrayList<>(onlinePlayersFromTexturesMap.get(playerTexture));
}finally {
onlinePlayersFromTexturesMapLock.readLock().unlock();
} }
boolean playersExist = possiblePlayers.size() > 0; boolean playersExist = possiblePlayers.size() > 0;
if(playersExist) { if(playersExist) {
for(UUID uuid : possiblePlayers) { for(UUID uuid : possiblePlayers) {
synchronized(onlinePlayersCache) { maybeCachedPacket = onlinePlayersCache.get(uuid);
maybeCachedPacket = onlinePlayersCache.get(uuid);
}
if(maybeCachedPacket != null) { if(maybeCachedPacket != null) {
SkinPacketVersionCache rewritten = SkinPacketVersionCache.rewriteUUID( SkinPacketVersionCache rewritten = SkinPacketVersionCache.rewriteUUID(
maybeCachedPacket.data, searchUUID.getMostSignificantBits(), maybeCachedPacket.data, searchUUID.getMostSignificantBits(),
searchUUID.getLeastSignificantBits()); searchUUID.getLeastSignificantBits());
if(player != null) { if(player != null) {
synchronized(onlinePlayersCache) { onlinePlayersCache.put(searchUUID, new CachedPlayerSkin(rewritten,
onlinePlayersCache.put(searchUUID, new CachedPlayerSkin(rewritten, maybeCachedPacket.textureUUID, maybeCachedPacket.modelId));
maybeCachedPacket.textureUUID, maybeCachedPacket.modelId));
}
} }
eaglerHandler.sendEaglerMessage(rewritten.get(eaglerHandler.getEaglerProtocol())); eaglerHandler.sendEaglerMessage(rewritten.get(eaglerHandler.getEaglerProtocol()));
return; return;
} }
} }
} }
CachedForeignSkin foreignSkin; CachedForeignSkin foreignSkin = foreignSkinCache.get(playerTexture);
synchronized(foreignSkinCache) {
foreignSkin = foreignSkinCache.get(playerTexture);
}
if(foreignSkin != null && foreignSkin.modelKnown != -1) { if(foreignSkin != null && foreignSkin.modelKnown != -1) {
if(player != null) { if(player != null) {
synchronized(onlinePlayersCache) { onlinePlayersCache.put(searchUUID,
onlinePlayersCache.put(searchUUID, new CachedPlayerSkin(SkinPacketVersionCache.rewriteUUID(foreignSkin.data,
new CachedPlayerSkin(SkinPacketVersionCache.rewriteUUID(foreignSkin.data, searchUUID.getMostSignificantBits(), searchUUID.getLeastSignificantBits()),
searchUUID.getMostSignificantBits(), searchUUID.getLeastSignificantBits()), playerTexture, foreignSkin.modelKnown));
playerTexture, foreignSkin.modelKnown)); foreignSkinCache.remove(playerTexture);
}
synchronized(foreignSkinCache) {
foreignSkinCache.remove(playerTexture);
}
}else { }else {
foreignSkin.lastHit = EaglerXBungeeAPIHelper.steadyTimeMillis(); foreignSkin.lastHit = EaglerXBungeeAPIHelper.steadyTimeMillis();
} }
@ -339,7 +337,7 @@ public class SkinService implements ISkinService {
if(skinObj != null) { if(skinObj != null) {
JsonElement url = json.get("url"); JsonElement url = json.get("url");
if(url != null) { if(url != null) {
String urlStr = SkinService.sanitizeTextureURL(url.getAsString()); String urlStr = sanitizeTextureURL(url.getAsString());
if(urlStr == null) { if(urlStr == null) {
break; break;
} }
@ -353,19 +351,14 @@ public class SkinService implements ISkinService {
} }
UUID skinUUID = SkinPackets.createEaglerURLSkinUUID(urlStr); UUID skinUUID = SkinPackets.createEaglerURLSkinUUID(urlStr);
CachedForeignSkin foreignSkin; CachedForeignSkin foreignSkin = foreignSkinCache.remove(skinUUID);
synchronized(foreignSkinCache) {
foreignSkin = foreignSkinCache.remove(skinUUID);
}
if(foreignSkin != null) { if(foreignSkin != null) {
registerTextureToPlayerAssociation(skinUUID, searchUUID); registerTextureToPlayerAssociation(skinUUID, searchUUID);
SkinPacketVersionCache rewrite = SkinPacketVersionCache SkinPacketVersionCache rewrite = SkinPacketVersionCache
.rewriteUUIDModel(foreignSkin.data, .rewriteUUIDModel(foreignSkin.data,
searchUUID.getMostSignificantBits(), searchUUID.getMostSignificantBits(),
searchUUID.getLeastSignificantBits(), model); searchUUID.getLeastSignificantBits(), model);
synchronized(onlinePlayersCache) { onlinePlayersCache.put(searchUUID, new CachedPlayerSkin(rewrite, skinUUID, model));
onlinePlayersCache.put(searchUUID, new CachedPlayerSkin(rewrite, skinUUID, model));
}
eaglerHandler.sendEaglerMessage(rewrite.get(eaglerHandler.getEaglerProtocol())); eaglerHandler.sendEaglerMessage(rewrite.get(eaglerHandler.getEaglerProtocol()));
return; return;
} }
@ -398,10 +391,7 @@ public class SkinService implements ISkinService {
}); });
} }
}else { }else {
CachedForeignSkin foreignSkin; CachedForeignSkin foreignSkin = foreignSkinCache.get(searchUUID);
synchronized(foreignSkinCache) {
foreignSkin = foreignSkinCache.get(searchUUID);
}
if(foreignSkin != null) { if(foreignSkin != null) {
foreignSkin.lastHit = EaglerXBungeeAPIHelper.steadyTimeMillis(); foreignSkin.lastHit = EaglerXBungeeAPIHelper.steadyTimeMillis();
eaglerHandler.sendEaglerMessage(foreignSkin.data.get(eaglerHandler.getEaglerProtocol())); eaglerHandler.sendEaglerMessage(foreignSkin.data.get(eaglerHandler.getEaglerProtocol()));
@ -430,25 +420,22 @@ public class SkinService implements ISkinService {
if(!eaglerHandler.skinLookupRateLimiter.rateLimit(masterRateLimitPerPlayer)) { if(!eaglerHandler.skinLookupRateLimiter.rateLimit(masterRateLimitPerPlayer)) {
return; return;
} }
CachedForeignSkin foreignSkin; CachedForeignSkin foreignSkin = foreignSkinCache.get(searchUUID);
synchronized(foreignSkinCache) {
foreignSkin = foreignSkinCache.get(searchUUID);
}
if(foreignSkin != null) { if(foreignSkin != null) {
foreignSkin.lastHit = EaglerXBungeeAPIHelper.steadyTimeMillis(); foreignSkin.lastHit = EaglerXBungeeAPIHelper.steadyTimeMillis();
eaglerHandler.sendEaglerMessage(foreignSkin.data.get(eaglerHandler.getEaglerProtocol())); eaglerHandler.sendEaglerMessage(foreignSkin.data.get(eaglerHandler.getEaglerProtocol()));
}else { }else {
Collection<UUID> possiblePlayers; Collection<UUID> possiblePlayers;
synchronized(onlinePlayersFromTexturesMap) { onlinePlayersFromTexturesMapLock.readLock().lock();
possiblePlayers = onlinePlayersFromTexturesMap.get(searchUUID); try {
possiblePlayers = new ArrayList<>(onlinePlayersFromTexturesMap.get(searchUUID));
}finally {
onlinePlayersFromTexturesMapLock.readLock().unlock();
} }
boolean playersExist = possiblePlayers.size() > 0; boolean playersExist = possiblePlayers.size() > 0;
if(playersExist) { if(playersExist) {
for(UUID uuid : possiblePlayers) { for(UUID uuid : possiblePlayers) {
CachedPlayerSkin maybeCachedPacket; CachedPlayerSkin maybeCachedPacket = onlinePlayersCache.get(uuid);
synchronized(onlinePlayersCache) {
maybeCachedPacket = onlinePlayersCache.get(uuid);
}
if(maybeCachedPacket != null) { if(maybeCachedPacket != null) {
eaglerHandler.sendEaglerMessage(maybeCachedPacket.data.get(eaglerHandler.getEaglerProtocol(), eaglerHandler.sendEaglerMessage(maybeCachedPacket.data.get(eaglerHandler.getEaglerProtocol(),
searchUUID.getMostSignificantBits(), searchUUID.getLeastSignificantBits())); searchUUID.getMostSignificantBits(), searchUUID.getLeastSignificantBits()));
@ -461,10 +448,17 @@ public class SkinService implements ISkinService {
searchUUID.getLeastSignificantBits(), 0)); searchUUID.getLeastSignificantBits(), 0));
return; return;
} }
if(eaglerHandler.skinTextureDownloadRateLimiter.rateLimit(config.getSkinRateLimitPlayer()) && !isLimitedAsAntagonist(sender.getUniqueId())) { skinURL = sanitizeTextureURL(skinURL);
doAsync(() -> { if(skinURL != null) {
processResolveURLTextureForForeign(sender, searchUUID, searchUUID, skinURL, -1); final String skinURL_ = skinURL;
}); if(eaglerHandler.skinTextureDownloadRateLimiter.rateLimit(config.getSkinRateLimitPlayer()) && !isLimitedAsAntagonist(sender.getUniqueId())) {
doAsync(() -> {
processResolveURLTextureForForeign(sender, searchUUID, searchUUID, skinURL_, -1);
});
}
}else {
eaglerHandler.sendEaglerMessage(new SPacketOtherSkinPresetEAG(searchUUID.getMostSignificantBits(),
searchUUID.getLeastSignificantBits(), 0));
} }
} }
} }
@ -479,10 +473,7 @@ public class SkinService implements ISkinService {
@Override @Override
public void accept(byte[] t) { public void accept(byte[] t) {
CachedPlayerSkin skin; CachedPlayerSkin skin = onlinePlayersCache.get(onlineCacheUUID);
synchronized(onlinePlayersCache) {
skin = onlinePlayersCache.get(onlineCacheUUID);
}
if(skin != null) { if(skin != null) {
EaglerInitialHandler initialHandler = (EaglerInitialHandler)initiator.getPendingConnection(); EaglerInitialHandler initialHandler = (EaglerInitialHandler)initiator.getPendingConnection();
initialHandler.sendEaglerMessage(skin.data.get(initialHandler.getEaglerProtocol())); initialHandler.sendEaglerMessage(skin.data.get(initialHandler.getEaglerProtocol()));
@ -510,9 +501,7 @@ public class SkinService implements ISkinService {
onlineCacheUUID.getMostSignificantBits(), onlineCacheUUID.getMostSignificantBits(),
onlineCacheUUID.getLeastSignificantBits()), null, -1); onlineCacheUUID.getLeastSignificantBits()), null, -1);
} }
synchronized (onlinePlayersCache) { onlinePlayersCache.put(onlineCacheUUID, skin);
onlinePlayersCache.put(onlineCacheUUID, skin);
}
EaglerInitialHandler initialHandler = (EaglerInitialHandler) initiator.getPendingConnection(); EaglerInitialHandler initialHandler = (EaglerInitialHandler) initiator.getPendingConnection();
initialHandler.sendEaglerMessage(skin.data.get(initialHandler.getEaglerProtocol())); initialHandler.sendEaglerMessage(skin.data.get(initialHandler.getEaglerProtocol()));
} }
@ -538,10 +527,7 @@ public class SkinService implements ISkinService {
@Override @Override
public void accept(byte[] t) { public void accept(byte[] t) {
CachedForeignSkin skin; CachedForeignSkin skin = foreignSkinCache.get(foreignCacheUUID);
synchronized(foreignSkinCache) {
skin = foreignSkinCache.get(foreignCacheUUID);
}
if(skin != null) { if(skin != null) {
EaglerInitialHandler initialHandler = (EaglerInitialHandler) initiator.getPendingConnection(); EaglerInitialHandler initialHandler = (EaglerInitialHandler) initiator.getPendingConnection();
initialHandler.sendEaglerMessage(skin.data.get(initialHandler.getEaglerProtocol())); initialHandler.sendEaglerMessage(skin.data.get(initialHandler.getEaglerProtocol()));
@ -570,9 +556,7 @@ public class SkinService implements ISkinService {
foreignCacheUUID.getLeastSignificantBits()), foreignCacheUUID.getLeastSignificantBits()),
-1); -1);
} }
synchronized (foreignSkinCache) { foreignSkinCache.put(foreignCacheUUID, skin);
foreignSkinCache.put(foreignCacheUUID, skin);
}
EaglerInitialHandler initialHandler = (EaglerInitialHandler) initiator.getPendingConnection(); EaglerInitialHandler initialHandler = (EaglerInitialHandler) initiator.getPendingConnection();
initialHandler.sendEaglerMessage(skin.data.get(initialHandler.getEaglerProtocol())); initialHandler.sendEaglerMessage(skin.data.get(initialHandler.getEaglerProtocol()));
} }
@ -598,10 +582,7 @@ public class SkinService implements ISkinService {
@Override @Override
public void accept(CacheFetchedProfile t) { public void accept(CacheFetchedProfile t) {
if(t == null || t.texture == null) { if(t == null || t.texture == null) {
CachedPlayerSkin skin; CachedPlayerSkin skin = onlinePlayersCache.get(playerUUID);
synchronized(onlinePlayersCache) {
skin = onlinePlayersCache.get(playerUUID);
}
if(skin != null) { if(skin != null) {
EaglerInitialHandler initialHandler = (EaglerInitialHandler) initiator.getPendingConnection(); EaglerInitialHandler initialHandler = (EaglerInitialHandler) initiator.getPendingConnection();
initialHandler.sendEaglerMessage(skin.data.get(initialHandler.getEaglerProtocol())); initialHandler.sendEaglerMessage(skin.data.get(initialHandler.getEaglerProtocol()));
@ -634,9 +615,7 @@ public class SkinService implements ISkinService {
SkinPackets.getModelId(t.model) == 1 ? 1 : 0), SkinPackets.getModelId(t.model) == 1 ? 1 : 0),
null, -1); null, -1);
} }
synchronized(onlinePlayersCache) { onlinePlayersCache.put(playerUUID, skin);
onlinePlayersCache.put(playerUUID, skin);
}
EaglerInitialHandler initialHandler = (EaglerInitialHandler) initiator.getPendingConnection(); EaglerInitialHandler initialHandler = (EaglerInitialHandler) initiator.getPendingConnection();
initialHandler.sendEaglerMessage(skin.data.get(initialHandler.getEaglerProtocol())); initialHandler.sendEaglerMessage(skin.data.get(initialHandler.getEaglerProtocol()));
}else { }else {
@ -666,10 +645,7 @@ public class SkinService implements ISkinService {
@Override @Override
public void accept(CacheFetchedProfile t) { public void accept(CacheFetchedProfile t) {
if(t == null || t.texture == null) { if(t == null || t.texture == null) {
CachedPlayerSkin skin; CachedPlayerSkin skin = onlinePlayersCache.get(t.uuid);
synchronized(onlinePlayersCache) {
skin = onlinePlayersCache.get(t.uuid);
}
if(skin != null) { if(skin != null) {
EaglerInitialHandler initialHandler = (EaglerInitialHandler) initiator.getPendingConnection(); EaglerInitialHandler initialHandler = (EaglerInitialHandler) initiator.getPendingConnection();
initialHandler.sendEaglerMessage(skin.data.get(initialHandler.getEaglerProtocol())); initialHandler.sendEaglerMessage(skin.data.get(initialHandler.getEaglerProtocol()));
@ -700,9 +676,7 @@ public class SkinService implements ISkinService {
mapUUID.getMostSignificantBits(), mapUUID.getLeastSignificantBits(), mapUUID.getMostSignificantBits(), mapUUID.getLeastSignificantBits(),
SkinPackets.getModelId(t.model) == 1 ? 1 : 0), null, -1); SkinPackets.getModelId(t.model) == 1 ? 1 : 0), null, -1);
} }
synchronized(onlinePlayersCache) { onlinePlayersCache.put(mapUUID, skin);
onlinePlayersCache.put(mapUUID, skin);
}
EaglerInitialHandler initialHandler = (EaglerInitialHandler) initiator.getPendingConnection(); EaglerInitialHandler initialHandler = (EaglerInitialHandler) initiator.getPendingConnection();
initialHandler.sendEaglerMessage(skin.data.get(initialHandler.getEaglerProtocol())); initialHandler.sendEaglerMessage(skin.data.get(initialHandler.getEaglerProtocol()));
}else { }else {
@ -732,10 +706,7 @@ public class SkinService implements ISkinService {
@Override @Override
public void accept(CacheFetchedProfile t) { public void accept(CacheFetchedProfile t) {
if(t == null || t.texture == null) { if(t == null || t.texture == null) {
CachedForeignSkin skin; CachedForeignSkin skin = foreignSkinCache.get(playerUUID);
synchronized(foreignSkinCache) {
skin = foreignSkinCache.get(playerUUID);
}
if(skin != null) { if(skin != null) {
EaglerInitialHandler initialHandler = (EaglerInitialHandler) initiator.getPendingConnection(); EaglerInitialHandler initialHandler = (EaglerInitialHandler) initiator.getPendingConnection();
initialHandler.sendEaglerMessage(skin.data.get(initialHandler.getEaglerProtocol())); initialHandler.sendEaglerMessage(skin.data.get(initialHandler.getEaglerProtocol()));
@ -768,9 +739,7 @@ public class SkinService implements ISkinService {
SkinPackets.getModelId(t.model) == 1 ? 1 : 0), SkinPackets.getModelId(t.model) == 1 ? 1 : 0),
-1); -1);
} }
synchronized(foreignSkinCache) { foreignSkinCache.put(playerUUID, skin);
foreignSkinCache.put(playerUUID, skin);
}
EaglerInitialHandler initialHandler = (EaglerInitialHandler) initiator.getPendingConnection(); EaglerInitialHandler initialHandler = (EaglerInitialHandler) initiator.getPendingConnection();
initialHandler.sendEaglerMessage(skin.data.get(initialHandler.getEaglerProtocol())); initialHandler.sendEaglerMessage(skin.data.get(initialHandler.getEaglerProtocol()));
}else { }else {
@ -791,27 +760,16 @@ public class SkinService implements ISkinService {
} }
public void registerEaglercraftPlayer(UUID clientUUID, SkinPacketVersionCache generatedPacket, int modelId) { public void registerEaglercraftPlayer(UUID clientUUID, SkinPacketVersionCache generatedPacket, int modelId) {
synchronized(foreignSkinCache) { foreignSkinCache.remove(clientUUID);
foreignSkinCache.remove(clientUUID); onlinePlayersCache.put(clientUUID, new CachedPlayerSkin(generatedPacket, null, modelId));
}
synchronized(onlinePlayersCache) {
onlinePlayersCache.put(clientUUID, new CachedPlayerSkin(generatedPacket, null, modelId));
}
} }
public void unregisterPlayer(UUID clientUUID) { public void unregisterPlayer(UUID clientUUID) {
CachedPlayerSkin data; CachedPlayerSkin data = onlinePlayersCache.remove(clientUUID);
synchronized(onlinePlayersCache) {
data = onlinePlayersCache.remove(clientUUID);
}
if(data != null) { 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) { 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); deletePlayerTextureAssociation(clientUUID, data.textureUUID);
}else { }else {
@ -821,94 +779,77 @@ public class SkinService implements ISkinService {
private void deletePlayerTextureAssociation(UUID clientUUID, UUID textureUUID) { private void deletePlayerTextureAssociation(UUID clientUUID, UUID textureUUID) {
if(textureUUID != null) { if(textureUUID != null) {
synchronized(onlinePlayersToTexturesMap) { onlinePlayersToTexturesMap.remove(clientUUID);
onlinePlayersToTexturesMap.remove(clientUUID); onlinePlayersFromTexturesMapLock.writeLock().lock();
} try {
synchronized(onlinePlayersFromTexturesMap) {
onlinePlayersFromTexturesMap.remove(textureUUID, clientUUID); onlinePlayersFromTexturesMap.remove(textureUUID, clientUUID);
}finally {
onlinePlayersFromTexturesMapLock.writeLock().unlock();
} }
}else { }else {
UUID removedUUID; UUID removedUUID = onlinePlayersToTexturesMap.remove(clientUUID);
synchronized(onlinePlayersToTexturesMap) {
removedUUID = onlinePlayersToTexturesMap.remove(clientUUID);
}
if(removedUUID != null) { if(removedUUID != null) {
synchronized(onlinePlayersFromTexturesMap) { onlinePlayersFromTexturesMapLock.writeLock().lock();
try {
onlinePlayersFromTexturesMap.remove(removedUUID, clientUUID); onlinePlayersFromTexturesMap.remove(removedUUID, clientUUID);
}finally {
onlinePlayersFromTexturesMapLock.writeLock().unlock();
} }
} }
} }
} }
public void registerTextureToPlayerAssociation(UUID textureUUID, UUID playerUUID) { public void registerTextureToPlayerAssociation(UUID textureUUID, UUID playerUUID) {
synchronized(onlinePlayersFromTexturesMap) { onlinePlayersFromTexturesMapLock.writeLock().lock();
try {
onlinePlayersFromTexturesMap.put(textureUUID, playerUUID); onlinePlayersFromTexturesMap.put(textureUUID, playerUUID);
}finally {
onlinePlayersFromTexturesMapLock.writeLock().unlock();
} }
synchronized(onlinePlayersToTexturesMap) { onlinePlayersToTexturesMap.put(playerUUID, textureUUID);
onlinePlayersToTexturesMap.put(playerUUID, textureUUID); CachedForeignSkin foreign = foreignSkinCache.remove(textureUUID);
}
CachedForeignSkin foreign;
synchronized(foreignSkinCache) {
foreign = foreignSkinCache.remove(textureUUID);
}
if(foreign != null) { 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, EaglerInitialHandler eaglerHandler) { public void processForceSkin(UUID playerUUID, EaglerInitialHandler eaglerHandler) {
CachedPlayerSkin maybeCachedPacket; CachedPlayerSkin maybeCachedPacket = onlinePlayersCache.get(playerUUID);
synchronized(onlinePlayersCache) {
maybeCachedPacket = onlinePlayersCache.get(playerUUID);
}
if(maybeCachedPacket != null) { if(maybeCachedPacket != null) {
eaglerHandler.sendEaglerMessage(maybeCachedPacket.data.getForceClientV4()); eaglerHandler.sendEaglerMessage(maybeCachedPacket.data.getForceClientV4());
}else { }else {
UUID playerTexture; UUID playerTexture = onlinePlayersToTexturesMap.get(playerUUID);
synchronized(onlinePlayersToTexturesMap) {
playerTexture = onlinePlayersToTexturesMap.get(playerUUID);
}
if(playerTexture != null) { if(playerTexture != null) {
Collection<UUID> possiblePlayers; Collection<UUID> possiblePlayers;
synchronized(onlinePlayersFromTexturesMap) { onlinePlayersFromTexturesMapLock.readLock().lock();
possiblePlayers = onlinePlayersFromTexturesMap.get(playerTexture); try {
possiblePlayers = new ArrayList<>(onlinePlayersFromTexturesMap.get(playerTexture));
}finally {
onlinePlayersFromTexturesMapLock.readLock().unlock();
} }
boolean playersExist = possiblePlayers.size() > 0; boolean playersExist = possiblePlayers.size() > 0;
if(playersExist) { if(playersExist) {
for(UUID uuid : possiblePlayers) { for(UUID uuid : possiblePlayers) {
synchronized(onlinePlayersCache) { maybeCachedPacket = onlinePlayersCache.get(uuid);
maybeCachedPacket = onlinePlayersCache.get(uuid);
}
if(maybeCachedPacket != null) { if(maybeCachedPacket != null) {
SkinPacketVersionCache rewritten = SkinPacketVersionCache.rewriteUUID( SkinPacketVersionCache rewritten = SkinPacketVersionCache.rewriteUUID(
maybeCachedPacket.data, playerUUID.getMostSignificantBits(), maybeCachedPacket.data, playerUUID.getMostSignificantBits(),
playerUUID.getLeastSignificantBits()); playerUUID.getLeastSignificantBits());
synchronized(onlinePlayersCache) { onlinePlayersCache.put(playerUUID, new CachedPlayerSkin(rewritten,
onlinePlayersCache.put(playerUUID, new CachedPlayerSkin(rewritten, maybeCachedPacket.textureUUID, maybeCachedPacket.modelId));
maybeCachedPacket.textureUUID, maybeCachedPacket.modelId));
}
eaglerHandler.sendEaglerMessage(rewritten.getForceClientV4()); eaglerHandler.sendEaglerMessage(rewritten.getForceClientV4());
return; return;
} }
} }
} }
CachedForeignSkin foreignSkin; CachedForeignSkin foreignSkin = foreignSkinCache.get(playerTexture);
synchronized(foreignSkinCache) {
foreignSkin = foreignSkinCache.get(playerTexture);
}
if(foreignSkin != null && foreignSkin.modelKnown != -1) { if(foreignSkin != null && foreignSkin.modelKnown != -1) {
synchronized(onlinePlayersCache) { onlinePlayersCache.put(playerUUID,
onlinePlayersCache.put(playerUUID, new CachedPlayerSkin(SkinPacketVersionCache.rewriteUUID(foreignSkin.data,
new CachedPlayerSkin(SkinPacketVersionCache.rewriteUUID(foreignSkin.data, playerUUID.getMostSignificantBits(), playerUUID.getLeastSignificantBits()),
playerUUID.getMostSignificantBits(), playerUUID.getLeastSignificantBits()), playerTexture, foreignSkin.modelKnown));
playerTexture, foreignSkin.modelKnown)); foreignSkinCache.remove(playerTexture);
}
synchronized(foreignSkinCache) {
foreignSkinCache.remove(playerTexture);
}
eaglerHandler.sendEaglerMessage(foreignSkin.data.getForceClientV4()); eaglerHandler.sendEaglerMessage(foreignSkin.data.getForceClientV4());
return; return;
} }
@ -927,7 +868,7 @@ public class SkinService implements ISkinService {
if(skinObj != null) { if(skinObj != null) {
JsonElement url = json.get("url"); JsonElement url = json.get("url");
if(url != null) { if(url != null) {
String urlStr = SkinService.sanitizeTextureURL(url.getAsString()); String urlStr = sanitizeTextureURL(url.getAsString());
if(urlStr == null) { if(urlStr == null) {
break; break;
} }
@ -941,19 +882,14 @@ public class SkinService implements ISkinService {
} }
UUID skinUUID = SkinPackets.createEaglerURLSkinUUID(urlStr); UUID skinUUID = SkinPackets.createEaglerURLSkinUUID(urlStr);
CachedForeignSkin foreignSkin; CachedForeignSkin foreignSkin = foreignSkinCache.remove(skinUUID);
synchronized(foreignSkinCache) {
foreignSkin = foreignSkinCache.remove(skinUUID);
}
if(foreignSkin != null) { if(foreignSkin != null) {
registerTextureToPlayerAssociation(skinUUID, playerUUID); registerTextureToPlayerAssociation(skinUUID, playerUUID);
SkinPacketVersionCache rewrite = SkinPacketVersionCache SkinPacketVersionCache rewrite = SkinPacketVersionCache
.rewriteUUIDModel(foreignSkin.data, .rewriteUUIDModel(foreignSkin.data,
playerUUID.getMostSignificantBits(), playerUUID.getMostSignificantBits(),
playerUUID.getLeastSignificantBits(), model); 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()); eaglerHandler.sendEaglerMessage(rewrite.getForceClientV4());
return; return;
} }
@ -981,10 +917,7 @@ public class SkinService implements ISkinService {
} }
}); });
}else { }else {
CachedForeignSkin foreignSkin; CachedForeignSkin foreignSkin = foreignSkinCache.get(playerUUID);
synchronized(foreignSkinCache) {
foreignSkin = foreignSkinCache.get(playerUUID);
}
if(foreignSkin != null) { if(foreignSkin != null) {
foreignSkin.lastHit = EaglerXBungeeAPIHelper.steadyTimeMillis(); foreignSkin.lastHit = EaglerXBungeeAPIHelper.steadyTimeMillis();
eaglerHandler.sendEaglerMessage(foreignSkin.data.getForceClientV4()); eaglerHandler.sendEaglerMessage(foreignSkin.data.getForceClientV4());
@ -1011,10 +944,7 @@ public class SkinService implements ISkinService {
@Override @Override
public void accept(byte[] t) { public void accept(byte[] t) {
CachedPlayerSkin skin; CachedPlayerSkin skin = onlinePlayersCache.get(onlineCacheUUID);
synchronized(onlinePlayersCache) {
skin = onlinePlayersCache.get(onlineCacheUUID);
}
if(skin != null) { if(skin != null) {
initiator.sendEaglerMessage(skin.data.getForceClientV4()); initiator.sendEaglerMessage(skin.data.getForceClientV4());
} }
@ -1041,9 +971,7 @@ public class SkinService implements ISkinService {
onlineCacheUUID.getMostSignificantBits(), onlineCacheUUID.getMostSignificantBits(),
onlineCacheUUID.getLeastSignificantBits()), null, -1); onlineCacheUUID.getLeastSignificantBits()), null, -1);
} }
synchronized (onlinePlayersCache) { onlinePlayersCache.put(onlineCacheUUID, skin);
onlinePlayersCache.put(onlineCacheUUID, skin);
}
initiator.sendEaglerMessage(skin.data.getForceClientV4()); initiator.sendEaglerMessage(skin.data.getForceClientV4());
} }
@ -1068,10 +996,7 @@ public class SkinService implements ISkinService {
@Override @Override
public void accept(byte[] t) { public void accept(byte[] t) {
CachedForeignSkin skin; CachedForeignSkin skin = foreignSkinCache.get(foreignCacheUUID);
synchronized(foreignSkinCache) {
skin = foreignSkinCache.get(foreignCacheUUID);
}
if(skin != null) { if(skin != null) {
initiator.sendEaglerMessage(skin.data.getForceClientV4()); initiator.sendEaglerMessage(skin.data.getForceClientV4());
} }
@ -1099,9 +1024,7 @@ public class SkinService implements ISkinService {
foreignCacheUUID.getLeastSignificantBits()), foreignCacheUUID.getLeastSignificantBits()),
-1); -1);
} }
synchronized (foreignSkinCache) { foreignSkinCache.put(foreignCacheUUID, skin);
foreignSkinCache.put(foreignCacheUUID, skin);
}
initiator.sendEaglerMessage(skin.data.getForceClientV4()); initiator.sendEaglerMessage(skin.data.getForceClientV4());
} }
@ -1126,10 +1049,7 @@ public class SkinService implements ISkinService {
@Override @Override
public void accept(CacheFetchedProfile t) { public void accept(CacheFetchedProfile t) {
if(t == null || t.texture == null) { if(t == null || t.texture == null) {
CachedPlayerSkin skin; CachedPlayerSkin skin = onlinePlayersCache.get(playerUUID);
synchronized(onlinePlayersCache) {
skin = onlinePlayersCache.get(playerUUID);
}
if(skin != null) { if(skin != null) {
initiator.sendEaglerMessage(skin.data.getForceClientV4()); initiator.sendEaglerMessage(skin.data.getForceClientV4());
} }
@ -1161,9 +1081,7 @@ public class SkinService implements ISkinService {
SkinPackets.getModelId(t.model) == 1 ? 1 : 0), SkinPackets.getModelId(t.model) == 1 ? 1 : 0),
null, -1); null, -1);
} }
synchronized(onlinePlayersCache) { onlinePlayersCache.put(playerUUID, skin);
onlinePlayersCache.put(playerUUID, skin);
}
initiator.sendEaglerMessage(skin.data.getForceClientV4()); initiator.sendEaglerMessage(skin.data.getForceClientV4());
}else { }else {
processResolveURLTextureForOnlineToForce(initiator, playerUUID, t.textureUUID, t.texture, processResolveURLTextureForOnlineToForce(initiator, playerUUID, t.textureUUID, t.texture,
@ -1192,10 +1110,7 @@ public class SkinService implements ISkinService {
@Override @Override
public void accept(CacheFetchedProfile t) { public void accept(CacheFetchedProfile t) {
if(t == null || t.texture == null) { if(t == null || t.texture == null) {
CachedPlayerSkin skin; CachedPlayerSkin skin = onlinePlayersCache.get(t.uuid);
synchronized(onlinePlayersCache) {
skin = onlinePlayersCache.get(t.uuid);
}
if(skin != null) { if(skin != null) {
initiator.sendEaglerMessage(skin.data.getForceClientV4()); initiator.sendEaglerMessage(skin.data.getForceClientV4());
} }
@ -1225,9 +1140,7 @@ public class SkinService implements ISkinService {
mapUUID.getMostSignificantBits(), mapUUID.getLeastSignificantBits(), mapUUID.getMostSignificantBits(), mapUUID.getLeastSignificantBits(),
SkinPackets.getModelId(t.model) == 1 ? 1 : 0), null, -1); SkinPackets.getModelId(t.model) == 1 ? 1 : 0), null, -1);
} }
synchronized(onlinePlayersCache) { onlinePlayersCache.put(mapUUID, skin);
onlinePlayersCache.put(mapUUID, skin);
}
initiator.sendEaglerMessage(skin.data.getForceClientV4()); initiator.sendEaglerMessage(skin.data.getForceClientV4());
}else { }else {
processResolveURLTextureForOnlineToForce(initiator, mapUUID, t.textureUUID, t.texture, processResolveURLTextureForOnlineToForce(initiator, mapUUID, t.textureUUID, t.texture,
@ -1256,10 +1169,7 @@ public class SkinService implements ISkinService {
@Override @Override
public void accept(CacheFetchedProfile t) { public void accept(CacheFetchedProfile t) {
if(t == null || t.texture == null) { if(t == null || t.texture == null) {
CachedForeignSkin skin; CachedForeignSkin skin = foreignSkinCache.get(playerUUID);
synchronized(foreignSkinCache) {
skin = foreignSkinCache.get(playerUUID);
}
if(skin != null) { if(skin != null) {
initiator.sendEaglerMessage(skin.data.getForceClientV4()); initiator.sendEaglerMessage(skin.data.getForceClientV4());
} }
@ -1291,9 +1201,7 @@ public class SkinService implements ISkinService {
SkinPackets.getModelId(t.model) == 1 ? 1 : 0), SkinPackets.getModelId(t.model) == 1 ? 1 : 0),
-1); -1);
} }
synchronized(foreignSkinCache) { foreignSkinCache.put(playerUUID, skin);
foreignSkinCache.put(playerUUID, skin);
}
initiator.sendEaglerMessage(skin.data.getForceClientV4()); initiator.sendEaglerMessage(skin.data.getForceClientV4());
}else { }else {
processResolveURLTextureForForeignToForce(initiator, playerUUID, t.textureUUID, t.texture, processResolveURLTextureForForeignToForce(initiator, playerUUID, t.textureUUID, t.texture,
@ -1315,12 +1223,16 @@ public class SkinService implements ISkinService {
public void flush() { public void flush() {
long millis = EaglerXBungeeAPIHelper.steadyTimeMillis(); long millis = EaglerXBungeeAPIHelper.steadyTimeMillis();
synchronized(foreignSkinCache) { final List<UUID> foreignSkinCleanup = new ArrayList<>(4);
Iterator<CachedForeignSkin> itr = foreignSkinCache.values().iterator(); foreignSkinCache.entrySet().forEach((etr) -> {
while(itr.hasNext()) { if(millis - etr.getValue().lastHit > 900000l) { // 15 minutes
if(millis - itr.next().lastHit > 900000l) { // 15 minutes foreignSkinCleanup.add(etr.getKey());
itr.remove(); }
} });
if(!foreignSkinCleanup.isEmpty()) {
for(UUID uuid : foreignSkinCleanup) {
foreignSkinCache.remove(uuid);
} }
} }
@ -1371,7 +1283,8 @@ public class SkinService implements ISkinService {
elapsedCooldown /= cooldownPeriod; elapsedCooldown /= cooldownPeriod;
if(elapsedCooldown > 0) { if(elapsedCooldown > 0) {
antagonistCooldown += elapsedCooldown * cooldownPeriod; antagonistCooldown += elapsedCooldown * cooldownPeriod;
synchronized(antagonists) { antagonistsLock.writeLock().lock();
try {
Iterator<UUID> itr = antagonists.keySet().iterator(); Iterator<UUID> itr = antagonists.keySet().iterator();
while(itr.hasNext()) { while(itr.hasNext()) {
UUID key = itr.next(); UUID key = itr.next();
@ -1382,6 +1295,8 @@ public class SkinService implements ISkinService {
antagonists.put(key, i); antagonists.put(key, i);
} }
} }
}finally {
antagonistsLock.writeLock().unlock();
} }
} }
@ -1389,10 +1304,7 @@ public class SkinService implements ISkinService {
} }
public SkinPacketVersionCache getSkin(UUID playerUUID) { public SkinPacketVersionCache getSkin(UUID playerUUID) {
CachedPlayerSkin skin; CachedPlayerSkin skin = onlinePlayersCache.get(playerUUID);
synchronized(onlinePlayersCache) {
skin = onlinePlayersCache.get(playerUUID);
}
return skin != null ? skin.data : null; return skin != null ? skin.data : null;
} }
@ -1407,25 +1319,26 @@ public class SkinService implements ISkinService {
private boolean isLimitedAsAntagonist(UUID uuid) { private boolean isLimitedAsAntagonist(UUID uuid) {
int limit = EaglerXBungee.getEagler().getConfig().getAntagonistsRateLimit(); int limit = EaglerXBungee.getEagler().getConfig().getAntagonistsRateLimit();
limit += limit >> 1; limit += limit >> 1;
synchronized(antagonists) { int i;
int i = antagonists.get(uuid); antagonistsLock.readLock().lock();
return i != antagonists.getNoEntryValue() && i > limit; try {
i = antagonists.get(uuid);
}finally {
antagonistsLock.readLock().unlock();
} }
return i != antagonists.getNoEntryValue() && i > limit;
} }
private void resetMaps() { private void resetMaps() {
synchronized(onlinePlayersCache) { onlinePlayersCache.clear();
onlinePlayersCache.clear(); onlinePlayersFromTexturesMapLock.writeLock().lock();
} try {
synchronized(onlinePlayersFromTexturesMap) {
onlinePlayersFromTexturesMap.clear(); onlinePlayersFromTexturesMap.clear();
}finally {
onlinePlayersFromTexturesMapLock.writeLock().unlock();
} }
synchronized(onlinePlayersToTexturesMap) { onlinePlayersToTexturesMap.clear();
onlinePlayersToTexturesMap.clear(); foreignSkinCache.clear();
}
synchronized(foreignSkinCache) {
foreignSkinCache.clear();
}
synchronized(pendingTextures) { synchronized(pendingTextures) {
pendingTextures.clear(); pendingTextures.clear();
} }
@ -1435,8 +1348,11 @@ public class SkinService implements ISkinService {
synchronized(pendingNameLookups) { synchronized(pendingNameLookups) {
pendingNameLookups.clear(); pendingNameLookups.clear();
} }
synchronized(antagonists) { antagonistsLock.writeLock().lock();
try {
antagonists.clear(); antagonists.clear();
}finally {
antagonistsLock.writeLock().unlock();
} }
} }
@ -1453,7 +1369,7 @@ public class SkinService implements ISkinService {
return null; return null;
} }
String host = uri.getHost(); String host = uri.getHost();
if(host == null) { if(host == null || !EaglerXBungee.getEagler().getConfig().isValidSkinHost(host)) {
return null; return null;
} }
scheme = scheme.toLowerCase(); scheme = scheme.toLowerCase();

View File

@ -1,13 +1,8 @@
package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins; package net.lax1dude.eaglercraft.v1_8.plugin.gateway_bungeecord.skins;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import com.google.common.collect.Multimap; import java.util.concurrent.ConcurrentMap;
import com.google.common.collect.MultimapBuilder;
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.socket.protocol.pkt.server.SPacketOtherSkinPresetEAG; import net.lax1dude.eaglercraft.v1_8.socket.protocol.pkt.server.SPacketOtherSkinPresetEAG;
@ -15,7 +10,7 @@ import net.lax1dude.eaglercraft.v1_8.socket.protocol.util.SkinPacketVersionCache
import net.md_5.bungee.UserConnection; import net.md_5.bungee.UserConnection;
/** /**
* 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 * 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
@ -45,24 +40,17 @@ public class SkinServiceOffline implements ISkinService {
} }
private final Map<UUID, CachedSkin> skinCache = new HashMap<>(); private final ConcurrentMap<UUID, CachedSkin> skinCache = new ConcurrentHashMap<>();
private final Multimap<UUID, UUID> onlinePlayersFromTexturesMap = MultimapBuilder.hashKeys().hashSetValues().build();
public void init(String uri, String driverClass, String driverPath, int keepObjectsDays, int keepProfilesDays, public void init(String uri, String driverClass, String driverPath, int keepObjectsDays, int keepProfilesDays,
int maxObjects, int maxProfiles) { int maxObjects, int maxProfiles) {
synchronized(skinCache) { skinCache.clear();
skinCache.clear();
}
} }
public void processGetOtherSkin(UUID searchUUID, UserConnection sender) { public void processGetOtherSkin(UUID searchUUID, UserConnection sender) {
EaglerInitialHandler initialHandler = (EaglerInitialHandler)sender.getPendingConnection(); EaglerInitialHandler initialHandler = (EaglerInitialHandler)sender.getPendingConnection();
if(initialHandler.skinLookupRateLimiter.rateLimit(masterRateLimitPerPlayer)) { if(initialHandler.skinLookupRateLimiter.rateLimit(masterRateLimitPerPlayer)) {
CachedSkin cached; CachedSkin cached = skinCache.get(searchUUID);
synchronized(skinCache) {
cached = skinCache.get(searchUUID);
}
if(cached != null) { if(cached != null) {
initialHandler.sendEaglerMessage(cached.packet.get(initialHandler.getEaglerProtocol())); initialHandler.sendEaglerMessage(cached.packet.get(initialHandler.getEaglerProtocol()));
}else { }else {
@ -74,24 +62,6 @@ public class SkinServiceOffline implements ISkinService {
public void processGetOtherSkin(UUID searchUUID, String skinURL, UserConnection sender) { public void processGetOtherSkin(UUID searchUUID, String skinURL, UserConnection sender) {
EaglerInitialHandler initialHandler = (EaglerInitialHandler)sender.getPendingConnection(); EaglerInitialHandler initialHandler = (EaglerInitialHandler)sender.getPendingConnection();
Collection<UUID> uuids;
synchronized(onlinePlayersFromTexturesMap) {
uuids = onlinePlayersFromTexturesMap.get(searchUUID);
}
if(uuids.size() > 0) {
CachedSkin cached;
synchronized(skinCache) {
Iterator<UUID> uuidItr = uuids.iterator();
while(uuidItr.hasNext()) {
cached = skinCache.get(uuidItr.next());
if(cached != null) {
initialHandler.sendEaglerMessage(cached.packet.get(initialHandler.getEaglerProtocol(),
searchUUID.getMostSignificantBits(), searchUUID.getLeastSignificantBits()));
return;
}
}
}
}
if(skinURL.startsWith("eagler://")) { // customs skulls from exported singleplayer worlds if(skinURL.startsWith("eagler://")) { // customs skulls from exported singleplayer worlds
initialHandler.sendEaglerMessage(new SPacketOtherSkinPresetEAG(searchUUID.getMostSignificantBits(), initialHandler.sendEaglerMessage(new SPacketOtherSkinPresetEAG(searchUUID.getMostSignificantBits(),
searchUUID.getLeastSignificantBits(), 0)); searchUUID.getLeastSignificantBits(), 0));
@ -102,28 +72,21 @@ public class SkinServiceOffline implements ISkinService {
} }
public void registerEaglercraftPlayer(UUID clientUUID, SkinPacketVersionCache generatedPacket, int modelId) { 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) { 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) { public void registerTextureToPlayerAssociation(UUID textureUUID, UUID playerUUID) {
synchronized(onlinePlayersFromTexturesMap) {
onlinePlayersFromTexturesMap.put(textureUUID, playerUUID);
}
} }
public void processForceSkin(UUID playerUUID, EaglerInitialHandler initialHandler) { public void processForceSkin(UUID playerUUID, EaglerInitialHandler initialHandler) {
CachedSkin cached; CachedSkin cached = skinCache.get(playerUUID);
synchronized(skinCache) {
cached = skinCache.get(playerUUID);
}
if(cached != null) { if(cached != null) {
initialHandler.sendEaglerMessage(cached.packet.getForceClientV4()); initialHandler.sendEaglerMessage(cached.packet.getForceClientV4());
} }
@ -134,16 +97,11 @@ public class SkinServiceOffline implements ISkinService {
} }
public void shutdown() { public void shutdown() {
synchronized(skinCache) { skinCache.clear();
skinCache.clear();
}
} }
public SkinPacketVersionCache getSkin(UUID playerUUID) { public SkinPacketVersionCache getSkin(UUID playerUUID) {
CachedSkin cached; CachedSkin cached = skinCache.get(playerUUID);
synchronized(skinCache) {
cached = skinCache.get(playerUUID);
}
return cached != null ? cached.packet : null; return cached != null ? cached.packet : null;
} }

View File

@ -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.3.4 version: 1.3.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 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

View File

@ -1 +1 @@
1.3.4 1.3.5