Skip to content

Instantly share code, notes, and snippets.

@Elfocrash
Last active February 7, 2023 19:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save Elfocrash/644d99e27cfb798220340aadffd684e6 to your computer and use it in GitHub Desktop.
Save Elfocrash/644d99e27cfb798220340aadffd684e6 to your computer and use it in GitHub Desktop.
The Java side diff for L2Proxy 0.2.0
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