-
-
Save Elfocrash/644d99e27cfb798220340aadffd684e6 to your computer and use it in GitHub Desktop.
The Java side diff for L2Proxy 0.2.0
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/config/loginserver.properties b/config/loginserver.properties | |
index cf75ea2e..51331654 100644 | |
--- a/config/loginserver.properties | |
+++ b/config/loginserver.properties | |
@@ -53,4 +53,20 @@ EnableFloodProtection = True | |
FastConnectionLimit = 15 | |
NormalConnectionTime = 700 | |
FastConnectionTime = 350 | |
-MaxConnectionPerIP = 50 | |
\ No newline at end of file | |
+MaxConnectionPerIP = 50 | |
+ | |
+# ================================================================= | |
+# Threadpool | |
+# ================================================================= | |
+ | |
+# Determines the amount of scheduled thread pools. If set to -1, the server will decide the amount depending on the available processors. | |
+ScheduledThreadPoolCount = -1 | |
+ | |
+# Specifies how many threads will be in a single scheduled pool. | |
+ThreadsPerScheduledThreadPool = 4 | |
+ | |
+# Determines the amount of instant thread pools. If set to -1, the server will decide the amount depending on the available processors. | |
+InstantThreadPoolCount = -1 | |
+ | |
+# Specifies how many threads will be in a single instant pool. | |
+ThreadsPerInstantThreadPool = 2 | |
\ No newline at end of file | |
diff --git a/data/xml/proxy.xml b/data/xml/proxy.xml | |
new file mode 100644 | |
index 00000000..196df95d | |
--- /dev/null | |
+++ b/data/xml/proxy.xml | |
@@ -0,0 +1,18 @@ | |
+<?xml version='1.0' encoding='utf-8'?> | |
+<list> | |
+ <config /> | |
+ <!-- | |
+ serverId: The true id of the gameserver | |
+ hide: When enabled, the true gameserver will not appear in the server list | |
+ fallbackToGameserver: When the proxy server is down, when true, the real gameserver will appear | |
+ proxyServerId: The gameserver id that the proxy will use to be listed in the server list | |
+ proxyHost: The host of the proxy | |
+ proxyPort: The port of the proxy | |
+ apiPort: The port of the API for the proxy | |
+ apiKey: The api key for the proxy api | |
+ --> | |
+ <gameserver serverId="1" hide="true" fallbackToGameserver="false"> | |
+ <proxy proxyServerId="2" proxyHost="127.0.0.1" proxyPort="7778" validateHealth="true" apiPort="6969" apiKey="changeit"/> | |
+ <proxy proxyServerId="3" proxyHost="127.0.0.1" proxyPort="7779" validateHealth="true" apiPort="6969" apiKey="changeit"/> | |
+ </gameserver> | |
+</list> | |
\ No newline at end of file | |
diff --git a/java/net/sf/l2j/Config.java b/java/net/sf/l2j/Config.java | |
index dcbc2f0a..ae800d8b 100644 | |
--- a/java/net/sf/l2j/Config.java | |
+++ b/java/net/sf/l2j/Config.java | |
@@ -1266,6 +1266,11 @@ private static final void loadLogin() | |
NORMAL_CONNECTION_TIME = server.getProperty("NormalConnectionTime", 700); | |
FAST_CONNECTION_TIME = server.getProperty("FastConnectionTime", 350); | |
MAX_CONNECTION_PER_IP = server.getProperty("MaxConnectionPerIP", 50); | |
+ | |
+ SCHEDULED_THREAD_POOL_COUNT = server.getProperty("ScheduledThreadPoolCount", -1); | |
+ THREADS_PER_SCHEDULED_THREAD_POOL = server.getProperty("ThreadsPerScheduledThreadPool", 4); | |
+ INSTANT_THREAD_POOL_COUNT = server.getProperty("InstantThreadPoolCount", -1); | |
+ THREADS_PER_INSTANT_THREAD_POOL = server.getProperty("ThreadsPerInstantThreadPool", 2); | |
} | |
public static final void loadGameServer() | |
diff --git a/java/net/sf/l2j/gameserver/LoginServerThread.java b/java/net/sf/l2j/gameserver/LoginServerThread.java | |
index 141b2c72..48a51b99 100644 | |
--- a/java/net/sf/l2j/gameserver/LoginServerThread.java | |
+++ b/java/net/sf/l2j/gameserver/LoginServerThread.java | |
@@ -219,6 +219,7 @@ public void run() | |
final GameClient client = _clients.get(par.getAccount()); | |
if (client != null) | |
{ | |
+ client.setRealIpAddress(par.getRealIpAddress()); | |
if (par.isAuthed()) | |
{ | |
sendPacket(new PlayerInGame(par.getAccount())); | |
diff --git a/java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminEditChar.java b/java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminEditChar.java | |
index 25fc6bf6..9014dc4c 100644 | |
--- a/java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminEditChar.java | |
+++ b/java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminEditChar.java | |
@@ -571,7 +571,7 @@ public static void gatherPlayerInfo(Player player, Player targetPlayer, NpcHtmlM | |
html.replace("%nextai%", targetPlayer.getAI().getNextIntention().getType().toString()); | |
html.replace("%loc%", targetPlayer.getPosition().toString()); | |
html.replace("%account%", targetPlayer.getAccountName()); | |
- html.replace("%ip%", (targetPlayer.getClient().isDetached()) ? "Disconnected" : targetPlayer.getClient().getConnection().getInetAddress().getHostAddress()); | |
+ html.replace("%ip%", (targetPlayer.getClient().isDetached()) ? "Disconnected" : targetPlayer.getClient().getRealIpAddress()); | |
} | |
/** | |
diff --git a/java/net/sf/l2j/gameserver/network/GameClient.java b/java/net/sf/l2j/gameserver/network/GameClient.java | |
index 72499d03..349c893a 100644 | |
--- a/java/net/sf/l2j/gameserver/network/GameClient.java | |
+++ b/java/net/sf/l2j/gameserver/network/GameClient.java | |
@@ -60,8 +60,8 @@ | |
private static final String DELETE_CHAR_ITEMS = "DELETE FROM items WHERE owner_id=?"; | |
private static final String DELETE_CHAR_RBP = "DELETE FROM character_raid_points WHERE char_id=?"; | |
private static final String DELETE_CHAR = "DELETE FROM characters WHERE obj_Id=?"; | |
- | |
- public enum GameClientState | |
+ | |
+ public enum GameClientState | |
{ | |
CONNECTED, // client has just connected | |
AUTHED, // client has authed but doesnt has character attached to it yet | |
@@ -90,6 +90,7 @@ | |
protected final ScheduledFuture<?> _autoSaveInDB; | |
protected ScheduledFuture<?> _cleanupTask; | |
+ private String _realIpAddress; | |
public GameClient(MMOConnection<GameClient> con) | |
{ | |
@@ -300,6 +301,14 @@ public SessionKey getSessionId() | |
{ | |
return _sessionId; | |
} | |
+ | |
+ public String getRealIpAddress() { | |
+ return _realIpAddress; | |
+ } | |
+ | |
+ public void setRealIpAddress(String realIpAddress) { | |
+ _realIpAddress = realIpAddress; | |
+ } | |
public void sendPacket(L2GameServerPacket gsp) | |
{ | |
diff --git a/java/net/sf/l2j/gameserver/network/loginserverpackets/PlayerAuthResponse.java b/java/net/sf/l2j/gameserver/network/loginserverpackets/PlayerAuthResponse.java | |
index 96ffbcf4..059cb7ed 100644 | |
--- a/java/net/sf/l2j/gameserver/network/loginserverpackets/PlayerAuthResponse.java | |
+++ b/java/net/sf/l2j/gameserver/network/loginserverpackets/PlayerAuthResponse.java | |
@@ -4,6 +4,7 @@ | |
{ | |
private final String _account; | |
private final boolean _authed; | |
+ private final String _realIpAddress; | |
public PlayerAuthResponse(byte[] decrypt) | |
{ | |
@@ -11,6 +12,7 @@ public PlayerAuthResponse(byte[] decrypt) | |
_account = readS(); | |
_authed = readC() != 0; | |
+ _realIpAddress = readS(); | |
} | |
public String getAccount() | |
@@ -22,4 +24,8 @@ public boolean isAuthed() | |
{ | |
return _authed; | |
} | |
+ | |
+ public String getRealIpAddress() { | |
+ return _realIpAddress; | |
+ } | |
} | |
\ No newline at end of file | |
diff --git a/java/net/sf/l2j/loginserver/GameServerThread.java b/java/net/sf/l2j/loginserver/GameServerThread.java | |
index 6959395b..4658c1d7 100644 | |
--- a/java/net/sf/l2j/loginserver/GameServerThread.java | |
+++ b/java/net/sf/l2j/loginserver/GameServerThread.java | |
@@ -249,14 +249,16 @@ private void onReceivePlayerAuthRequest(byte[] data) | |
{ | |
final PlayerAuthRequest par = new PlayerAuthRequest(data); | |
final SessionKey key = LoginController.getInstance().getKeyForAccount(par.getAccount()); | |
+ var authedClient = LoginController.getInstance().getAuthedClient(par.getAccount()); | |
+ var ipAddress = authedClient != null ? authedClient.getConnection().getInetAddress().getHostAddress() : ""; | |
if (key != null && key.equals(par.getKey())) | |
{ | |
LoginController.getInstance().removeAuthedLoginClient(par.getAccount()); | |
- sendPacket(new PlayerAuthResponse(par.getAccount(), true)); | |
+ sendPacket(new PlayerAuthResponse(par.getAccount(), true, ipAddress)); | |
} | |
else | |
- sendPacket(new PlayerAuthResponse(par.getAccount(), false)); | |
+ sendPacket(new PlayerAuthResponse(par.getAccount(), false, ipAddress)); | |
} | |
else | |
forceClose(LoginServerFail.NOT_AUTHED); | |
diff --git a/java/net/sf/l2j/loginserver/LoginServer.java b/java/net/sf/l2j/loginserver/LoginServer.java | |
index 95880e0e..c29ee45d 100644 | |
--- a/java/net/sf/l2j/loginserver/LoginServer.java | |
+++ b/java/net/sf/l2j/loginserver/LoginServer.java | |
@@ -15,9 +15,11 @@ | |
import net.sf.l2j.commons.pool.ConnectionPool; | |
import net.sf.l2j.Config; | |
+import net.sf.l2j.commons.pool.ThreadPool; | |
import net.sf.l2j.loginserver.data.manager.GameServerManager; | |
import net.sf.l2j.loginserver.data.manager.IpBanManager; | |
import net.sf.l2j.loginserver.data.sql.AccountTable; | |
+import net.sf.l2j.loginserver.data.xml.L2ProxyDataLoader; | |
import net.sf.l2j.loginserver.network.LoginClient; | |
import net.sf.l2j.loginserver.network.LoginPacketHandler; | |
@@ -55,6 +57,7 @@ public LoginServer() throws Exception | |
StringUtil.printSection("Poolers"); | |
ConnectionPool.init(); | |
+ ThreadPool.init(); | |
AccountTable.getInstance(); | |
@@ -63,6 +66,9 @@ public LoginServer() throws Exception | |
StringUtil.printSection("GameServerManager"); | |
GameServerManager.getInstance(); | |
+ | |
+ StringUtil.printSection("L2Proxy"); | |
+ L2ProxyDataLoader.getInstance().load(); | |
StringUtil.printSection("Ban List"); | |
IpBanManager.getInstance(); | |
diff --git a/java/net/sf/l2j/loginserver/data/manager/L2ProxyManager.java b/java/net/sf/l2j/loginserver/data/manager/L2ProxyManager.java | |
new file mode 100644 | |
index 00000000..a07393bf | |
--- /dev/null | |
+++ b/java/net/sf/l2j/loginserver/data/manager/L2ProxyManager.java | |
@@ -0,0 +1,86 @@ | |
+package net.sf.l2j.loginserver.data.manager; | |
+ | |
+import net.sf.l2j.commons.logging.CLogger; | |
+import net.sf.l2j.commons.pool.ThreadPool; | |
+import net.sf.l2j.loginserver.data.xml.L2ProxyDataLoader; | |
+import net.sf.l2j.loginserver.model.L2Proxy; | |
+import net.sf.l2j.loginserver.model.L2ProxyInfo; | |
+import net.sf.l2j.util.PredicateHelpers; | |
+ | |
+import java.io.IOException; | |
+import java.net.URI; | |
+import java.net.http.HttpClient; | |
+import java.net.http.HttpRequest; | |
+import java.net.http.HttpResponse; | |
+import java.time.Duration; | |
+import java.util.Collection; | |
+import java.util.HashMap; | |
+import java.util.Map; | |
+import java.util.concurrent.ScheduledFuture; | |
+import java.util.stream.Collectors; | |
+ | |
+public enum L2ProxyManager { | |
+ INSTANCE; | |
+ | |
+ private final CLogger _logger = new CLogger(L2ProxyDataLoader.class.getName()); | |
+ private ScheduledFuture<?> _statusUpdateTask; | |
+ private Map<Integer, L2ProxyInfo> _proxyInfo = new HashMap<>(); | |
+ private Map<Integer, L2Proxy> _proxyServers = new HashMap<>(); | |
+ private final HttpClient _httpClient = HttpClient.newHttpClient(); | |
+ | |
+ public synchronized void initialise(Map<Integer, L2ProxyInfo> proxyInfoList, Map<Integer, L2Proxy> proxies) { | |
+ _proxyInfo = proxyInfoList; | |
+ _proxyServers = proxies; | |
+ _logger.info("Loaded {} proxy servers.", _proxyServers.size()); | |
+ | |
+ if(_statusUpdateTask != null) { | |
+ _statusUpdateTask.cancel(true); | |
+ } | |
+ _statusUpdateTask = ThreadPool.scheduleAtFixedRate(updateProxyHealthTask(), 0, 10000); | |
+ } | |
+ | |
+ public L2Proxy getProxyById(int proxyId) { | |
+ return _proxyServers.get(proxyId); | |
+ } | |
+ | |
+ public Collection<L2Proxy> getProxies(){ | |
+ return _proxyServers.values(); | |
+ } | |
+ | |
+ public L2ProxyInfo getProxyInfoByGameserverId(int gameserverId) { | |
+ return _proxyInfo.get(gameserverId); | |
+ } | |
+ | |
+ private Runnable updateProxyHealthTask() { | |
+ return () -> { | |
+ | |
+ var distinctProxyHosts = _proxyServers.values().stream() | |
+ .filter(L2Proxy::shouldValidateHealth) | |
+ .filter(PredicateHelpers.distinctByKeys(L2Proxy::getProxyAddress, L2Proxy::getProxyPort)) | |
+ .map(x -> new Object() { | |
+ public final Integer proxyId = x.getProxyServerId(); | |
+ public final String apiHost = x.getProxyAddress().getHostAddress(); | |
+ public final Integer apiPort = x.getApiPort(); | |
+ }).collect(Collectors.toList()); | |
+ | |
+ for (var host : distinctProxyHosts) { | |
+ var request = HttpRequest.newBuilder( | |
+ URI.create(String.format("http://%s:%s/_ping", host.apiHost, host.apiPort))) | |
+ .header("accept", "application/json") | |
+ .timeout(Duration.ofSeconds(5)) | |
+ .build(); | |
+ | |
+ try { | |
+ var response = _httpClient.send(request, HttpResponse.BodyHandlers.ofString()); | |
+ if (response.statusCode() == 200 && response.body().equals("pong")) { | |
+ _proxyServers.get(host.proxyId).setHealthy(true); | |
+ continue; | |
+ } | |
+ _proxyServers.get(host.proxyId).setHealthy(false); | |
+ } catch (IOException | InterruptedException e) { | |
+ _proxyServers.get(host.proxyId).setHealthy(false); | |
+ } | |
+ } | |
+ }; | |
+ } | |
+} | |
\ No newline at end of file | |
diff --git a/java/net/sf/l2j/loginserver/data/xml/L2ProxyDataLoader.java b/java/net/sf/l2j/loginserver/data/xml/L2ProxyDataLoader.java | |
new file mode 100644 | |
index 00000000..e6c80849 | |
--- /dev/null | |
+++ b/java/net/sf/l2j/loginserver/data/xml/L2ProxyDataLoader.java | |
@@ -0,0 +1,69 @@ | |
+package net.sf.l2j.loginserver.data.xml; | |
+ | |
+import net.sf.l2j.commons.data.xml.IXmlReader; | |
+import net.sf.l2j.commons.logging.CLogger; | |
+import net.sf.l2j.loginserver.data.manager.L2ProxyManager; | |
+import net.sf.l2j.loginserver.model.L2Proxy; | |
+import net.sf.l2j.loginserver.model.L2ProxyInfo; | |
+import org.w3c.dom.Document; | |
+ | |
+import java.net.UnknownHostException; | |
+import java.nio.file.Path; | |
+import java.util.HashMap; | |
+ | |
+public class L2ProxyDataLoader implements IXmlReader { | |
+ | |
+ private static final CLogger LOGGER = new CLogger(L2ProxyDataLoader.class.getName()); | |
+ | |
+ @Override | |
+ public void load() { | |
+ parseFile("./data/xml/proxy.xml"); | |
+ } | |
+ | |
+ @Override | |
+ public void parseDocument(Document doc, Path path) { | |
+ | |
+ var proxyInfoList = new HashMap<Integer, L2ProxyInfo>(); | |
+ var proxies = new HashMap<Integer, L2Proxy>(); | |
+ forEach(doc, "list", listNode -> | |
+ { | |
+ forEach(listNode, "gameserver", gameserverNode -> | |
+ { | |
+ final var gameserverSet = parseAttributes(gameserverNode); | |
+ var serverId = gameserverSet.getInteger("serverId"); | |
+ var hidesGameserver = gameserverSet.getBool("hide"); | |
+ var fallbackToGameserver = gameserverSet.getBool("fallbackToGameserver"); | |
+ var proxyInfo = new L2ProxyInfo(serverId, hidesGameserver, fallbackToGameserver); | |
+ proxyInfoList.put(serverId, proxyInfo); | |
+ | |
+ forEach(gameserverNode, "proxy", proxyNode -> { | |
+ final var proxySet = parseAttributes(proxyNode); | |
+ try { | |
+ final var proxy = new L2Proxy( | |
+ serverId, | |
+ proxySet.getInteger("proxyServerId"), | |
+ proxySet.getString("proxyHost"), | |
+ proxySet.getInteger("proxyPort"), | |
+ proxySet.getString("apiKey"), | |
+ proxySet.getInteger("apiPort"), | |
+ proxySet.getBool("validateHealth")); | |
+ proxies.put(proxySet.getInteger("proxyServerId"), proxy); | |
+ }catch (UnknownHostException ex) { | |
+ LOGGER.warn("Failed to process proxy due to badly formatted proxy host", ex); | |
+ } | |
+ }); | |
+ }); | |
+ }); | |
+ L2ProxyManager.INSTANCE.initialise(proxyInfoList, proxies); | |
+ } | |
+ | |
+ public static L2ProxyDataLoader getInstance() | |
+ { | |
+ return SingletonHolder.INSTANCE; | |
+ } | |
+ | |
+ private static class SingletonHolder | |
+ { | |
+ protected static final L2ProxyDataLoader INSTANCE = new L2ProxyDataLoader(); | |
+ } | |
+} | |
diff --git a/java/net/sf/l2j/loginserver/model/Account.java b/java/net/sf/l2j/loginserver/model/Account.java | |
index e64e9d80..82d73d0f 100644 | |
--- a/java/net/sf/l2j/loginserver/model/Account.java | |
+++ b/java/net/sf/l2j/loginserver/model/Account.java | |
@@ -3,6 +3,7 @@ | |
import net.sf.l2j.commons.network.ServerType; | |
import net.sf.l2j.loginserver.data.manager.GameServerManager; | |
+import net.sf.l2j.loginserver.data.manager.L2ProxyManager; | |
import net.sf.l2j.loginserver.data.sql.AccountTable; | |
public final class Account | |
@@ -42,7 +43,13 @@ public int getLastServer() | |
public final boolean isLoginPossible(int serverId) | |
{ | |
- final GameServerInfo gsi = GameServerManager.getInstance().getRegisteredGameServers().get(serverId); | |
+ GameServerInfo gsi = GameServerManager.getInstance().getRegisteredGameServers().get(serverId); | |
+ L2Proxy proxy = L2ProxyManager.INSTANCE.getProxyById(serverId); | |
+ | |
+ if(gsi == null && proxy != null) { | |
+ gsi = GameServerManager.getInstance().getRegisteredGameServers().get(proxy.getGameserverId()); | |
+ } | |
+ | |
if (gsi == null || !gsi.isAuthed()) | |
return false; | |
diff --git a/java/net/sf/l2j/loginserver/model/GameServerInfo.java b/java/net/sf/l2j/loginserver/model/GameServerInfo.java | |
index 62864f70..5b16310c 100644 | |
--- a/java/net/sf/l2j/loginserver/model/GameServerInfo.java | |
+++ b/java/net/sf/l2j/loginserver/model/GameServerInfo.java | |
@@ -174,4 +174,8 @@ public int getCurrentPlayerCount() | |
{ | |
return (_gst == null) ? 0 : _gst.getPlayerCount(); | |
} | |
+ | |
+ public GameServerInfo cloneInfo(){ | |
+ return new GameServerInfo(_id, _hexId, _gst); | |
+ } | |
} | |
\ No newline at end of file | |
diff --git a/java/net/sf/l2j/loginserver/model/L2Proxy.java b/java/net/sf/l2j/loginserver/model/L2Proxy.java | |
new file mode 100644 | |
index 00000000..dab373a3 | |
--- /dev/null | |
+++ b/java/net/sf/l2j/loginserver/model/L2Proxy.java | |
@@ -0,0 +1,74 @@ | |
+package net.sf.l2j.loginserver.model; | |
+ | |
+import java.net.InetAddress; | |
+import java.net.UnknownHostException; | |
+ | |
+public class L2Proxy { | |
+ private final int _gameserverId; | |
+ private final int _proxyServerId; | |
+ private final InetAddress _proxyAddress; | |
+ private final int _proxyPort; | |
+ private final String _apiKey; | |
+ private final int _apiPort; | |
+ private final boolean _validateHealth; | |
+ private boolean _isHealthy = true; | |
+ private int _maxConnections = -1; | |
+ | |
+ public L2Proxy(int gameserverId, int proxyServerId, String proxyHost, int proxyPort, String apiKey, int apiPort, boolean validateHealth) throws UnknownHostException { | |
+ _gameserverId = gameserverId; | |
+ _proxyServerId = proxyServerId; | |
+ _proxyAddress = InetAddress.getByName(proxyHost); | |
+ _proxyPort = proxyPort; | |
+ _apiKey = apiKey; | |
+ _apiPort = apiPort; | |
+ _validateHealth = validateHealth; | |
+ } | |
+ | |
+ public int getGameserverId() | |
+ { | |
+ return _gameserverId; | |
+ } | |
+ | |
+ public int getProxyServerId() | |
+ { | |
+ return _proxyServerId; | |
+ } | |
+ | |
+ public InetAddress getProxyAddress() | |
+ { | |
+ return _proxyAddress; | |
+ } | |
+ | |
+ public int getProxyPort() | |
+ { | |
+ return _proxyPort; | |
+ } | |
+ | |
+ public boolean isHealthy() { | |
+ return _isHealthy; | |
+ } | |
+ | |
+ public void setHealthy(boolean healthy) { | |
+ _isHealthy = healthy; | |
+ } | |
+ | |
+ public int getMaxConnections() { | |
+ return _maxConnections; | |
+ } | |
+ | |
+ public void setMaxConnections(int maxConnections) { | |
+ _maxConnections = maxConnections; | |
+ } | |
+ | |
+ public String getApiKey() { | |
+ return _apiKey; | |
+ } | |
+ | |
+ public int getApiPort() { | |
+ return _apiPort; | |
+ } | |
+ | |
+ public boolean shouldValidateHealth() { | |
+ return _validateHealth; | |
+ } | |
+} | |
diff --git a/java/net/sf/l2j/loginserver/model/L2ProxyInfo.java b/java/net/sf/l2j/loginserver/model/L2ProxyInfo.java | |
new file mode 100644 | |
index 00000000..1dd8176d | |
--- /dev/null | |
+++ b/java/net/sf/l2j/loginserver/model/L2ProxyInfo.java | |
@@ -0,0 +1,25 @@ | |
+package net.sf.l2j.loginserver.model; | |
+ | |
+public class L2ProxyInfo { | |
+ private final int _gameserverId; | |
+ private final boolean _hidesGameserver; | |
+ private final boolean _fallbackToGameserver; | |
+ | |
+ public L2ProxyInfo(int gameserverId, boolean hidesGameserver, boolean fallbackToGameserver) { | |
+ _gameserverId = gameserverId; | |
+ _hidesGameserver = hidesGameserver; | |
+ _fallbackToGameserver = fallbackToGameserver; | |
+ } | |
+ | |
+ public boolean hidesGameserver() { | |
+ return _hidesGameserver; | |
+ } | |
+ | |
+ public int getGameserverId() { | |
+ return _gameserverId; | |
+ } | |
+ | |
+ public boolean isFallbackToGameserver() { | |
+ return _fallbackToGameserver; | |
+ } | |
+} | |
diff --git a/java/net/sf/l2j/loginserver/network/loginserverpackets/PlayerAuthResponse.java b/java/net/sf/l2j/loginserver/network/loginserverpackets/PlayerAuthResponse.java | |
index 620c012f..13c15cda 100644 | |
--- a/java/net/sf/l2j/loginserver/network/loginserverpackets/PlayerAuthResponse.java | |
+++ b/java/net/sf/l2j/loginserver/network/loginserverpackets/PlayerAuthResponse.java | |
@@ -4,11 +4,12 @@ | |
public class PlayerAuthResponse extends ServerBasePacket | |
{ | |
- public PlayerAuthResponse(String account, boolean response) | |
+ public PlayerAuthResponse(String account, boolean response, String realIp) | |
{ | |
writeC(0x03); | |
writeS(account); | |
writeC(response ? 1 : 0); | |
+ writeS(realIp); | |
} | |
@Override | |
diff --git a/java/net/sf/l2j/loginserver/network/serverpackets/ServerList.java b/java/net/sf/l2j/loginserver/network/serverpackets/ServerList.java | |
index 37f91d9d..cccd5708 100644 | |
--- a/java/net/sf/l2j/loginserver/network/serverpackets/ServerList.java | |
+++ b/java/net/sf/l2j/loginserver/network/serverpackets/ServerList.java | |
@@ -3,19 +3,24 @@ | |
import java.net.InetAddress; | |
import java.net.UnknownHostException; | |
import java.util.ArrayList; | |
+import java.util.Comparator; | |
import java.util.List; | |
+import java.util.stream.Collectors; | |
import net.sf.l2j.commons.network.ServerType; | |
import net.sf.l2j.loginserver.data.manager.GameServerManager; | |
+import net.sf.l2j.loginserver.data.manager.L2ProxyManager; | |
import net.sf.l2j.loginserver.model.Account; | |
import net.sf.l2j.loginserver.model.GameServerInfo; | |
+import net.sf.l2j.loginserver.model.L2Proxy; | |
import net.sf.l2j.loginserver.model.ServerData; | |
public final class ServerList extends L2LoginServerPacket | |
{ | |
private final List<ServerData> _servers = new ArrayList<>(); | |
- | |
+ private static final Comparator<ServerData> SERVER_DATA_COMPARATOR = Comparator.comparingInt(ServerData::getServerId); | |
+ | |
private final int _lastServer; | |
public ServerList(Account account) | |
@@ -27,8 +32,38 @@ public ServerList(Account account) | |
final ServerType type = (account.getAccessLevel() < 0 || (gsi.getType() == ServerType.GM_ONLY && account.getAccessLevel() <= 0)) ? ServerType.DOWN : gsi.getType(); | |
final String hostName = gsi.getHostName(); | |
- _servers.add(new ServerData(type, hostName, gsi)); | |
+ var proxyInfo = L2ProxyManager.INSTANCE.getProxyInfoByGameserverId(gsi.getId()); | |
+ | |
+ if(proxyInfo == null) { | |
+ _servers.add(new ServerData(type, hostName, gsi)); | |
+ continue; | |
+ } | |
+ | |
+ var proxiesForGameserver = L2ProxyManager.INSTANCE.getProxies().stream() | |
+ .filter(x -> x.getGameserverId() == gsi.getId()).collect(Collectors.toList()); | |
+ | |
+ if(!proxyInfo.hidesGameserver()) { | |
+ _servers.add(new ServerData(type, hostName, gsi)); | |
+ } | |
+ | |
+ if(proxyInfo.isFallbackToGameserver() && proxiesForGameserver.stream().noneMatch(L2Proxy::isHealthy)) { | |
+ _servers.add(new ServerData(type, hostName, gsi)); | |
+ } | |
+ | |
+ proxiesForGameserver.forEach(l2Proxy -> { | |
+ var newType = l2Proxy.isHealthy() ? ServerType.AUTO : ServerType.DOWN; | |
+ var proxyGsi = gsi.cloneInfo(); | |
+ proxyGsi.setPort(l2Proxy.getProxyPort()); | |
+ proxyGsi.setId(l2Proxy.getProxyServerId()); | |
+ if(gsi.getType() == ServerType.DOWN) { | |
+ proxyGsi.setDown(); | |
+ newType = ServerType.DOWN; | |
+ } | |
+ _servers.add(new ServerData(newType, l2Proxy.getProxyAddress().getHostAddress(), proxyGsi)); | |
+ }); | |
} | |
+ | |
+ _servers.sort(SERVER_DATA_COMPARATOR); | |
} | |
@Override | |
diff --git a/java/net/sf/l2j/util/PredicateHelpers.java b/java/net/sf/l2j/util/PredicateHelpers.java | |
new file mode 100644 | |
index 00000000..e9bd5b78 | |
--- /dev/null | |
+++ b/java/net/sf/l2j/util/PredicateHelpers.java | |
@@ -0,0 +1,24 @@ | |
+package net.sf.l2j.util; | |
+ | |
+import java.util.Arrays; | |
+import java.util.List; | |
+import java.util.Map; | |
+import java.util.concurrent.ConcurrentHashMap; | |
+import java.util.function.Function; | |
+import java.util.function.Predicate; | |
+import java.util.stream.Collectors; | |
+ | |
+public class PredicateHelpers { | |
+ @SafeVarargs | |
+ public static <T> Predicate<T> distinctByKeys(Function<? super T, ?>... keyExtractors) | |
+ { | |
+ final Map<List<?>, Boolean> seen = new ConcurrentHashMap<>(); | |
+ return t -> | |
+ { | |
+ final List<?> keys = Arrays.stream(keyExtractors) | |
+ .map(ke -> ke.apply(t)) | |
+ .collect(Collectors.toList()); | |
+ return seen.putIfAbsent(keys, Boolean.TRUE) == null; | |
+ }; | |
+ } | |
+} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment