Add dedicated host hwid cache, further refactor session coordinator
This commit is contained in:
1 parent
671313ab57
commit
d34798649b
5 files changed
+134
-100
No files matched your search
@@ -0,0 +1,16 @@
|
||||
package net.server.coordinator.session;
|
||||
|
||||
import net.server.Server;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
record HostHwid(String hwid, Instant expiry) {
|
||||
static HostHwid createWithDefaultExpiry(String hwid) {
|
||||
return new HostHwid(hwid, getDefaultExpiry());
|
||||
}
|
||||
|
||||
private static Instant getDefaultExpiry() {
|
||||
return Instant.ofEpochMilli(Server.getInstance().getCurrentTime() + TimeUnit.DAYS.toMillis(7));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package net.server.coordinator.session;
|
||||
|
||||
import net.server.Server;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
class HostHwidCache {
|
||||
private final ConcurrentHashMap<String, HostHwid> hostHwidCache = new ConcurrentHashMap<>(); // Key: remoteHost
|
||||
|
||||
void clearExpired() {
|
||||
SessionDAO.deleteExpiredHwidAccounts();
|
||||
|
||||
Instant now = Instant.ofEpochMilli(Server.getInstance().getCurrentTime());
|
||||
List<String> remoteHostsToRemove = new ArrayList<>();
|
||||
for (Map.Entry<String, HostHwid> entry : hostHwidCache.entrySet()) {
|
||||
if (now.isAfter(entry.getValue().expiry())) {
|
||||
remoteHostsToRemove.add(entry.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
for (String remoteHost : remoteHostsToRemove) {
|
||||
hostHwidCache.remove(remoteHost);
|
||||
}
|
||||
}
|
||||
|
||||
void addEntry(String remoteHost, String remoteHwid) {
|
||||
hostHwidCache.put(remoteHost, HostHwid.createWithDefaultExpiry(remoteHwid));
|
||||
}
|
||||
|
||||
HostHwid getEntry(String remoteHost) {
|
||||
return hostHwidCache.get(remoteHost);
|
||||
}
|
||||
|
||||
String removeEntryAndGetItsHwid(String remoteHost) {
|
||||
HostHwid hostHwid = hostHwidCache.remove(remoteHost);
|
||||
return hostHwid == null ? null : hostHwid.hwid();
|
||||
}
|
||||
|
||||
String getEntryHwid(String remoteHost) {
|
||||
HostHwid hostHwid = hostHwidCache.get(remoteHost);
|
||||
return hostHwid == null ? null : hostHwid.hwid();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,7 @@
|
||||
package net.server.coordinator.session;
|
||||
|
||||
public record HwidRelevance(String hwid, int relevance) {
|
||||
public int getIncrementedRelevance() {
|
||||
return relevance < Byte.MAX_VALUE ? relevance + 1 : relevance;
|
||||
}
|
||||
}
|
||||
@@ -34,8 +34,6 @@ import java.sql.SQLException;
|
||||
import java.time.Instant;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -62,13 +60,10 @@ public class MapleSessionCoordinator {
|
||||
|
||||
private final SessionInitialization sessionInit = new SessionInitialization();
|
||||
private final LoginStorage loginStorage = new LoginStorage();
|
||||
private final Map<Integer, MapleClient> onlineClients = new HashMap<>();
|
||||
private final Set<String> onlineRemoteHwids = new HashSet<>();
|
||||
private final Map<String, Set<IoSession>> loginRemoteHosts = new HashMap<>();
|
||||
private final Set<String> pooledRemoteHosts = new HashSet<>();
|
||||
|
||||
private final ConcurrentHashMap<String, String> cachedHostHwids = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<String, Long> cachedHostTimeout = new ConcurrentHashMap<>();
|
||||
private final Map<Integer, MapleClient> onlineClients = new HashMap<>(); // Key: account id
|
||||
private final Set<String> onlineRemoteHwids = new HashSet<>(); // Hwid/nibblehwid
|
||||
private final Map<String, Set<IoSession>> loginRemoteHosts = new HashMap<>(); // Key: Ip (+ nibblehwid)
|
||||
private final HostHwidCache hostHwidCache = new HostHwidCache();
|
||||
|
||||
private MapleSessionCoordinator() {
|
||||
}
|
||||
@@ -81,12 +76,7 @@ public class MapleSessionCoordinator {
|
||||
if (!routineCheck) {
|
||||
// better update HWID relevance as soon as the login is authenticated
|
||||
Instant expiry = HwidAssociationExpiry.getHwidAccountExpiry(hwidRelevance.relevance());
|
||||
int relevance = hwidRelevance.relevance();
|
||||
if (relevance < Byte.MAX_VALUE) {
|
||||
relevance++;
|
||||
}
|
||||
|
||||
SessionDAO.updateAccountAccess(con, nibbleHwid, accountId, expiry, relevance);
|
||||
SessionDAO.updateAccountAccess(con, nibbleHwid, accountId, expiry, hwidRelevance.getIncrementedRelevance());
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -121,20 +111,26 @@ public class MapleSessionCoordinator {
|
||||
return (MapleClient) session.getAttribute(MapleClient.CLIENT_KEY);
|
||||
}
|
||||
|
||||
public void updateOnlineSession(IoSession session) {
|
||||
/**
|
||||
* Overwrites any existing online client for the account id, making sure to disconnect it as well.
|
||||
*/
|
||||
public void updateOnlineClient(IoSession session) {
|
||||
MapleClient client = getSessionClient(session);
|
||||
|
||||
if (client != null) {
|
||||
int accountId = client.getAccID();
|
||||
MapleClient ingameClient = onlineClients.get(accountId);
|
||||
if (ingameClient != null) { // thanks MedicOP for finding out a loss of loggedin account uniqueness when using the CMS "Unstuck" feature
|
||||
ingameClient.forceDisconnect();
|
||||
}
|
||||
|
||||
disconnectClientIfOnline(accountId);
|
||||
onlineClients.put(accountId, client);
|
||||
}
|
||||
}
|
||||
|
||||
private void disconnectClientIfOnline(int accountId) {
|
||||
MapleClient ingameClient = onlineClients.get(accountId);
|
||||
if (ingameClient != null) { // thanks MedicOP for finding out a loss of loggedin account uniqueness when using the CMS "Unstuck" feature
|
||||
ingameClient.forceDisconnect();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean canStartLoginSession(IoSession session) {
|
||||
if (!YamlConfig.config.server.DETERRED_MULTICLIENT) {
|
||||
return true;
|
||||
@@ -152,39 +148,31 @@ public class MapleSessionCoordinator {
|
||||
}
|
||||
|
||||
try {
|
||||
String knownHwid = cachedHostHwids.get(remoteHost);
|
||||
if (knownHwid != null) {
|
||||
if (onlineRemoteHwids.contains(knownHwid)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (loginRemoteHosts.containsKey(remoteHost)) {
|
||||
final HostHwid knownHwid = hostHwidCache.getEntry(remoteHost);
|
||||
if (knownHwid != null && onlineRemoteHwids.contains(knownHwid.hwid())) {
|
||||
return false;
|
||||
} else if (loginRemoteHosts.containsKey(remoteHost)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Set<IoSession> lrh = new HashSet<>(2);
|
||||
lrh.add(session);
|
||||
loginRemoteHosts.put(remoteHost, lrh);
|
||||
|
||||
addRemoteHostSession(remoteHost, session);
|
||||
return true;
|
||||
} finally {
|
||||
sessionInit.finalize(remoteHost);
|
||||
}
|
||||
}
|
||||
|
||||
private void addRemoteHostSession(String remoteHost, IoSession session) {
|
||||
Set<IoSession> sessions = new HashSet<>(2);
|
||||
sessions.add(session);
|
||||
loginRemoteHosts.put(remoteHost, sessions);
|
||||
}
|
||||
|
||||
public void closeLoginSession(IoSession session) {
|
||||
String nibbleHwid = (String) session.removeAttribute(MapleClient.CLIENT_NIBBLEHWID);
|
||||
String remoteHost = getSessionRemoteHost(session);
|
||||
|
||||
Set<IoSession> lrh = loginRemoteHosts.get(remoteHost);
|
||||
if (lrh != null) {
|
||||
lrh.remove(session);
|
||||
if (lrh.isEmpty()) {
|
||||
loginRemoteHosts.remove(remoteHost);
|
||||
}
|
||||
}
|
||||
|
||||
removeRemoteHostSession(remoteHost, session);
|
||||
|
||||
String nibbleHwid = (String) session.removeAttribute(MapleClient.CLIENT_NIBBLEHWID);
|
||||
if (nibbleHwid != null) {
|
||||
onlineRemoteHwids.remove(nibbleHwid);
|
||||
|
||||
@@ -200,6 +188,17 @@ public class MapleSessionCoordinator {
|
||||
}
|
||||
}
|
||||
|
||||
private void removeRemoteHostSession(String remoteHost, IoSession session) {
|
||||
Set<IoSession> sessions = loginRemoteHosts.get(remoteHost);
|
||||
if (sessions != null) {
|
||||
sessions.remove(session);
|
||||
|
||||
if (sessions.isEmpty()) {
|
||||
loginRemoteHosts.remove(remoteHost);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public AntiMulticlientResult attemptLoginSession(IoSession session, String nibbleHwid, int accountId, boolean routineCheck) {
|
||||
if (!YamlConfig.config.server.DETERRED_MULTICLIENT) {
|
||||
session.setAttribute(MapleClient.CLIENT_NIBBLEHWID, nibbleHwid);
|
||||
@@ -215,24 +214,16 @@ public class MapleSessionCoordinator {
|
||||
try {
|
||||
if (!loginStorage.registerLogin(accountId)) {
|
||||
return AntiMulticlientResult.MANY_ACCOUNT_ATTEMPTS;
|
||||
} else if (routineCheck && !attemptAccountAccess(accountId, nibbleHwid, routineCheck)) {
|
||||
return AntiMulticlientResult.REMOTE_REACHED_LIMIT;
|
||||
} else if (onlineRemoteHwids.contains(nibbleHwid)) {
|
||||
return AntiMulticlientResult.REMOTE_LOGGEDIN;
|
||||
} else if (!attemptAccountAccess(accountId, nibbleHwid, routineCheck)) {
|
||||
return AntiMulticlientResult.REMOTE_REACHED_LIMIT;
|
||||
}
|
||||
|
||||
if (!routineCheck) {
|
||||
if (onlineRemoteHwids.contains(nibbleHwid)) {
|
||||
return AntiMulticlientResult.REMOTE_LOGGEDIN;
|
||||
}
|
||||
|
||||
if (!attemptAccountAccess(accountId, nibbleHwid, routineCheck)) {
|
||||
return AntiMulticlientResult.REMOTE_REACHED_LIMIT;
|
||||
}
|
||||
|
||||
session.setAttribute(MapleClient.CLIENT_NIBBLEHWID, nibbleHwid);
|
||||
onlineRemoteHwids.add(nibbleHwid);
|
||||
} else {
|
||||
if (!attemptAccountAccess(accountId, nibbleHwid, routineCheck)) {
|
||||
return AntiMulticlientResult.REMOTE_REACHED_LIMIT;
|
||||
}
|
||||
}
|
||||
session.setAttribute(MapleClient.CLIENT_NIBBLEHWID, nibbleHwid);
|
||||
onlineRemoteHwids.add(nibbleHwid);
|
||||
|
||||
return AntiMulticlientResult.SUCCESS;
|
||||
} finally {
|
||||
@@ -243,8 +234,8 @@ public class MapleSessionCoordinator {
|
||||
public AntiMulticlientResult attemptGameSession(IoSession session, int accountId, String remoteHwid) {
|
||||
final String remoteHost = getSessionRemoteHost(session);
|
||||
if (!YamlConfig.config.server.DETERRED_MULTICLIENT) {
|
||||
associateRemoteHostHwid(remoteHost, remoteHwid);
|
||||
associateRemoteHostHwid(getSessionRemoteAddress(session), remoteHwid); // no HWID information on the loggedin newcomer session...
|
||||
hostHwidCache.addEntry(remoteHost, remoteHwid);
|
||||
hostHwidCache.addEntry(getSessionRemoteAddress(session), remoteHwid); // no HWID information on the loggedin newcomer session...
|
||||
return AntiMulticlientResult.SUCCESS;
|
||||
}
|
||||
|
||||
@@ -263,9 +254,7 @@ public class MapleSessionCoordinator {
|
||||
|
||||
if (!remoteHwid.endsWith(nibbleHwid)) {
|
||||
return AntiMulticlientResult.REMOTE_NO_MATCH;
|
||||
}
|
||||
|
||||
if (onlineRemoteHwids.contains(remoteHwid)) {
|
||||
} else if (onlineRemoteHwids.contains(remoteHwid)) {
|
||||
return AntiMulticlientResult.REMOTE_LOGGEDIN;
|
||||
}
|
||||
|
||||
@@ -273,8 +262,8 @@ public class MapleSessionCoordinator {
|
||||
|
||||
// updated session CLIENT_HWID attribute will be set when the player log in the game
|
||||
onlineRemoteHwids.add(remoteHwid);
|
||||
associateRemoteHostHwid(remoteHost, remoteHwid);
|
||||
associateRemoteHostHwid(getSessionRemoteAddress(session), remoteHwid);
|
||||
hostHwidCache.addEntry(remoteHost, remoteHwid);
|
||||
hostHwidCache.addEntry(getSessionRemoteAddress(session), remoteHwid);
|
||||
associateHwidAccountIfAbsent(remoteHwid, accountId);
|
||||
|
||||
return AntiMulticlientResult.SUCCESS;
|
||||
@@ -317,10 +306,10 @@ public class MapleSessionCoordinator {
|
||||
}
|
||||
|
||||
MapleClient client = new MapleClient(null, null, session);
|
||||
Integer cid = Server.getInstance().freeCharacteridInTransition(client);
|
||||
if (cid != null) {
|
||||
Integer chrId = Server.getInstance().freeCharacteridInTransition(client);
|
||||
if (chrId != null) {
|
||||
try {
|
||||
client.setAccID(MapleCharacter.loadCharFromDB(cid, client, false).getAccountID());
|
||||
client.setAccID(MapleCharacter.loadCharFromDB(chrId, client, false).getAccountID());
|
||||
} catch (SQLException sqle) {
|
||||
sqle.printStackTrace();
|
||||
}
|
||||
@@ -341,9 +330,10 @@ public class MapleSessionCoordinator {
|
||||
|
||||
hwid = (String) session.removeAttribute(MapleClient.CLIENT_HWID);
|
||||
onlineRemoteHwids.remove(hwid);
|
||||
|
||||
|
||||
if (client != null) {
|
||||
if (hwid != null) { // is a game session
|
||||
final boolean isGameSession = hwid != null;
|
||||
if (isGameSession) {
|
||||
onlineClients.remove(client.getAccID());
|
||||
} else {
|
||||
MapleClient loggedClient = onlineClients.get(client.getAccID());
|
||||
@@ -358,44 +348,21 @@ public class MapleSessionCoordinator {
|
||||
if (immediately != null) {
|
||||
session.close(immediately);
|
||||
}
|
||||
|
||||
// session.removeAttribute(MapleClient.CLIENT_REMOTE_ADDRESS); No real need for removing String property on closed sessions
|
||||
}
|
||||
|
||||
public String pickLoginSessionHwid(IoSession session) {
|
||||
String remoteHost = getSessionRemoteAddress(session);
|
||||
return cachedHostHwids.remove(remoteHost); // thanks BHB, resinate for noticing players from same network not being able to login
|
||||
// thanks BHB, resinate for noticing players from same network not being able to login
|
||||
return hostHwidCache.removeEntryAndGetItsHwid(remoteHost);
|
||||
}
|
||||
|
||||
public String getGameSessionHwid(IoSession session) {
|
||||
String remoteHost = getSessionRemoteHost(session);
|
||||
return cachedHostHwids.get(remoteHost);
|
||||
return hostHwidCache.getEntryHwid(remoteHost);
|
||||
}
|
||||
|
||||
private void associateRemoteHostHwid(String remoteHost, String remoteHwid) {
|
||||
cachedHostHwids.put(remoteHost, remoteHwid);
|
||||
cachedHostTimeout.put(remoteHost, getHostTimeout());
|
||||
}
|
||||
|
||||
private static long getHostTimeout() {
|
||||
return Server.getInstance().getCurrentTime() + TimeUnit.DAYS.toMillis(7); // 1 week-time entry
|
||||
}
|
||||
|
||||
public void runUpdateHwidHistory() {
|
||||
SessionDAO.deleteExpiredHwidAccounts();
|
||||
|
||||
long timeNow = Server.getInstance().getCurrentTime();
|
||||
List<String> toRemove = new LinkedList<>();
|
||||
for (Entry<String, Long> cht : cachedHostTimeout.entrySet()) {
|
||||
if (cht.getValue() < timeNow) {
|
||||
toRemove.add(cht.getKey());
|
||||
}
|
||||
}
|
||||
|
||||
for (String s : toRemove) {
|
||||
cachedHostHwids.remove(s);
|
||||
cachedHostTimeout.remove(s);
|
||||
}
|
||||
public void clearExpiredHwidHistory() {
|
||||
hostHwidCache.clearExpired();
|
||||
}
|
||||
|
||||
public void runUpdateLoginHistory() {
|
||||
|
||||
@@ -29,6 +29,6 @@ public class LoginCoordinatorTask implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
MapleSessionCoordinator.getInstance().runUpdateHwidHistory();
|
||||
MapleSessionCoordinator.getInstance().clearExpiredHwidHistory();
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user