From 4dc093539124468994296959e4d6b2ac6ac868f7 Mon Sep 17 00:00:00 2001 From: P0nk Date: Tue, 8 Jun 2021 19:32:06 +0200 Subject: [PATCH 01/37] Reformat MapleClient Now uses 4 space indentation --- src/main/java/client/MapleClient.java | 2642 +++++++++++++------------ 1 file changed, 1322 insertions(+), 1320 deletions(-) diff --git a/src/main/java/client/MapleClient.java b/src/main/java/client/MapleClient.java index 83f76f6e5b..f158759f96 100644 --- a/src/main/java/client/MapleClient.java +++ b/src/main/java/client/MapleClient.java @@ -51,8 +51,8 @@ import tools.*; import javax.script.ScriptEngine; import java.io.IOException; -import java.io.UnsupportedEncodingException; import java.net.InetAddress; +import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.sql.*; @@ -63,1398 +63,1400 @@ import java.util.concurrent.locks.Lock; public class MapleClient { - public static final int LOGIN_NOTLOGGEDIN = 0; - public static final int LOGIN_SERVER_TRANSITION = 1; - public static final int LOGIN_LOGGEDIN = 2; - public static final String CLIENT_KEY = "CLIENT"; - public static final String CLIENT_HWID = "HWID"; - public static final String CLIENT_NIBBLEHWID = "HWID2"; - public static final String CLIENT_REMOTE_ADDRESS = "REMOTE_IP"; - public static final String CLIENT_TRANSITION = "TRANSITION"; - private MapleAESOFB send; - private MapleAESOFB receive; - private final IoSession session; - private MapleCharacter player; - private int channel = 1; - private int accId = -4; - private boolean loggedIn = false; - private boolean serverTransition = false; - private Calendar birthday = null; - private String accountName = null; - private int world; - private long lastPong; - private int gmlevel; - private Set macs = new HashSet<>(); - private Map engines = new HashMap<>(); - private byte characterSlots = 3; - private byte loginattempt = 0; - private String pin = ""; - private int pinattempt = 0; - private String pic = ""; - private int picattempt = 0; - private String hwid = null; - private byte csattempt = 0; - private byte gender = -1; - private boolean disconnecting = false; - private final Semaphore actionsSemaphore = new Semaphore(7); - private final Lock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CLIENT, true); - private final Lock encoderLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CLIENT_ENCODER, true); - private final Lock announcerLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CLIENT_ANNOUNCER, true); - // thanks Masterrulax & try2hack for pointing out a bottleneck issue with shared locks, shavit for noticing an opportunity for improvement - private Calendar tempBanCalendar; - private int votePoints; - private int voteTime = -1; - private int visibleWorlds; - private long lastNpcClick; - private long sessionId; - private long lastPacket = System.currentTimeMillis(); - private int lang = 0; - - public void updateLastPacket() { - lastPacket = System.currentTimeMillis(); - } + public static final int LOGIN_NOTLOGGEDIN = 0; + public static final int LOGIN_SERVER_TRANSITION = 1; + public static final int LOGIN_LOGGEDIN = 2; + public static final String CLIENT_KEY = "CLIENT"; + public static final String CLIENT_HWID = "HWID"; + public static final String CLIENT_NIBBLEHWID = "HWID2"; + public static final String CLIENT_REMOTE_ADDRESS = "REMOTE_IP"; + public static final String CLIENT_TRANSITION = "TRANSITION"; + private MapleAESOFB send; + private MapleAESOFB receive; + private final IoSession session; + private MapleCharacter player; + private int channel = 1; + private int accId = -4; + private boolean loggedIn = false; + private boolean serverTransition = false; + private Calendar birthday = null; + private String accountName = null; + private int world; + private long lastPong; + private int gmlevel; + private Set macs = new HashSet<>(); + private Map engines = new HashMap<>(); + private byte characterSlots = 3; + private byte loginattempt = 0; + private String pin = ""; + private int pinattempt = 0; + private String pic = ""; + private int picattempt = 0; + private String hwid = null; + private byte csattempt = 0; + private byte gender = -1; + private boolean disconnecting = false; + private final Semaphore actionsSemaphore = new Semaphore(7); + private final Lock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CLIENT, true); + private final Lock encoderLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CLIENT_ENCODER, true); + private final Lock announcerLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CLIENT_ANNOUNCER, true); + // thanks Masterrulax & try2hack for pointing out a bottleneck issue with shared locks, shavit for noticing an opportunity for improvement + private Calendar tempBanCalendar; + private int votePoints; + private int voteTime = -1; + private int visibleWorlds; + private long lastNpcClick; + private long sessionId; + private long lastPacket = System.currentTimeMillis(); + private int lang = 0; - public long getLastPacket() { - return lastPacket; - } + public void updateLastPacket() { + lastPacket = System.currentTimeMillis(); + } - public MapleClient(MapleAESOFB send, MapleAESOFB receive, IoSession session) { - this.send = send; - this.receive = receive; - this.session = session; - } + public long getLastPacket() { + return lastPacket; + } - public MapleAESOFB getReceiveCrypto() { - return receive; - } + public MapleClient(MapleAESOFB send, MapleAESOFB receive, IoSession session) { + this.send = send; + this.receive = receive; + this.session = session; + } - public MapleAESOFB getSendCrypto() { - return send; - } + public MapleAESOFB getReceiveCrypto() { + return receive; + } - public IoSession getSession() { - return session; - } - - public EventManager getEventManager(String event) { - return getChannelServer().getEventSM().getEventManager(event); + public MapleAESOFB getSendCrypto() { + return send; + } + + public IoSession getSession() { + return session; + } + + public EventManager getEventManager(String event) { + return getChannelServer().getEventSM().getEventManager(event); + } + + public MapleCharacter getPlayer() { + return player; + } + + public void setPlayer(MapleCharacter player) { + this.player = player; + } + + public AbstractPlayerInteraction getAbstractPlayerInteraction() { + return new AbstractPlayerInteraction(this); + } + + public void sendCharList(int server) { + this.announce(MaplePacketCreator.getCharList(this, server, 0)); + } + + public List loadCharacters(int serverId) { + List chars = new ArrayList<>(15); + try { + for (CharNameAndId cni : loadCharactersInternal(serverId)) { + chars.add(MapleCharacter.loadCharFromDB(cni.id, this, false)); + } + } catch (Exception e) { + e.printStackTrace(); + } + return chars; + } + + public List loadCharacterNames(int worldId) { + List chars = new ArrayList<>(15); + for (CharNameAndId cni : loadCharactersInternal(worldId)) { + chars.add(cni.name); + } + return chars; + } + + private List loadCharactersInternal(int worldId) { + List chars = new ArrayList<>(15); + try (Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("SELECT id, name FROM characters WHERE accountid = ? AND world = ?")) { + ps.setInt(1, this.getAccID()); + ps.setInt(2, worldId); + + try (ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + chars.add(new CharNameAndId(rs.getString("name"), rs.getInt("id"))); + } + } + } catch (SQLException e) { + e.printStackTrace(); + } + return chars; + } + + public boolean isLoggedIn() { + return loggedIn; + } + + public boolean hasBannedIP() { + boolean ret = false; + try (Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("SELECT COUNT(*) FROM ipbans WHERE ? LIKE CONCAT(ip, '%')")) { + ps.setString(1, session.getRemoteAddress().toString()); + try (ResultSet rs = ps.executeQuery()) { + rs.next(); + if (rs.getInt(1) > 0) { + ret = true; + } + } + } catch (SQLException e) { + e.printStackTrace(); + } + return ret; + } + + public int getVoteTime() { + if (voteTime != -1) { + return voteTime; } - public MapleCharacter getPlayer() { - return player; - } + try (Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("SELECT date FROM bit_votingrecords WHERE UPPER(account) = UPPER(?)")) { + ps.setString(1, accountName); + try (ResultSet rs = ps.executeQuery()) { + if (!rs.next()) { + return -1; + } + voteTime = rs.getInt("date"); + } + } catch (SQLException e) { + FilePrinter.printError("hasVotedAlready.txt", e); + return -1; + } + return voteTime; + } - public void setPlayer(MapleCharacter player) { - this.player = player; - } - - public AbstractPlayerInteraction getAbstractPlayerInteraction() { - return new AbstractPlayerInteraction(this); + public void resetVoteTime() { + voteTime = -1; + } + + public boolean hasVotedAlready() { + Date currentDate = new Date(); + int timeNow = (int) (currentDate.getTime() / 1000); + int difference = (timeNow - getVoteTime()); + return difference < 86400 && difference > 0; + } + + public boolean hasBannedHWID() { + if (hwid == null) { + return false; } - public void sendCharList(int server) { - this.announce(MaplePacketCreator.getCharList(this, server, 0)); - } + boolean ret = false; + try (Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("SELECT COUNT(*) FROM hwidbans WHERE hwid LIKE ?")) { + ps.setString(1, hwid); - public List loadCharacters(int serverId) { - List chars = new ArrayList<>(15); - try { - for (CharNameAndId cni : loadCharactersInternal(serverId)) { - chars.add(MapleCharacter.loadCharFromDB(cni.id, this, false)); - } - } catch (Exception e) { - e.printStackTrace(); - } - return chars; - } - - public List loadCharacterNames(int worldId) { - List chars = new ArrayList<>(15); - for (CharNameAndId cni : loadCharactersInternal(worldId)) { - chars.add(cni.name); - } - return chars; - } - - private List loadCharactersInternal(int worldId) { - List chars = new ArrayList<>(15); - try (Connection con = DatabaseConnection.getConnection(); - PreparedStatement ps = con.prepareStatement("SELECT id, name FROM characters WHERE accountid = ? AND world = ?")) { - ps.setInt(1, this.getAccID()); - ps.setInt(2, worldId); - - try (ResultSet rs = ps.executeQuery()) { - while (rs.next()) { - chars.add(new CharNameAndId(rs.getString("name"), rs.getInt("id"))); - } - } - } catch (SQLException e) { - e.printStackTrace(); - } - return chars; - } - - public boolean isLoggedIn() { - return loggedIn; - } - - public boolean hasBannedIP() { - boolean ret = false; - try (Connection con = DatabaseConnection.getConnection(); - PreparedStatement ps = con.prepareStatement("SELECT COUNT(*) FROM ipbans WHERE ? LIKE CONCAT(ip, '%')")) { - ps.setString(1, session.getRemoteAddress().toString()); - try (ResultSet rs = ps.executeQuery()) { - rs.next(); - if (rs.getInt(1) > 0) { - ret = true; - } - } - } catch (SQLException e) { - e.printStackTrace(); - } - return ret; - } - - public int getVoteTime() { - if (voteTime != -1) { - return voteTime; - } - - try (Connection con = DatabaseConnection.getConnection(); - PreparedStatement ps = con.prepareStatement("SELECT date FROM bit_votingrecords WHERE UPPER(account) = UPPER(?)")) { - ps.setString(1, accountName); - try (ResultSet rs = ps.executeQuery()) { - if (!rs.next()) { - return -1; - } - voteTime = rs.getInt("date"); - } - } catch (SQLException e) { - FilePrinter.printError("hasVotedAlready.txt", e); - return -1; - } - return voteTime; - } - - public void resetVoteTime() { - voteTime = -1; - } - - public boolean hasVotedAlready(){ - Date currentDate = new Date(); - int timeNow = (int) (currentDate.getTime() / 1000); - int difference = (timeNow - getVoteTime()); - return difference < 86400 && difference > 0; - } - - public boolean hasBannedHWID() { - if (hwid == null) { - return false; - } - - boolean ret = false; - try (Connection con = DatabaseConnection.getConnection(); - PreparedStatement ps = con.prepareStatement("SELECT COUNT(*) FROM hwidbans WHERE hwid LIKE ?")) { - ps.setString(1, hwid); - - try (ResultSet rs = ps.executeQuery()) { - if (rs != null && rs.next()) { - if (rs.getInt(1) > 0) { - ret = true; - } - } - } - } catch (SQLException e) { - e.printStackTrace(); - } - - return ret; - } - - public boolean hasBannedMac() { - if (macs.isEmpty()) { - return false; - } - boolean ret = false; - int i; - StringBuilder sql = new StringBuilder("SELECT COUNT(*) FROM macbans WHERE mac IN ("); - for (i = 0; i < macs.size(); i++) { - sql.append("?"); - if (i != macs.size() - 1) { - sql.append(", "); - } - } - sql.append(")"); - - try (Connection con = DatabaseConnection.getConnection(); - PreparedStatement ps = con.prepareStatement(sql.toString())) { - i = 0; - for (String mac : macs) { - ps.setString(++i, mac); - } - try (ResultSet rs = ps.executeQuery()) { - rs.next(); - if (rs.getInt(1) > 0) { - ret = true; - } - } - } catch (Exception e) { - e.printStackTrace(); - } - - return ret; - } - - private void loadHWIDIfNescessary() throws SQLException { - if (hwid == null) { - try (Connection con = DatabaseConnection.getConnection(); - PreparedStatement ps = con.prepareStatement("SELECT hwid FROM accounts WHERE id = ?")) { - ps.setInt(1, accId); - - try (ResultSet rs = ps.executeQuery()) { - if (rs.next()) { - hwid = rs.getString("hwid"); - } - } - } - } - } - - // TODO: Recode to close statements... - private void loadMacsIfNescessary() throws SQLException { - if (macs.isEmpty()) { - try (Connection con = DatabaseConnection.getConnection(); - PreparedStatement ps = con.prepareStatement("SELECT macs FROM accounts WHERE id = ?")) { - ps.setInt(1, accId); - try (ResultSet rs = ps.executeQuery()) { - if (rs.next()) { - for (String mac : rs.getString("macs").split(", ")) { - if (!mac.equals("")) { - macs.add(mac); - } - } - } - } - } - } - } - - public void banHWID() { - try { - loadHWIDIfNescessary(); - - try (Connection con = DatabaseConnection.getConnection(); - PreparedStatement ps = con.prepareStatement("INSERT INTO hwidbans (hwid) VALUES (?)")) { - ps.setString(1, hwid); - ps.executeUpdate(); - } - } catch (SQLException e) { - e.printStackTrace(); - } - } - - public void banMacs() { - try { - loadMacsIfNescessary(); - - List filtered = new LinkedList<>(); - try (Connection con = DatabaseConnection.getConnection()) { - try (PreparedStatement ps = con.prepareStatement("SELECT filter FROM macfilters"); - ResultSet rs = ps.executeQuery()) { - while (rs.next()) { - filtered.add(rs.getString("filter")); - } - } - - try (PreparedStatement ps = con.prepareStatement("INSERT INTO macbans (mac, aid) VALUES (?, ?)")) { - for (String mac : macs) { - boolean matched = false; - for (String filter : filtered) { - if (mac.matches(filter)) { - matched = true; - break; - } - } - if (!matched) { - ps.setString(1, mac); - ps.setString(2, String.valueOf(getAccID())); - ps.executeUpdate(); - } - } - } - } - } catch (SQLException e) { - e.printStackTrace(); - } - } - - public int finishLogin() { - encoderLock.lock(); - try { - if (getLoginState() > LOGIN_NOTLOGGEDIN) { // 0 = LOGIN_NOTLOGGEDIN, 1= LOGIN_SERVER_TRANSITION, 2 = LOGIN_LOGGEDIN - loggedIn = false; - return 7; + try (ResultSet rs = ps.executeQuery()) { + if (rs != null && rs.next()) { + if (rs.getInt(1) > 0) { + ret = true; } - updateLoginState(MapleClient.LOGIN_LOGGEDIN); - } finally { - encoderLock.unlock(); } - - return 0; - } + } + } catch (SQLException e) { + e.printStackTrace(); + } - public void setPin(String pin) { - this.pin = pin; - try (Connection con = DatabaseConnection.getConnection(); - PreparedStatement ps = con.prepareStatement("UPDATE accounts SET pin = ? WHERE id = ?")) { - ps.setString(1, pin); - ps.setInt(2, accId); - ps.executeUpdate(); - } catch (SQLException e) { - e.printStackTrace(); - } - } + return ret; + } - public String getPin() { - return pin; - } + public boolean hasBannedMac() { + if (macs.isEmpty()) { + return false; + } + boolean ret = false; + int i; + StringBuilder sql = new StringBuilder("SELECT COUNT(*) FROM macbans WHERE mac IN ("); + for (i = 0; i < macs.size(); i++) { + sql.append("?"); + if (i != macs.size() - 1) { + sql.append(", "); + } + } + sql.append(")"); - public boolean checkPin(String other) { - if (!(YamlConfig.config.server.ENABLE_PIN && !canBypassPin())) { - return true; + try (Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement(sql.toString())) { + i = 0; + for (String mac : macs) { + ps.setString(++i, mac); + } + try (ResultSet rs = ps.executeQuery()) { + rs.next(); + if (rs.getInt(1) > 0) { + ret = true; } - - pinattempt++; - if (pinattempt > 5) { - MapleSessionCoordinator.getInstance().closeSession(session, false); - } - if (pin.equals(other)) { - pinattempt = 0; - MapleLoginBypassCoordinator.getInstance().registerLoginBypassEntry(getNibbleHWID(), accId, false); - return true; - } - return false; - } + } + } catch (Exception e) { + e.printStackTrace(); + } - public void setPic(String pic) { - this.pic = pic; - try (Connection con = DatabaseConnection.getConnection(); - PreparedStatement ps = con.prepareStatement("UPDATE accounts SET pic = ? WHERE id = ?")) { - ps.setString(1, pic); - ps.setInt(2, accId); - ps.executeUpdate(); - } catch (SQLException e) { - e.printStackTrace(); - } - } + return ret; + } - public String getPic() { - return pic; - } + private void loadHWIDIfNescessary() throws SQLException { + if (hwid == null) { + try (Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("SELECT hwid FROM accounts WHERE id = ?")) { + ps.setInt(1, accId); - public boolean checkPic(String other) { - if (!(YamlConfig.config.server.ENABLE_PIC && !canBypassPic())) { - return true; + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) { + hwid = rs.getString("hwid"); + } } - - picattempt++; - if (picattempt > 5) { - MapleSessionCoordinator.getInstance().closeSession(session, false); - } - if (pic.equals(other)) { // thanks ryantpayton (HeavenClient) for noticing null pics being checked here - picattempt = 0; - MapleLoginBypassCoordinator.getInstance().registerLoginBypassEntry(getNibbleHWID(), accId, true); - return true; - } - return false; - } + } + } + } - public int login(String login, String pwd, String nibbleHwid) { - int loginok = 5; - - loginattempt++; - if (loginattempt > 4) { - loggedIn = false; - MapleSessionCoordinator.getInstance().closeSession(session, false); - return 6; // thanks Survival_Project for finding out an issue with AUTOMATIC_REGISTER here - } - - try (Connection con = DatabaseConnection.getConnection(); - PreparedStatement ps = con.prepareStatement("SELECT id, password, gender, banned, pin, pic, characterslots, tos, language FROM accounts WHERE name = ?")) { - ps.setString(1, login); - - try (ResultSet rs = ps.executeQuery()) { - accId = -2; - if (rs.next()) { - accId = rs.getInt("id"); - if (accId <= 0) { - FilePrinter.printError(FilePrinter.LOGIN_EXCEPTION, "Tried to login with accid " + accId); - return 15; - } - - boolean banned = (rs.getByte("banned") == 1); - gmlevel = 0; - pin = rs.getString("pin"); - pic = rs.getString("pic"); - gender = rs.getByte("gender"); - characterSlots = rs.getByte("characterslots"); - lang = rs.getInt("language"); - String passhash = rs.getString("password"); - byte tos = rs.getByte("tos"); - - if (banned) { - return 3; - } - - if (getLoginState() > LOGIN_NOTLOGGEDIN) { // already loggedin - loggedIn = false; - loginok = 7; - } else if (passhash.charAt(0) == '$' && passhash.charAt(1) == '2' && BCrypt.checkpw(pwd, passhash)) { - loginok = (tos == 0) ? 23 : 0; - } else if (pwd.equals(passhash) || checkHash(passhash, "SHA-1", pwd) || checkHash(passhash, "SHA-512", pwd)) { - // thanks GabrielSin for detecting some no-bcrypt inconsistencies here - loginok = (tos == 0) ? (!YamlConfig.config.server.BCRYPT_MIGRATION ? 23 : -23) : (!YamlConfig.config.server.BCRYPT_MIGRATION ? 0 : -10); // migrate to bcrypt - } else { - loggedIn = false; - loginok = 4; - } - } else { - accId = -3; - } - } - } catch (SQLException e) { - e.printStackTrace(); - } - - if (loginok == 0 || loginok == 4) { - AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptLoginSession(session, nibbleHwid, accId, loginok == 4); - - switch (res) { - case SUCCESS: - if (loginok == 0) { - loginattempt = 0; - } - - return loginok; - - case REMOTE_LOGGEDIN: - return 17; - - case REMOTE_REACHED_LIMIT: - return 13; - - case REMOTE_PROCESSING: - return 10; - - case MANY_ACCOUNT_ATTEMPTS: - return 16; - - default: - return 8; - } - } else { - return loginok; - } - } - - public Calendar getTempBanCalendarFromDB() { - final Calendar lTempban = Calendar.getInstance(); - - try (Connection con = DatabaseConnection.getConnection(); - PreparedStatement ps = con.prepareStatement("SELECT `tempban` FROM accounts WHERE id = ?")) { - ps.setInt(1, getAccID()); - - final Timestamp tempban; - try (ResultSet rs = ps.executeQuery()) { - if (!rs.next()) { - return null; - } - - tempban = rs.getTimestamp("tempban"); - if (tempban.toLocalDateTime().equals(DefaultDates.getTempban())) { - return null; - } - } - - lTempban.setTimeInMillis(tempban.getTime()); - tempBanCalendar = lTempban; - return lTempban; - } catch (SQLException e) { - e.printStackTrace(); - } - - return null;//why oh why!?! - } - - public Calendar getTempBanCalendar() { - return tempBanCalendar; - } - - public boolean hasBeenBanned() { - return tempBanCalendar != null; - } - - public static long dottedQuadToLong(String dottedQuad) throws RuntimeException { - String[] quads = dottedQuad.split("\\."); - if (quads.length != 4) { - throw new RuntimeException("Invalid IP Address format."); - } - long ipAddress = 0; - for (int i = 0; i < 4; i++) { - int quad = Integer.parseInt(quads[i]); - ipAddress += (long) (quad % 256) * (long) Math.pow(256, 4 - i); - } - return ipAddress; - } - - public void updateHWID(String newHwid) { - String[] split = newHwid.split("_"); - if (split.length > 1 && split[1].length() == 8) { - StringBuilder hwid = new StringBuilder(); - String convert = split[1]; - - int len = convert.length(); - for (int i = len - 2; i >= 0; i -= 2) { - hwid.append(convert.substring(i, i + 2)); - } - hwid.insert(4, "-"); - - this.hwid = hwid.toString(); - - try (Connection con = DatabaseConnection.getConnection(); - PreparedStatement ps = con.prepareStatement("UPDATE accounts SET hwid = ? WHERE id = ?")) { - ps.setString(1, this.hwid); - ps.setInt(2, accId); - ps.executeUpdate(); - } catch (SQLException e) { - e.printStackTrace(); - } - } else { - this.disconnect(false, false); // Invalid HWID... - } - } - - public void updateMacs(String macData) { - macs.addAll(Arrays.asList(macData.split(", "))); - StringBuilder newMacData = new StringBuilder(); - Iterator iter = macs.iterator(); - while (iter.hasNext()) { - String cur = iter.next(); - newMacData.append(cur); - if (iter.hasNext()) { - newMacData.append(", "); - } - } - - try (Connection con = DatabaseConnection.getConnection(); - PreparedStatement ps = con.prepareStatement("UPDATE accounts SET macs = ? WHERE id = ?")) { - ps.setString(1, newMacData.toString()); - ps.setInt(2, accId); - ps.executeUpdate(); - } catch (SQLException e) { - e.printStackTrace(); - } - } - - public void setAccID(int id) { - this.accId = id; - } - - public int getAccID() { - return accId; - } - - public void updateLoginState(int newstate) { - // rules out possibility of multiple account entries - if (newstate == LOGIN_LOGGEDIN) { - MapleSessionCoordinator.getInstance().updateOnlineSession(this.getSession()); - } - - try (Connection con = DatabaseConnection.getConnection(); - PreparedStatement ps = con.prepareStatement("UPDATE accounts SET loggedin = ?, lastlogin = ? WHERE id = ?")) { - // using sql currenttime here could potentially break the login, thanks Arnah for pointing this out - - ps.setInt(1, newstate); - ps.setTimestamp(2, new java.sql.Timestamp(Server.getInstance().getCurrentTime())); - ps.setInt(3, getAccID()); - ps.executeUpdate(); - } catch (SQLException e) { - e.printStackTrace(); - } - - if (newstate == LOGIN_NOTLOGGEDIN) { - loggedIn = false; - serverTransition = false; - setAccID(0); - } else { - serverTransition = (newstate == LOGIN_SERVER_TRANSITION); - loggedIn = !serverTransition; - } - } - - public int getLoginState() { // 0 = LOGIN_NOTLOGGEDIN, 1= LOGIN_SERVER_TRANSITION, 2 = LOGIN_LOGGEDIN - try (Connection con = DatabaseConnection.getConnection()) { - int state; - try (PreparedStatement ps = con.prepareStatement("SELECT loggedin, lastlogin, birthday FROM accounts WHERE id = ?")) { - ps.setInt(1, getAccID()); - - try (ResultSet rs = ps.executeQuery()) { - if (!rs.next()) { - throw new RuntimeException("getLoginState - MapleClient AccID: " + getAccID()); - } - - birthday = Calendar.getInstance(); - try { - birthday.setTime(rs.getDate("birthday")); - } catch (SQLException e) { - } - - state = rs.getInt("loggedin"); - if (state == LOGIN_SERVER_TRANSITION) { - if (rs.getTimestamp("lastlogin").getTime() + 30000 < Server.getInstance().getCurrentTime()) { - int accountId = accId; - state = LOGIN_NOTLOGGEDIN; - updateLoginState(MapleClient.LOGIN_NOTLOGGEDIN); // ACCID = 0, issue found thanks to Tochi & K u ssss o & Thora & Omo Oppa - this.setAccID(accountId); - } - } - } - } - if (state == LOGIN_LOGGEDIN) { - loggedIn = true; - } else if (state == LOGIN_SERVER_TRANSITION) { - try (PreparedStatement ps2 = con.prepareStatement("UPDATE accounts SET loggedin = 0 WHERE id = ?")) { - ps2.setInt(1, getAccID()); - ps2.executeUpdate(); - } - } else { - loggedIn = false; - } - return state; - } catch (SQLException e) { - loggedIn = false; - e.printStackTrace(); - throw new RuntimeException("login state"); - } - } - - public boolean checkBirthDate(Calendar date) { - return date.get(Calendar.YEAR) == birthday.get(Calendar.YEAR) && date.get(Calendar.MONTH) == birthday.get(Calendar.MONTH) && date.get(Calendar.DAY_OF_MONTH) == birthday.get(Calendar.DAY_OF_MONTH); - } - - private void removePartyPlayer(World wserv) { - MapleMap map = player.getMap(); - final MapleParty party = player.getParty(); - final int idz = player.getId(); - - if (party != null) { - final MaplePartyCharacter chrp = new MaplePartyCharacter(player); - chrp.setOnline(false); - wserv.updateParty(party.getId(), PartyOperation.LOG_ONOFF, chrp); - if (party.getLeader().getId() == idz && map != null) { - MaplePartyCharacter lchr = null; - for (MaplePartyCharacter pchr : party.getMembers()) { - if (pchr != null && pchr.getId() != idz && (lchr == null || lchr.getLevel() <= pchr.getLevel()) && map.getCharacterById(pchr.getId()) != null) { - lchr = pchr; - } - } - if (lchr != null) { - wserv.updateParty(party.getId(), PartyOperation.CHANGE_LEADER, lchr); - } + // TODO: Recode to close statements... + private void loadMacsIfNescessary() throws SQLException { + if (macs.isEmpty()) { + try (Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("SELECT macs FROM accounts WHERE id = ?")) { + ps.setInt(1, accId); + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) { + for (String mac : rs.getString("macs").split(", ")) { + if (!mac.equals("")) { + macs.add(mac); + } } + } } + } } - - private void removePlayer(World wserv, boolean serverTransition) { - try { - player.setDisconnectedFromChannelWorld(); - player.notifyMapTransferToPartner(-1); - player.removeIncomingInvites(); - player.cancelAllBuffs(true); - - player.closePlayerInteractions(); - player.closePartySearchInteractions(); - - if (!serverTransition) { // thanks MedicOP for detecting an issue with party leader change on changing channels - removePartyPlayer(wserv); + } - EventInstanceManager eim = player.getEventInstance(); - if (eim != null) { - eim.playerDisconnected(player); - } - - if (player.getMonsterCarnival() != null) { - player.getMonsterCarnival().playerDisconnected(getPlayer().getId()); - } - - if (player.getAriantColiseum() != null) { - player.getAriantColiseum().playerDisconnected(getPlayer()); - } + public void banHWID() { + try { + loadHWIDIfNescessary(); + + try (Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("INSERT INTO hwidbans (hwid) VALUES (?)")) { + ps.setString(1, hwid); + ps.executeUpdate(); + } + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public void banMacs() { + try { + loadMacsIfNescessary(); + + List filtered = new LinkedList<>(); + try (Connection con = DatabaseConnection.getConnection()) { + try (PreparedStatement ps = con.prepareStatement("SELECT filter FROM macfilters"); + ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + filtered.add(rs.getString("filter")); + } + } + + try (PreparedStatement ps = con.prepareStatement("INSERT INTO macbans (mac, aid) VALUES (?, ?)")) { + for (String mac : macs) { + boolean matched = false; + for (String filter : filtered) { + if (mac.matches(filter)) { + matched = true; + break; + } } - - if (player.getMap() != null) { - int mapId = player.getMapId(); - player.getMap().removePlayer(player); - if(GameConstants.isDojo(mapId)) { - this.getChannelServer().freeDojoSectionIfEmpty(mapId); - } - } - - } catch (final Throwable t) { - FilePrinter.printError(FilePrinter.ACCOUNT_STUCK, t); - } - } - - public final void disconnect(final boolean shutdown, final boolean cashshop) { - if (canDisconnect()) { - ThreadManager.getInstance().newTask(() -> disconnectInternal(shutdown, cashshop)); + if (!matched) { + ps.setString(1, mac); + ps.setString(2, String.valueOf(getAccID())); + ps.executeUpdate(); + } + } } + } + } catch (SQLException e) { + e.printStackTrace(); } - - public final void forceDisconnect() { - if (canDisconnect()) { - disconnectInternal(true, false); + } + + public int finishLogin() { + encoderLock.lock(); + try { + if (getLoginState() > LOGIN_NOTLOGGEDIN) { // 0 = LOGIN_NOTLOGGEDIN, 1= LOGIN_SERVER_TRANSITION, 2 = LOGIN_LOGGEDIN + loggedIn = false; + return 7; + } + updateLoginState(MapleClient.LOGIN_LOGGEDIN); + } finally { + encoderLock.unlock(); + } + + return 0; + } + + public void setPin(String pin) { + this.pin = pin; + try (Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("UPDATE accounts SET pin = ? WHERE id = ?")) { + ps.setString(1, pin); + ps.setInt(2, accId); + ps.executeUpdate(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public String getPin() { + return pin; + } + + public boolean checkPin(String other) { + if (!(YamlConfig.config.server.ENABLE_PIN && !canBypassPin())) { + return true; + } + + pinattempt++; + if (pinattempt > 5) { + MapleSessionCoordinator.getInstance().closeSession(session, false); + } + if (pin.equals(other)) { + pinattempt = 0; + MapleLoginBypassCoordinator.getInstance().registerLoginBypassEntry(getNibbleHWID(), accId, false); + return true; + } + return false; + } + + public void setPic(String pic) { + this.pic = pic; + try (Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("UPDATE accounts SET pic = ? WHERE id = ?")) { + ps.setString(1, pic); + ps.setInt(2, accId); + ps.executeUpdate(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public String getPic() { + return pic; + } + + public boolean checkPic(String other) { + if (!(YamlConfig.config.server.ENABLE_PIC && !canBypassPic())) { + return true; + } + + picattempt++; + if (picattempt > 5) { + MapleSessionCoordinator.getInstance().closeSession(session, false); + } + if (pic.equals(other)) { // thanks ryantpayton (HeavenClient) for noticing null pics being checked here + picattempt = 0; + MapleLoginBypassCoordinator.getInstance().registerLoginBypassEntry(getNibbleHWID(), accId, true); + return true; + } + return false; + } + + public int login(String login, String pwd, String nibbleHwid) { + int loginok = 5; + + loginattempt++; + if (loginattempt > 4) { + loggedIn = false; + MapleSessionCoordinator.getInstance().closeSession(session, false); + return 6; // thanks Survival_Project for finding out an issue with AUTOMATIC_REGISTER here + } + + try (Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("SELECT id, password, gender, banned, pin, pic, characterslots, tos, language FROM accounts WHERE name = ?")) { + ps.setString(1, login); + + try (ResultSet rs = ps.executeQuery()) { + accId = -2; + if (rs.next()) { + accId = rs.getInt("id"); + if (accId <= 0) { + FilePrinter.printError(FilePrinter.LOGIN_EXCEPTION, "Tried to login with accid " + accId); + return 15; + } + + boolean banned = (rs.getByte("banned") == 1); + gmlevel = 0; + pin = rs.getString("pin"); + pic = rs.getString("pic"); + gender = rs.getByte("gender"); + characterSlots = rs.getByte("characterslots"); + lang = rs.getInt("language"); + String passhash = rs.getString("password"); + byte tos = rs.getByte("tos"); + + if (banned) { + return 3; + } + + if (getLoginState() > LOGIN_NOTLOGGEDIN) { // already loggedin + loggedIn = false; + loginok = 7; + } else if (passhash.charAt(0) == '$' && passhash.charAt(1) == '2' && BCrypt.checkpw(pwd, passhash)) { + loginok = (tos == 0) ? 23 : 0; + } else if (pwd.equals(passhash) || checkHash(passhash, "SHA-1", pwd) || checkHash(passhash, "SHA-512", pwd)) { + // thanks GabrielSin for detecting some no-bcrypt inconsistencies here + loginok = (tos == 0) ? (!YamlConfig.config.server.BCRYPT_MIGRATION ? 23 : -23) : (!YamlConfig.config.server.BCRYPT_MIGRATION ? 0 : -10); // migrate to bcrypt + } else { + loggedIn = false; + loginok = 4; + } + } else { + accId = -3; } + } + } catch (SQLException e) { + e.printStackTrace(); } - - private synchronized boolean canDisconnect() { - if (disconnecting) { - return false; - } - - disconnecting = true; - return true; + + if (loginok == 0 || loginok == 4) { + AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptLoginSession(session, nibbleHwid, accId, loginok == 4); + + switch (res) { + case SUCCESS: + if (loginok == 0) { + loginattempt = 0; + } + + return loginok; + + case REMOTE_LOGGEDIN: + return 17; + + case REMOTE_REACHED_LIMIT: + return 13; + + case REMOTE_PROCESSING: + return 10; + + case MANY_ACCOUNT_ATTEMPTS: + return 16; + + default: + return 8; + } + } else { + return loginok; } - - private void disconnectInternal(boolean shutdown, boolean cashshop) {//once per MapleClient instance - if (player != null && player.isLoggedin() && player.getClient() != null) { - final int messengerid = player.getMessenger() == null ? 0 : player.getMessenger().getId(); - //final int fid = player.getFamilyId(); - final BuddyList bl = player.getBuddylist(); - final MapleMessengerCharacter chrm = new MapleMessengerCharacter(player, 0); - final MapleGuildCharacter chrg = player.getMGC(); - final MapleGuild guild = player.getGuild(); - - player.cancelMagicDoor(); - - final World wserv = getWorldServer(); // obviously wserv is NOT null if this player was online on it - try { - removePlayer(wserv, this.serverTransition); - - if (!(channel == -1 || shutdown)) { - if (!cashshop) { - if (!this.serverTransition) { // meaning not changing channels - if (messengerid > 0) { - wserv.leaveMessenger(messengerid, chrm); - } + } + + public Calendar getTempBanCalendarFromDB() { + final Calendar lTempban = Calendar.getInstance(); + + try (Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("SELECT `tempban` FROM accounts WHERE id = ?")) { + ps.setInt(1, getAccID()); + + final Timestamp tempban; + try (ResultSet rs = ps.executeQuery()) { + if (!rs.next()) { + return null; + } + + tempban = rs.getTimestamp("tempban"); + if (tempban.toLocalDateTime().equals(DefaultDates.getTempban())) { + return null; + } + } + + lTempban.setTimeInMillis(tempban.getTime()); + tempBanCalendar = lTempban; + return lTempban; + } catch (SQLException e) { + e.printStackTrace(); + } + + return null;//why oh why!?! + } + + public Calendar getTempBanCalendar() { + return tempBanCalendar; + } + + public boolean hasBeenBanned() { + return tempBanCalendar != null; + } + + public static long dottedQuadToLong(String dottedQuad) throws RuntimeException { + String[] quads = dottedQuad.split("\\."); + if (quads.length != 4) { + throw new RuntimeException("Invalid IP Address format."); + } + long ipAddress = 0; + for (int i = 0; i < 4; i++) { + int quad = Integer.parseInt(quads[i]); + ipAddress += (long) (quad % 256) * (long) Math.pow(256, 4 - i); + } + return ipAddress; + } + + public void updateHWID(String newHwid) { + String[] split = newHwid.split("_"); + if (split.length > 1 && split[1].length() == 8) { + StringBuilder hwid = new StringBuilder(); + String convert = split[1]; + + int len = convert.length(); + for (int i = len - 2; i >= 0; i -= 2) { + hwid.append(convert, i, i + 2); + } + hwid.insert(4, "-"); + + this.hwid = hwid.toString(); + + try (Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("UPDATE accounts SET hwid = ? WHERE id = ?")) { + ps.setString(1, this.hwid); + ps.setInt(2, accId); + ps.executeUpdate(); + } catch (SQLException e) { + e.printStackTrace(); + } + } else { + this.disconnect(false, false); // Invalid HWID... + } + } + + public void updateMacs(String macData) { + macs.addAll(Arrays.asList(macData.split(", "))); + StringBuilder newMacData = new StringBuilder(); + Iterator iter = macs.iterator(); + while (iter.hasNext()) { + String cur = iter.next(); + newMacData.append(cur); + if (iter.hasNext()) { + newMacData.append(", "); + } + } + + try (Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("UPDATE accounts SET macs = ? WHERE id = ?")) { + ps.setString(1, newMacData.toString()); + ps.setInt(2, accId); + ps.executeUpdate(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + public void setAccID(int id) { + this.accId = id; + } + + public int getAccID() { + return accId; + } + + public void updateLoginState(int newstate) { + // rules out possibility of multiple account entries + if (newstate == LOGIN_LOGGEDIN) { + MapleSessionCoordinator.getInstance().updateOnlineSession(this.getSession()); + } + + try (Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("UPDATE accounts SET loggedin = ?, lastlogin = ? WHERE id = ?")) { + // using sql currenttime here could potentially break the login, thanks Arnah for pointing this out + + ps.setInt(1, newstate); + ps.setTimestamp(2, new java.sql.Timestamp(Server.getInstance().getCurrentTime())); + ps.setInt(3, getAccID()); + ps.executeUpdate(); + } catch (SQLException e) { + e.printStackTrace(); + } + + if (newstate == LOGIN_NOTLOGGEDIN) { + loggedIn = false; + serverTransition = false; + setAccID(0); + } else { + serverTransition = (newstate == LOGIN_SERVER_TRANSITION); + loggedIn = !serverTransition; + } + } + + public int getLoginState() { // 0 = LOGIN_NOTLOGGEDIN, 1= LOGIN_SERVER_TRANSITION, 2 = LOGIN_LOGGEDIN + try (Connection con = DatabaseConnection.getConnection()) { + int state; + try (PreparedStatement ps = con.prepareStatement("SELECT loggedin, lastlogin, birthday FROM accounts WHERE id = ?")) { + ps.setInt(1, getAccID()); + + try (ResultSet rs = ps.executeQuery()) { + if (!rs.next()) { + throw new RuntimeException("getLoginState - MapleClient AccID: " + getAccID()); + } + + birthday = Calendar.getInstance(); + try { + birthday.setTime(rs.getDate("birthday")); + } catch (SQLException e) { + } + + state = rs.getInt("loggedin"); + if (state == LOGIN_SERVER_TRANSITION) { + if (rs.getTimestamp("lastlogin").getTime() + 30000 < Server.getInstance().getCurrentTime()) { + int accountId = accId; + state = LOGIN_NOTLOGGEDIN; + updateLoginState(MapleClient.LOGIN_NOTLOGGEDIN); // ACCID = 0, issue found thanks to Tochi & K u ssss o & Thora & Omo Oppa + this.setAccID(accountId); + } + } + } + } + if (state == LOGIN_LOGGEDIN) { + loggedIn = true; + } else if (state == LOGIN_SERVER_TRANSITION) { + try (PreparedStatement ps2 = con.prepareStatement("UPDATE accounts SET loggedin = 0 WHERE id = ?")) { + ps2.setInt(1, getAccID()); + ps2.executeUpdate(); + } + } else { + loggedIn = false; + } + return state; + } catch (SQLException e) { + loggedIn = false; + e.printStackTrace(); + throw new RuntimeException("login state"); + } + } + + public boolean checkBirthDate(Calendar date) { + return date.get(Calendar.YEAR) == birthday.get(Calendar.YEAR) && date.get(Calendar.MONTH) == birthday.get(Calendar.MONTH) && date.get(Calendar.DAY_OF_MONTH) == birthday.get(Calendar.DAY_OF_MONTH); + } + + private void removePartyPlayer(World wserv) { + MapleMap map = player.getMap(); + final MapleParty party = player.getParty(); + final int idz = player.getId(); + + if (party != null) { + final MaplePartyCharacter chrp = new MaplePartyCharacter(player); + chrp.setOnline(false); + wserv.updateParty(party.getId(), PartyOperation.LOG_ONOFF, chrp); + if (party.getLeader().getId() == idz && map != null) { + MaplePartyCharacter lchr = null; + for (MaplePartyCharacter pchr : party.getMembers()) { + if (pchr != null && pchr.getId() != idz && (lchr == null || lchr.getLevel() <= pchr.getLevel()) && map.getCharacterById(pchr.getId()) != null) { + lchr = pchr; + } + } + if (lchr != null) { + wserv.updateParty(party.getId(), PartyOperation.CHANGE_LEADER, lchr); + } + } + } + } + + private void removePlayer(World wserv, boolean serverTransition) { + try { + player.setDisconnectedFromChannelWorld(); + player.notifyMapTransferToPartner(-1); + player.removeIncomingInvites(); + player.cancelAllBuffs(true); + + player.closePlayerInteractions(); + player.closePartySearchInteractions(); + + if (!serverTransition) { // thanks MedicOP for detecting an issue with party leader change on changing channels + removePartyPlayer(wserv); + + EventInstanceManager eim = player.getEventInstance(); + if (eim != null) { + eim.playerDisconnected(player); + } + + if (player.getMonsterCarnival() != null) { + player.getMonsterCarnival().playerDisconnected(getPlayer().getId()); + } + + if (player.getAriantColiseum() != null) { + player.getAriantColiseum().playerDisconnected(getPlayer()); + } + } + + if (player.getMap() != null) { + int mapId = player.getMapId(); + player.getMap().removePlayer(player); + if (GameConstants.isDojo(mapId)) { + this.getChannelServer().freeDojoSectionIfEmpty(mapId); + } + } + + } catch (final Throwable t) { + FilePrinter.printError(FilePrinter.ACCOUNT_STUCK, t); + } + } + + public final void disconnect(final boolean shutdown, final boolean cashshop) { + if (canDisconnect()) { + ThreadManager.getInstance().newTask(() -> disconnectInternal(shutdown, cashshop)); + } + } + + public final void forceDisconnect() { + if (canDisconnect()) { + disconnectInternal(true, false); + } + } + + private synchronized boolean canDisconnect() { + if (disconnecting) { + return false; + } + + disconnecting = true; + return true; + } + + private void disconnectInternal(boolean shutdown, boolean cashshop) {//once per MapleClient instance + if (player != null && player.isLoggedin() && player.getClient() != null) { + final int messengerid = player.getMessenger() == null ? 0 : player.getMessenger().getId(); + //final int fid = player.getFamilyId(); + final BuddyList bl = player.getBuddylist(); + final MapleMessengerCharacter chrm = new MapleMessengerCharacter(player, 0); + final MapleGuildCharacter chrg = player.getMGC(); + final MapleGuild guild = player.getGuild(); + + player.cancelMagicDoor(); + + final World wserv = getWorldServer(); // obviously wserv is NOT null if this player was online on it + try { + removePlayer(wserv, this.serverTransition); + + if (!(channel == -1 || shutdown)) { + if (!cashshop) { + if (!this.serverTransition) { // meaning not changing channels + if (messengerid > 0) { + wserv.leaveMessenger(messengerid, chrm); + } /* if (fid > 0) { final MapleFamily family = worlda.getFamily(fid); family. } */ - - player.forfeitExpirableQuests(); //This is for those quests that you have to stay logged in for a certain amount of time - - if (guild != null) { - final Server server = Server.getInstance(); - server.setGuildMemberOnline(player, false, player.getClient().getChannel()); - player.getClient().announce(MaplePacketCreator.showGuildInfo(player)); - } - if (bl != null) { - wserv.loggedOff(player.getName(), player.getId(), channel, player.getBuddylist().getBuddyIds()); - } - } - } else { - if (!this.serverTransition) { // if dc inside of cash shop. - if (bl != null) { - wserv.loggedOff(player.getName(), player.getId(), channel, player.getBuddylist().getBuddyIds()); - } - } - } - } - } catch (final Exception e) { - FilePrinter.printError(FilePrinter.ACCOUNT_STUCK, e); - } finally { - if (!this.serverTransition) { - if(chrg != null) { - chrg.setCharacter(null); - } - wserv.removePlayer(player); - //getChannelServer().removePlayer(player); already being done - - player.saveCooldowns(); - player.cancelAllDebuffs(); - player.saveCharToDB(true); - - player.logOff(); - if(YamlConfig.config.server.INSTANT_NAME_CHANGE) player.doPendingNameChange(); - clear(); - } else { - getChannelServer().removePlayer(player); - player.saveCooldowns(); - player.cancelAllDebuffs(); - player.saveCharToDB(); - } - } - } - if (!serverTransition && isLoggedIn()) { - MapleSessionCoordinator.getInstance().closeSession(session, false); - updateLoginState(MapleClient.LOGIN_NOTLOGGEDIN); - session.removeAttribute(MapleClient.CLIENT_KEY); // prevents double dcing during login - - clear(); - } else { - if (session.containsAttribute(MapleClient.CLIENT_KEY)) { - MapleSessionCoordinator.getInstance().closeSession(session, false); - session.removeAttribute(MapleClient.CLIENT_KEY); + player.forfeitExpirableQuests(); //This is for those quests that you have to stay logged in for a certain amount of time + + if (guild != null) { + final Server server = Server.getInstance(); + server.setGuildMemberOnline(player, false, player.getClient().getChannel()); + player.getClient().announce(MaplePacketCreator.showGuildInfo(player)); + } + if (bl != null) { + wserv.loggedOff(player.getName(), player.getId(), channel, player.getBuddylist().getBuddyIds()); + } } - - if (!Server.getInstance().hasCharacteridInTransition(this)) { - updateLoginState(MapleClient.LOGIN_NOTLOGGEDIN); + } else { + if (!this.serverTransition) { // if dc inside of cash shop. + if (bl != null) { + wserv.loggedOff(player.getName(), player.getId(), channel, player.getBuddylist().getBuddyIds()); + } } - - engines = null; // thanks Tochi for pointing out a NPE here + } } - } + } catch (final Exception e) { + FilePrinter.printError(FilePrinter.ACCOUNT_STUCK, e); + } finally { + if (!this.serverTransition) { + if (chrg != null) { + chrg.setCharacter(null); + } + wserv.removePlayer(player); + //getChannelServer().removePlayer(player); already being done - private void clear() { - // player hard reference removal thanks to Steve (kaito1410) - if (this.player != null) { - this.player.empty(true); // clears schedules and stuff + player.saveCooldowns(); + player.cancelAllDebuffs(); + player.saveCharToDB(true); + + player.logOff(); + if (YamlConfig.config.server.INSTANT_NAME_CHANGE) { + player.doPendingNameChange(); + } + clear(); + } else { + getChannelServer().removePlayer(player); + + player.saveCooldowns(); + player.cancelAllDebuffs(); + player.saveCharToDB(); } - - Server.getInstance().unregisterLoginState(this); - - this.accountName = null; - this.macs = null; - this.hwid = null; - this.birthday = null; - this.engines = null; - this.player = null; - this.receive = null; - this.send = null; - //this.session = null; - } - - public void setCharacterOnSessionTransitionState(int cid) { - this.updateLoginState(MapleClient.LOGIN_SERVER_TRANSITION); - session.setAttribute(MapleClient.CLIENT_TRANSITION); - Server.getInstance().setCharacteridInTransition(this, cid); - } - - public int getChannel() { - return channel; - } - - public Channel getChannelServer() { - return Server.getInstance().getChannel(world, channel); - } - - public World getWorldServer() { - return Server.getInstance().getWorld(world); - } - - public Channel getChannelServer(byte channel) { - return Server.getInstance().getChannel(world, channel); - } - - public boolean deleteCharacter(int cid, int senderAccId) { - try { - MapleCharacter chr = MapleCharacter.loadCharFromDB(cid, this, false); - - Integer partyid = chr.getWorldServer().getCharacterPartyid(cid); - if (partyid != null) { - this.setPlayer(chr); - - MapleParty party = chr.getWorldServer().getParty(partyid); - chr.setParty(party); - chr.getMPC(); - chr.leaveParty(); // thanks Vcoc for pointing out deleted characters would still stay in a party - - this.setPlayer(null); - } - - return MapleCharacter.deleteCharFromDB(chr, senderAccId); - } catch(SQLException ex) { - ex.printStackTrace(); - return false; - } - } - - public String getAccountName() { - return accountName; - } - - public void setAccountName(String a) { - this.accountName = a; - } - - public void setChannel(int channel) { - this.channel = channel; - } - - public int getWorld() { - return world; - } - - public void setWorld(int world) { - this.world = world; - } - - public void pongReceived() { - lastPong = Server.getInstance().getCurrentTime(); - } - - public void testPing(long timeThen) { - try { - if (lastPong < timeThen) { - if (session != null && session.isConnected()) { - MapleSessionCoordinator.getInstance().closeSession(session, false); - updateLoginState(MapleClient.LOGIN_NOTLOGGEDIN); - session.removeAttribute(MapleClient.CLIENT_KEY); - } - } - } catch (NullPointerException e) { - e.printStackTrace(); - } - } - - public String getHWID() { - return hwid; - } - - public void setHWID(String hwid) { - this.hwid = hwid; - } - - public Set getMacs() { - return Collections.unmodifiableSet(macs); - } - - public int getGMLevel() { - return gmlevel; - } - - public void setGMLevel(int level) { - gmlevel = level; - } - - public void setScriptEngine(String name, ScriptEngine e) { - engines.put(name, e); - } - - public ScriptEngine getScriptEngine(String name) { - return engines.get(name); - } - - public void removeScriptEngine(String name) { - engines.remove(name); - } - - public NPCConversationManager getCM() { - return NPCScriptManager.getInstance().getCM(this); - } - - public QuestActionManager getQM() { - return QuestScriptManager.getInstance().getQM(this); - } - - public boolean acceptToS() { - if (accountName == null) { - return true; - } - - boolean disconnect = false; - try (Connection con = DatabaseConnection.getConnection()) { - try (PreparedStatement ps = con.prepareStatement("SELECT `tos` FROM accounts WHERE id = ?")) { - ps.setInt(1, accId); - - try (ResultSet rs = ps.executeQuery()) { - if (rs.next()) { - if (rs.getByte("tos") == 1) { - disconnect = true; - } - } - } - } - - try (PreparedStatement ps = con.prepareStatement("UPDATE accounts SET tos = 1 WHERE id = ?")) { - ps.setInt(1, accId); - ps.executeUpdate(); - } - } catch (SQLException e) { - e.printStackTrace(); - } - return disconnect; - } - - public void checkChar(int accid) { /// issue with multiple chars from same account login found by shavit, resinate - if (!YamlConfig.config.server.USE_CHARACTER_ACCOUNT_CHECK) { - return; } - - for (World w : Server.getInstance().getWorlds()) { - for (MapleCharacter chr : w.getPlayerStorage().getAllCharacters()) { - if (accid == chr.getAccountID()) { - FilePrinter.print(FilePrinter.EXPLOITS, "Player: " + chr.getName() + " has been removed from " + GameConstants.WORLD_NAMES[w.getId()] + ". Possible Dupe attempt."); - chr.getClient().forceDisconnect(); - w.getPlayerStorage().removePlayer(chr.getId()); + } + if (!serverTransition && isLoggedIn()) { + MapleSessionCoordinator.getInstance().closeSession(session, false); + updateLoginState(MapleClient.LOGIN_NOTLOGGEDIN); + session.removeAttribute(MapleClient.CLIENT_KEY); // prevents double dcing during login + + clear(); + } else { + if (session.containsAttribute(MapleClient.CLIENT_KEY)) { + MapleSessionCoordinator.getInstance().closeSession(session, false); + session.removeAttribute(MapleClient.CLIENT_KEY); + } + + if (!Server.getInstance().hasCharacteridInTransition(this)) { + updateLoginState(MapleClient.LOGIN_NOTLOGGEDIN); + } + + engines = null; // thanks Tochi for pointing out a NPE here + } + } + + private void clear() { + // player hard reference removal thanks to Steve (kaito1410) + if (this.player != null) { + this.player.empty(true); // clears schedules and stuff + } + + Server.getInstance().unregisterLoginState(this); + + this.accountName = null; + this.macs = null; + this.hwid = null; + this.birthday = null; + this.engines = null; + this.player = null; + this.receive = null; + this.send = null; + //this.session = null; + } + + public void setCharacterOnSessionTransitionState(int cid) { + this.updateLoginState(MapleClient.LOGIN_SERVER_TRANSITION); + session.setAttribute(MapleClient.CLIENT_TRANSITION); + Server.getInstance().setCharacteridInTransition(this, cid); + } + + public int getChannel() { + return channel; + } + + public Channel getChannelServer() { + return Server.getInstance().getChannel(world, channel); + } + + public World getWorldServer() { + return Server.getInstance().getWorld(world); + } + + public Channel getChannelServer(byte channel) { + return Server.getInstance().getChannel(world, channel); + } + + public boolean deleteCharacter(int cid, int senderAccId) { + try { + MapleCharacter chr = MapleCharacter.loadCharFromDB(cid, this, false); + + Integer partyid = chr.getWorldServer().getCharacterPartyid(cid); + if (partyid != null) { + this.setPlayer(chr); + + MapleParty party = chr.getWorldServer().getParty(partyid); + chr.setParty(party); + chr.getMPC(); + chr.leaveParty(); // thanks Vcoc for pointing out deleted characters would still stay in a party + + this.setPlayer(null); + } + + return MapleCharacter.deleteCharFromDB(chr, senderAccId); + } catch (SQLException ex) { + ex.printStackTrace(); + return false; + } + } + + public String getAccountName() { + return accountName; + } + + public void setAccountName(String a) { + this.accountName = a; + } + + public void setChannel(int channel) { + this.channel = channel; + } + + public int getWorld() { + return world; + } + + public void setWorld(int world) { + this.world = world; + } + + public void pongReceived() { + lastPong = Server.getInstance().getCurrentTime(); + } + + public void testPing(long timeThen) { + try { + if (lastPong < timeThen) { + if (session != null && session.isConnected()) { + MapleSessionCoordinator.getInstance().closeSession(session, false); + updateLoginState(MapleClient.LOGIN_NOTLOGGEDIN); + session.removeAttribute(MapleClient.CLIENT_KEY); + } + } + } catch (NullPointerException e) { + e.printStackTrace(); + } + } + + public String getHWID() { + return hwid; + } + + public void setHWID(String hwid) { + this.hwid = hwid; + } + + public Set getMacs() { + return Collections.unmodifiableSet(macs); + } + + public int getGMLevel() { + return gmlevel; + } + + public void setGMLevel(int level) { + gmlevel = level; + } + + public void setScriptEngine(String name, ScriptEngine e) { + engines.put(name, e); + } + + public ScriptEngine getScriptEngine(String name) { + return engines.get(name); + } + + public void removeScriptEngine(String name) { + engines.remove(name); + } + + public NPCConversationManager getCM() { + return NPCScriptManager.getInstance().getCM(this); + } + + public QuestActionManager getQM() { + return QuestScriptManager.getInstance().getQM(this); + } + + public boolean acceptToS() { + if (accountName == null) { + return true; + } + + boolean disconnect = false; + try (Connection con = DatabaseConnection.getConnection()) { + try (PreparedStatement ps = con.prepareStatement("SELECT `tos` FROM accounts WHERE id = ?")) { + ps.setInt(1, accId); + + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) { + if (rs.getByte("tos") == 1) { + disconnect = true; + } } } } + + try (PreparedStatement ps = con.prepareStatement("UPDATE accounts SET tos = 1 WHERE id = ?")) { + ps.setInt(1, accId); + ps.executeUpdate(); + } + } catch (SQLException e) { + e.printStackTrace(); + } + return disconnect; + } + + public void checkChar(int accid) { /// issue with multiple chars from same account login found by shavit, resinate + if (!YamlConfig.config.server.USE_CHARACTER_ACCOUNT_CHECK) { + return; } - public int getVotePoints() { - int points = 0; - try (Connection con = DatabaseConnection.getConnection(); - PreparedStatement ps = con.prepareStatement("SELECT `votepoints` FROM accounts WHERE id = ?")) { - ps.setInt(1, accId); - - try (ResultSet rs = ps.executeQuery()) { - if (rs.next()) { - points = rs.getInt("votepoints"); - } - } - } catch (SQLException e) { - e.printStackTrace(); - } - votePoints = points; - return votePoints; - } - - public void addVotePoints(int points) { - votePoints += points; - saveVotePoints(); - } - - public void useVotePoints(int points){ - if (points > votePoints){ - //Should not happen, should probably log this - return; - } - votePoints -= points; - saveVotePoints(); - LogHelper.logLeaf(player, false, Integer.toString(points)); - } - - private void saveVotePoints() { - try (Connection con = DatabaseConnection.getConnection(); - PreparedStatement ps = con.prepareStatement("UPDATE accounts SET votepoints = ? WHERE id = ?")) { - ps.setInt(1, votePoints); - ps.setInt(2, accId); - ps.executeUpdate(); - } catch (SQLException e) { - e.printStackTrace(); - } - } - - public void lockClient() { - lock.lock(); - } - - public void unlockClient() { - lock.unlock(); - } - - public boolean tryacquireClient() { - if (actionsSemaphore.tryAcquire()) { - lockClient(); - return true; - } else { - return false; + for (World w : Server.getInstance().getWorlds()) { + for (MapleCharacter chr : w.getPlayerStorage().getAllCharacters()) { + if (accid == chr.getAccountID()) { + FilePrinter.print(FilePrinter.EXPLOITS, "Player: " + chr.getName() + " has been removed from " + GameConstants.WORLD_NAMES[w.getId()] + ". Possible Dupe attempt."); + chr.getClient().forceDisconnect(); + w.getPlayerStorage().removePlayer(chr.getId()); } - } - - public void releaseClient() { - unlockClient(); - actionsSemaphore.release(); + } } - - public boolean tryacquireEncoder() { - if (actionsSemaphore.tryAcquire()) { - encoderLock.lock(); - return true; - } else { - return false; + } + + public int getVotePoints() { + int points = 0; + try (Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("SELECT `votepoints` FROM accounts WHERE id = ?")) { + ps.setInt(1, accId); + + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) { + points = rs.getInt("votepoints"); } - } - - public void unlockEncoder() { - encoderLock.unlock(); - actionsSemaphore.release(); - } + } + } catch (SQLException e) { + e.printStackTrace(); + } + votePoints = points; + return votePoints; + } - private static class CharNameAndId { + public void addVotePoints(int points) { + votePoints += points; + saveVotePoints(); + } - public String name; - public int id; + public void useVotePoints(int points) { + if (points > votePoints) { + //Should not happen, should probably log this + return; + } + votePoints -= points; + saveVotePoints(); + LogHelper.logLeaf(player, false, Integer.toString(points)); + } - public CharNameAndId(String name, int id) { - super(); - this.name = name; - this.id = id; - } - } + private void saveVotePoints() { + try (Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("UPDATE accounts SET votepoints = ? WHERE id = ?")) { + ps.setInt(1, votePoints); + ps.setInt(2, accId); + ps.executeUpdate(); + } catch (SQLException e) { + e.printStackTrace(); + } + } - private static boolean checkHash(String hash, String type, String password) { - try { - MessageDigest digester = MessageDigest.getInstance(type); - digester.update(password.getBytes("UTF-8"), 0, password.length()); - return HexTool.toString(digester.digest()).replace(" ", "").toLowerCase().equals(hash); - } catch (NoSuchAlgorithmException | UnsupportedEncodingException e) { - throw new RuntimeException("Encoding the string failed", e); - } - } + public void lockClient() { + lock.lock(); + } + + public void unlockClient() { + lock.unlock(); + } + + public boolean tryacquireClient() { + if (actionsSemaphore.tryAcquire()) { + lockClient(); + return true; + } else { + return false; + } + } + + public void releaseClient() { + unlockClient(); + actionsSemaphore.release(); + } + + public boolean tryacquireEncoder() { + if (actionsSemaphore.tryAcquire()) { + encoderLock.lock(); + return true; + } else { + return false; + } + } + + public void unlockEncoder() { + encoderLock.unlock(); + actionsSemaphore.release(); + } + + private static class CharNameAndId { + + public String name; + public int id; + + public CharNameAndId(String name, int id) { + super(); + this.name = name; + this.id = id; + } + } + + private static boolean checkHash(String hash, String type, String password) { + try { + MessageDigest digester = MessageDigest.getInstance(type); + digester.update(password.getBytes(StandardCharsets.UTF_8), 0, password.length()); + return HexTool.toString(digester.digest()).replace(" ", "").toLowerCase().equals(hash); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("Encoding the string failed", e); + } + } + + public short getAvailableCharacterSlots() { + return (short) Math.max(0, characterSlots - Server.getInstance().getAccountCharacterCount(accId)); + } + + public short getAvailableCharacterWorldSlots() { + return (short) Math.max(0, characterSlots - Server.getInstance().getAccountWorldCharacterCount(accId, world)); + } - public short getAvailableCharacterSlots() { - return (short) Math.max(0, characterSlots - Server.getInstance().getAccountCharacterCount(accId)); - } - - public short getAvailableCharacterWorldSlots() { - return (short) Math.max(0, characterSlots - Server.getInstance().getAccountWorldCharacterCount(accId, world)); - } - public short getAvailableCharacterWorldSlots(int world) { return (short) Math.max(0, characterSlots - Server.getInstance().getAccountWorldCharacterCount(accId, world)); } - - public short getCharacterSlots() { - return characterSlots; - } - - public void setCharacterSlots(byte slots) { - characterSlots = slots; - } - - public boolean canGainCharacterSlot() { - return characterSlots < 15; - } - public synchronized boolean gainCharacterSlot() { - if (canGainCharacterSlot()) { - try (Connection con = DatabaseConnection.getConnection(); - PreparedStatement ps = con.prepareStatement("UPDATE accounts SET characterslots = ? WHERE id = ?")) { - ps.setInt(1, this.characterSlots += 1); - ps.setInt(2, accId); - ps.executeUpdate(); + public short getCharacterSlots() { + return characterSlots; + } - } catch (SQLException e) { - e.printStackTrace(); - } - return true; - } - return false; - } + public void setCharacterSlots(byte slots) { + characterSlots = slots; + } - public final byte getGReason() { - try (Connection con = DatabaseConnection.getConnection(); - PreparedStatement ps = con.prepareStatement("SELECT `greason` FROM `accounts` WHERE id = ?")) { - ps.setInt(1, accId); + public boolean canGainCharacterSlot() { + return characterSlots < 15; + } - try (ResultSet rs = ps.executeQuery()) { - if (rs.next()) { - return rs.getByte("greason"); - } - } - } catch (SQLException e) { - e.printStackTrace(); - } - return 0; - } + public synchronized boolean gainCharacterSlot() { + if (canGainCharacterSlot()) { + try (Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("UPDATE accounts SET characterslots = ? WHERE id = ?")) { + ps.setInt(1, this.characterSlots += 1); + ps.setInt(2, accId); + ps.executeUpdate(); - public byte getGender() { - return gender; - } - - public void setGender(byte m) { - this.gender = m; - - try (Connection con = DatabaseConnection.getConnection(); - PreparedStatement ps = con.prepareStatement("UPDATE accounts SET gender = ? WHERE id = ?")) { - ps.setByte(1, gender); - ps.setInt(2, accId); - ps.executeUpdate(); - } catch (SQLException e) { - e.printStackTrace(); - } - } - - private void announceDisableServerMessage() { - if(!this.getWorldServer().registerDisabledServerMessage(player.getId())) { - announce(MaplePacketCreator.serverMessage("")); + } catch (SQLException e) { + e.printStackTrace(); } + return true; } - - public void announceServerMessage() { - announce(MaplePacketCreator.serverMessage(this.getChannelServer().getServerMessage())); - } - - public synchronized void announceBossHpBar(MapleMonster mm, final int mobHash, final byte[] packet) { - long timeNow = System.currentTimeMillis(); - int targetHash = player.getTargetHpBarHash(); - - if(mobHash != targetHash) { - if(timeNow - player.getTargetHpBarTime() >= 5 * 1000) { - // is there a way to INTERRUPT this annoying thread running on the client that drops the boss bar after some time at every attack? - announceDisableServerMessage(); - announce(packet); - - player.setTargetHpBarHash(mobHash); - player.setTargetHpBarTime(timeNow); - } - } else { - announceDisableServerMessage(); - announce(packet); - - player.setTargetHpBarTime(timeNow); + return false; + } + + public final byte getGReason() { + try (Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("SELECT `greason` FROM `accounts` WHERE id = ?")) { + ps.setInt(1, accId); + + try (ResultSet rs = ps.executeQuery()) { + if (rs.next()) { + return rs.getByte("greason"); } - } - - public void announce(final byte[] packet) { // thanks GitGud for noticing an opportunity for improvement by overcoming "synchronized announce" - announcerLock.lock(); - try { - session.write(packet); - } finally { - announcerLock.unlock(); - } - } + } + } catch (SQLException e) { + e.printStackTrace(); + } + return 0; + } - public void announceHint(String msg, int length) { - announce(MaplePacketCreator.sendHint(msg, length, 10)); - announce(MaplePacketCreator.enableActions()); + public byte getGender() { + return gender; + } + + public void setGender(byte m) { + this.gender = m; + + try (Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("UPDATE accounts SET gender = ? WHERE id = ?")) { + ps.setByte(1, gender); + ps.setInt(2, accId); + ps.executeUpdate(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + + private void announceDisableServerMessage() { + if (!this.getWorldServer().registerDisabledServerMessage(player.getId())) { + announce(MaplePacketCreator.serverMessage("")); + } + } + + public void announceServerMessage() { + announce(MaplePacketCreator.serverMessage(this.getChannelServer().getServerMessage())); + } + + public synchronized void announceBossHpBar(MapleMonster mm, final int mobHash, final byte[] packet) { + long timeNow = System.currentTimeMillis(); + int targetHash = player.getTargetHpBarHash(); + + if (mobHash != targetHash) { + if (timeNow - player.getTargetHpBarTime() >= 5 * 1000) { + // is there a way to INTERRUPT this annoying thread running on the client that drops the boss bar after some time at every attack? + announceDisableServerMessage(); + announce(packet); + + player.setTargetHpBarHash(mobHash); + player.setTargetHpBarTime(timeNow); + } + } else { + announceDisableServerMessage(); + announce(packet); + + player.setTargetHpBarTime(timeNow); + } + } + + public void announce(final byte[] packet) { // thanks GitGud for noticing an opportunity for improvement by overcoming "synchronized announce" + announcerLock.lock(); + try { + session.write(packet); + } finally { + announcerLock.unlock(); + } + } + + public void announceHint(String msg, int length) { + announce(MaplePacketCreator.sendHint(msg, length, 10)); + announce(MaplePacketCreator.enableActions()); + } + + public void changeChannel(int channel) { + Server server = Server.getInstance(); + if (player.isBanned()) { + disconnect(false, false); + return; + } + if (!player.isAlive() || FieldLimit.CANNOTMIGRATE.check(player.getMap().getFieldLimit())) { + announce(MaplePacketCreator.enableActions()); + return; + } else if (MapleMiniDungeonInfo.isDungeonMap(player.getMapId())) { + announce(MaplePacketCreator.serverNotice(5, "Changing channels or entering Cash Shop or MTS are disabled when inside a Mini-Dungeon.")); + announce(MaplePacketCreator.enableActions()); + return; } - public void changeChannel(int channel) { - Server server = Server.getInstance(); - if (player.isBanned()) { - disconnect(false, false); - return; - } - if (!player.isAlive() || FieldLimit.CANNOTMIGRATE.check(player.getMap().getFieldLimit())) { - announce(MaplePacketCreator.enableActions()); - return; - } else if(MapleMiniDungeonInfo.isDungeonMap(player.getMapId())) { - announce(MaplePacketCreator.serverNotice(5, "Changing channels or entering Cash Shop or MTS are disabled when inside a Mini-Dungeon.")); - announce(MaplePacketCreator.enableActions()); - return; - } - - String[] socket = Server.getInstance().getInetSocket(this.getSession(), getWorld(), channel); - if(socket == null) { - announce(MaplePacketCreator.serverNotice(1, "Channel " + channel + " is currently disabled. Try another channel.")); - announce(MaplePacketCreator.enableActions()); - return; - } - - player.closePlayerInteractions(); - player.closePartySearchInteractions(); - - player.unregisterChairBuff(); - server.getPlayerBuffStorage().addBuffsToStorage(player.getId(), player.getAllBuffs()); - server.getPlayerBuffStorage().addDiseasesToStorage(player.getId(), player.getAllDiseases()); - player.setDisconnectedFromChannelWorld(); - player.notifyMapTransferToPartner(-1); - player.removeIncomingInvites(); - player.cancelAllBuffs(true); - player.cancelAllDebuffs(); - player.cancelBuffExpireTask(); - player.cancelDiseaseExpireTask(); - player.cancelSkillCooldownTask(); - player.cancelQuestExpirationTask(); - //Cancelling magicdoor? Nope - //Cancelling mounts? Noty - - player.getInventory(MapleInventoryType.EQUIPPED).checked(false); //test - player.getMap().removePlayer(player); - player.clearBanishPlayerData(); - player.getClient().getChannelServer().removePlayer(player); - - player.saveCharToDB(); - - player.setSessionTransitionState(); - try { - announce(MaplePacketCreator.getChannelChange(InetAddress.getByName(socket[0]), Integer.parseInt(socket[1]))); - } catch (IOException e) { - e.printStackTrace(); - } - } - - public long getSessionId() { - return this.sessionId; - } - - public void setSessionId(long sessionId) { - this.sessionId = sessionId; - } - - public boolean canRequestCharlist(){ - return lastNpcClick + 877 < Server.getInstance().getCurrentTime(); - } - - public boolean canClickNPC(){ - return lastNpcClick + 500 < Server.getInstance().getCurrentTime(); - } - - public void setClickedNPC(){ - lastNpcClick = Server.getInstance().getCurrentTime(); - } - - public void removeClickedNPC(){ - lastNpcClick = 0; - } - - public int getVisibleWorlds(){ - return visibleWorlds; - } - - public void requestedServerlist(int worlds) { - visibleWorlds = worlds; - setClickedNPC(); - } - - public void closePlayerScriptInteractions() { - this.removeClickedNPC(); - NPCScriptManager.getInstance().dispose(this); - QuestScriptManager.getInstance().dispose(this); - } - - public boolean attemptCsCoupon() { - if (csattempt > 2) { - resetCsCoupon(); - return false; - } - - csattempt++; - return true; - } - - public void resetCsCoupon() { - csattempt = 0; - } - - public void enableCSActions() { - announce(MaplePacketCreator.enableCSUse(player)); - } - - public String getNibbleHWID() { - return (String) session.getAttribute(MapleClient.CLIENT_NIBBLEHWID); - } - - public boolean canBypassPin() { - return MapleLoginBypassCoordinator.getInstance().canLoginBypass(getNibbleHWID(), accId, false); - } - - public boolean canBypassPic() { - return MapleLoginBypassCoordinator.getInstance().canLoginBypass(getNibbleHWID(), accId, true); - } - - public int getLanguage() { - return lang; + String[] socket = Server.getInstance().getInetSocket(this.getSession(), getWorld(), channel); + if (socket == null) { + announce(MaplePacketCreator.serverNotice(1, "Channel " + channel + " is currently disabled. Try another channel.")); + announce(MaplePacketCreator.enableActions()); + return; } - public void setLanguage(int lingua) { - this.lang = lingua; + player.closePlayerInteractions(); + player.closePartySearchInteractions(); + + player.unregisterChairBuff(); + server.getPlayerBuffStorage().addBuffsToStorage(player.getId(), player.getAllBuffs()); + server.getPlayerBuffStorage().addDiseasesToStorage(player.getId(), player.getAllDiseases()); + player.setDisconnectedFromChannelWorld(); + player.notifyMapTransferToPartner(-1); + player.removeIncomingInvites(); + player.cancelAllBuffs(true); + player.cancelAllDebuffs(); + player.cancelBuffExpireTask(); + player.cancelDiseaseExpireTask(); + player.cancelSkillCooldownTask(); + player.cancelQuestExpirationTask(); + //Cancelling magicdoor? Nope + //Cancelling mounts? Noty + + player.getInventory(MapleInventoryType.EQUIPPED).checked(false); //test + player.getMap().removePlayer(player); + player.clearBanishPlayerData(); + player.getClient().getChannelServer().removePlayer(player); + + player.saveCharToDB(); + + player.setSessionTransitionState(); + try { + announce(MaplePacketCreator.getChannelChange(InetAddress.getByName(socket[0]), Integer.parseInt(socket[1]))); + } catch (IOException e) { + e.printStackTrace(); } + } + + public long getSessionId() { + return this.sessionId; + } + + public void setSessionId(long sessionId) { + this.sessionId = sessionId; + } + + public boolean canRequestCharlist() { + return lastNpcClick + 877 < Server.getInstance().getCurrentTime(); + } + + public boolean canClickNPC() { + return lastNpcClick + 500 < Server.getInstance().getCurrentTime(); + } + + public void setClickedNPC() { + lastNpcClick = Server.getInstance().getCurrentTime(); + } + + public void removeClickedNPC() { + lastNpcClick = 0; + } + + public int getVisibleWorlds() { + return visibleWorlds; + } + + public void requestedServerlist(int worlds) { + visibleWorlds = worlds; + setClickedNPC(); + } + + public void closePlayerScriptInteractions() { + this.removeClickedNPC(); + NPCScriptManager.getInstance().dispose(this); + QuestScriptManager.getInstance().dispose(this); + } + + public boolean attemptCsCoupon() { + if (csattempt > 2) { + resetCsCoupon(); + return false; + } + + csattempt++; + return true; + } + + public void resetCsCoupon() { + csattempt = 0; + } + + public void enableCSActions() { + announce(MaplePacketCreator.enableCSUse(player)); + } + + public String getNibbleHWID() { + return (String) session.getAttribute(MapleClient.CLIENT_NIBBLEHWID); + } + + public boolean canBypassPin() { + return MapleLoginBypassCoordinator.getInstance().canLoginBypass(getNibbleHWID(), accId, false); + } + + public boolean canBypassPic() { + return MapleLoginBypassCoordinator.getInstance().canLoginBypass(getNibbleHWID(), accId, true); + } + + public int getLanguage() { + return lang; + } + + public void setLanguage(int lingua) { + this.lang = lingua; + } } \ No newline at end of file From 23bad12f8c47aaa3d9718c1dff89f14f86c97546 Mon Sep 17 00:00:00 2001 From: P0nk Date: Tue, 15 Jun 2021 22:12:23 +0200 Subject: [PATCH 02/37] Netty WIP Implementing ByteBuf backed In/OutPacket first in a separate branch --- pom.xml | 45 +++++--- src/main/java/client/MapleClient.java | 26 ++++- .../java/constants/net/ServerConstants.java | 2 +- src/main/java/net/MapleServerHandler.java | 7 +- src/main/java/net/netty/ClientCyphers.java | 28 +++++ .../java/net/netty/ClientInitializer.java | 20 ++++ src/main/java/net/netty/InPacket.java | 4 + .../java/net/netty/InitializationVector.java | 27 +++++ .../netty/InvalidPacketHeaderException.java | 14 +++ src/main/java/net/netty/OutPacket.java | 5 + src/main/java/net/netty/PacketDecoder.java | 48 ++++++++ src/main/java/net/netty/PacketEncoder.java | 28 +++++ src/main/java/tools/MapleAESOFB.java | 106 +++++++++++------- src/main/java/tools/MaplePacketCreator.java | 7 +- 14 files changed, 301 insertions(+), 66 deletions(-) create mode 100644 src/main/java/net/netty/ClientCyphers.java create mode 100644 src/main/java/net/netty/ClientInitializer.java create mode 100644 src/main/java/net/netty/InPacket.java create mode 100644 src/main/java/net/netty/InitializationVector.java create mode 100644 src/main/java/net/netty/InvalidPacketHeaderException.java create mode 100644 src/main/java/net/netty/OutPacket.java create mode 100644 src/main/java/net/netty/PacketDecoder.java create mode 100644 src/main/java/net/netty/PacketEncoder.java diff --git a/pom.xml b/pom.xml index 7df287a87f..488df03163 100644 --- a/pom.xml +++ b/pom.xml @@ -22,21 +22,6 @@ - - com.zaxxer - HikariCP - 4.0.3 - - - org.apache.mina - mina-core - 2.1.3 - - - mysql - mysql-connector-java - 8.0.23 - com.esotericsoftware.yamlbeans yamlbeans @@ -48,6 +33,36 @@ 1.0 + + + com.zaxxer + HikariCP + 4.0.3 + + + mysql + mysql-connector-java + 8.0.23 + + + + + org.apache.mina + mina-core + 2.1.3 + + + io.netty + netty-transport + 4.1.65.Final + + + io.netty + netty-codec + 4.1.65.Final + + + org.slf4j diff --git a/src/main/java/client/MapleClient.java b/src/main/java/client/MapleClient.java index f158759f96..203a47b826 100644 --- a/src/main/java/client/MapleClient.java +++ b/src/main/java/client/MapleClient.java @@ -24,6 +24,10 @@ package client; import client.inventory.MapleInventoryType; import config.YamlConfig; import constants.game.GameConstants; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInboundHandlerAdapter; +import net.netty.ClientCyphers; +import net.netty.InvalidPacketHeaderException; import net.server.Server; import net.server.audit.locks.MonitoredLockType; import net.server.audit.locks.factory.MonitoredReentrantLockFactory; @@ -61,7 +65,7 @@ import java.util.*; import java.util.concurrent.Semaphore; import java.util.concurrent.locks.Lock; -public class MapleClient { +public class MapleClient extends ChannelInboundHandlerAdapter { public static final int LOGIN_NOTLOGGEDIN = 0; public static final int LOGIN_SERVER_TRANSITION = 1; @@ -118,12 +122,32 @@ public class MapleClient { return lastPacket; } + public MapleClient(ClientCyphers cyphers) { + this.send = cyphers.getSendCypher(); + this.receive = cyphers.getReceiveCypher(); + this.session = null; // TODO remove once the other constructor is removed + } + public MapleClient(MapleAESOFB send, MapleAESOFB receive, IoSession session) { this.send = send; this.receive = receive; this.session = session; } + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + super.channelRead(ctx, msg); + } + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + if (cause instanceof InvalidPacketHeaderException) { + // TODO close session through MapleSessionCoordinator + } + + super.exceptionCaught(ctx, cause); + } + public MapleAESOFB getReceiveCrypto() { return receive; } diff --git a/src/main/java/constants/net/ServerConstants.java b/src/main/java/constants/net/ServerConstants.java index 2905adbccd..9b5f6b1d8b 100644 --- a/src/main/java/constants/net/ServerConstants.java +++ b/src/main/java/constants/net/ServerConstants.java @@ -3,7 +3,7 @@ package constants.net; public class ServerConstants { //Server Version - public static short VERSION = 83; + public static final short VERSION = 83; //Debug Variables public static int[] DEBUG_VALUES = new int[10]; // Field designed for packet testing purposes diff --git a/src/main/java/net/MapleServerHandler.java b/src/main/java/net/MapleServerHandler.java index edca4bfda8..554366803d 100644 --- a/src/main/java/net/MapleServerHandler.java +++ b/src/main/java/net/MapleServerHandler.java @@ -24,6 +24,7 @@ package net; import client.MapleClient; import config.YamlConfig; import constants.net.ServerConstants; +import net.netty.InitializationVector; import net.server.Server; import net.server.audit.LockCollector; import net.server.audit.locks.MonitoredLockType; @@ -130,10 +131,8 @@ public class MapleServerHandler extends IoHandlerAdapter { FilePrinter.print(FilePrinter.SESSION, "IoSession with " + session.getRemoteAddress() + " opened on " + sdf.format(Calendar.getInstance().getTime()), false); } - byte[] ivRecv = {70, 114, 122, 82}; - byte[] ivSend = {82, 48, 120, 115}; - ivRecv[3] = (byte) (Math.random() * 255); - ivSend[3] = (byte) (Math.random() * 255); + final InitializationVector ivSend = InitializationVector.generateSend(); + final InitializationVector ivRecv = InitializationVector.generateReceive(); MapleAESOFB sendCypher = new MapleAESOFB(ivSend, (short) (0xFFFF - ServerConstants.VERSION)); MapleAESOFB recvCypher = new MapleAESOFB(ivRecv, ServerConstants.VERSION); MapleClient client = new MapleClient(sendCypher, recvCypher, session); diff --git a/src/main/java/net/netty/ClientCyphers.java b/src/main/java/net/netty/ClientCyphers.java new file mode 100644 index 0000000000..ea97b44ded --- /dev/null +++ b/src/main/java/net/netty/ClientCyphers.java @@ -0,0 +1,28 @@ +package net.netty; + +import constants.net.ServerConstants; +import tools.MapleAESOFB; + +public class ClientCyphers { + private final MapleAESOFB send; + private final MapleAESOFB receive; + + private ClientCyphers(MapleAESOFB send, MapleAESOFB receive) { + this.send = send; + this.receive = receive; + } + + public static ClientCyphers generateNew() { + MapleAESOFB send = new MapleAESOFB(InitializationVector.generateSend(), ServerConstants.VERSION); + MapleAESOFB receive = new MapleAESOFB(InitializationVector.generateReceive(), ServerConstants.VERSION); + return new ClientCyphers(send, receive); + } + + public MapleAESOFB getSendCypher() { + return send; + } + + public MapleAESOFB getReceiveCypher() { + return receive; + } +} diff --git a/src/main/java/net/netty/ClientInitializer.java b/src/main/java/net/netty/ClientInitializer.java new file mode 100644 index 0000000000..2c52725cbe --- /dev/null +++ b/src/main/java/net/netty/ClientInitializer.java @@ -0,0 +1,20 @@ +package net.netty; + +import client.MapleClient; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.socket.SocketChannel; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ClientInitializer extends ChannelInitializer { + private static final Logger log = LoggerFactory.getLogger(ClientInitializer.class); + + @Override + public void initChannel(SocketChannel socketChannel) { + final String clientIp = socketChannel.remoteAddress().getHostName(); + log.debug("Client initiated new connection from: {}", clientIp); + + MapleClient client = new MapleClient(ClientCyphers.generateNew()); + socketChannel.pipeline().addLast("MapleClient", client); + } +} diff --git a/src/main/java/net/netty/InPacket.java b/src/main/java/net/netty/InPacket.java new file mode 100644 index 0000000000..b4c536cc59 --- /dev/null +++ b/src/main/java/net/netty/InPacket.java @@ -0,0 +1,4 @@ +package net.netty; + +public class InPacket { +} diff --git a/src/main/java/net/netty/InitializationVector.java b/src/main/java/net/netty/InitializationVector.java new file mode 100644 index 0000000000..22cd624db1 --- /dev/null +++ b/src/main/java/net/netty/InitializationVector.java @@ -0,0 +1,27 @@ +package net.netty; + +public class InitializationVector { + private final byte[] bytes; + + private InitializationVector(byte[] bytes) { + this.bytes = bytes; + } + + public byte[] getBytes() { + return bytes; + } + + public static InitializationVector generateSend() { + byte[] ivSend = {82, 48, 120, getRandomByte()}; + return new InitializationVector(ivSend); + } + + public static InitializationVector generateReceive() { + byte[] ivRecv = {70, 114, 122, getRandomByte()}; + return new InitializationVector(ivRecv); + } + + private static byte getRandomByte() { + return (byte) (Math.random() * 255); + } +} diff --git a/src/main/java/net/netty/InvalidPacketHeaderException.java b/src/main/java/net/netty/InvalidPacketHeaderException.java new file mode 100644 index 0000000000..7eed5f3992 --- /dev/null +++ b/src/main/java/net/netty/InvalidPacketHeaderException.java @@ -0,0 +1,14 @@ +package net.netty; + +public class InvalidPacketHeaderException extends RuntimeException { + private final int header; + + public InvalidPacketHeaderException(String message, int header) { + super(message); + this.header = header; + } + + public int getHeader() { + return header; + } +} diff --git a/src/main/java/net/netty/OutPacket.java b/src/main/java/net/netty/OutPacket.java new file mode 100644 index 0000000000..f7f8f1dc46 --- /dev/null +++ b/src/main/java/net/netty/OutPacket.java @@ -0,0 +1,5 @@ +package net.netty; + +public interface OutPacket { + byte[] getBytes(); +} diff --git a/src/main/java/net/netty/PacketDecoder.java b/src/main/java/net/netty/PacketDecoder.java new file mode 100644 index 0000000000..99a30bb9f3 --- /dev/null +++ b/src/main/java/net/netty/PacketDecoder.java @@ -0,0 +1,48 @@ +package net.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.ReplayingDecoder; +import net.mina.MapleCustomEncryption; +import tools.MapleAESOFB; + +import java.util.List; + +public class PacketDecoder extends ReplayingDecoder { + private final MapleAESOFB receiveCypher; + + public PacketDecoder(MapleAESOFB receiveCypher) { + this.receiveCypher = receiveCypher; + } + + @Override + protected void decode(ChannelHandlerContext context, ByteBuf in, List out) { + final int header = in.readInt(); + + if (!receiveCypher.checkPacket(header)) { + throw new InvalidPacketHeaderException("Attempted to decode a packet with an invalid header", header); + } + + int packetLength = decodePacketLength(header); + byte[] packet = new byte[packetLength]; + in.readBytes(packet); + receiveCypher.crypt(packet); + MapleCustomEncryption.decryptData(packet); + out.add(packet); + // TODO conditionally log the packet + } + + /** + * @param header Packet header - the first 4 bytes of the packet + * @return Packet size in bytes + */ + private static int decodePacketLength(byte[] header) { + return (((header[1] ^ header[3]) & 0xFF) << 8) | ((header[0] ^ header[2]) & 0xFF); + } + + private static int decodePacketLength(int header) { + int length = ((header >>> 16) ^ (header & 0xFFFF)); + length = ((length << 8) & 0xFF00) | ((length >>> 8) & 0xFF); + return length; + } +} diff --git a/src/main/java/net/netty/PacketEncoder.java b/src/main/java/net/netty/PacketEncoder.java new file mode 100644 index 0000000000..10bb25724e --- /dev/null +++ b/src/main/java/net/netty/PacketEncoder.java @@ -0,0 +1,28 @@ +package net.netty; + +import io.netty.buffer.ByteBuf; +import io.netty.channel.ChannelHandlerContext; +import io.netty.handler.codec.MessageToByteEncoder; +import net.mina.MapleCustomEncryption; +import tools.MapleAESOFB; + +public class PacketEncoder extends MessageToByteEncoder { + private final MapleAESOFB sendCypher; + + public PacketEncoder(MapleAESOFB sendCypher) { + this.sendCypher = sendCypher; + } + + @Override + protected void encode(ChannelHandlerContext ctx, OutPacket in, ByteBuf out) { + byte[] packet = in.getBytes(); + out.writeBytes(getEncodedHeader(packet.length)); + MapleCustomEncryption.encryptData(packet); + sendCypher.crypt(packet); + out.writeBytes(packet); + } + + private byte[] getEncodedHeader(int length) { + return sendCypher.getPacketHeader(length); + } +} diff --git a/src/main/java/tools/MapleAESOFB.java b/src/main/java/tools/MapleAESOFB.java index 7ccfc16ac2..19ba74e279 100644 --- a/src/main/java/tools/MapleAESOFB.java +++ b/src/main/java/tools/MapleAESOFB.java @@ -21,6 +21,10 @@ */ package tools; +import net.netty.InitializationVector; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; @@ -30,52 +34,73 @@ import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; public class MapleAESOFB { - private byte[] iv; - private Cipher cipher; - private short mapleVersion; + private static final Logger log = LoggerFactory.getLogger(MapleAESOFB.class); private final static SecretKeySpec skey = new SecretKeySpec( - new byte[]{0x13, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, (byte) 0xB4, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00}, "AES"); + new byte[]{ + 0x13, 0x00, 0x00, 0x00, + 0x08, 0x00, 0x00, 0x00, + 0x06, 0x00, 0x00, 0x00, + (byte) 0xB4, 0x00, 0x00, 0x00, + 0x1B, 0x00, 0x00, 0x00, + 0x0F, 0x00, 0x00, 0x00, + 0x33, 0x00, 0x00, 0x00, + 0x52, 0x00, 0x00, 0x00}, "AES"); - private static final byte[] funnyBytes = new byte[]{(byte) 0xEC, (byte) 0x3F, (byte) 0x77, (byte) 0xA4, (byte) 0x45, (byte) 0xD0, (byte) 0x71, (byte) 0xBF, (byte) 0xB7, (byte) 0x98, (byte) 0x20, (byte) 0xFC, - (byte) 0x4B, (byte) 0xE9, (byte) 0xB3, (byte) 0xE1, (byte) 0x5C, (byte) 0x22, (byte) 0xF7, (byte) 0x0C, (byte) 0x44, (byte) 0x1B, (byte) 0x81, (byte) 0xBD, (byte) 0x63, (byte) 0x8D, (byte) 0xD4, (byte) 0xC3, - (byte) 0xF2, (byte) 0x10, (byte) 0x19, (byte) 0xE0, (byte) 0xFB, (byte) 0xA1, (byte) 0x6E, (byte) 0x66, (byte) 0xEA, (byte) 0xAE, (byte) 0xD6, (byte) 0xCE, (byte) 0x06, (byte) 0x18, (byte) 0x4E, (byte) 0xEB, - (byte) 0x78, (byte) 0x95, (byte) 0xDB, (byte) 0xBA, (byte) 0xB6, (byte) 0x42, (byte) 0x7A, (byte) 0x2A, (byte) 0x83, (byte) 0x0B, (byte) 0x54, (byte) 0x67, (byte) 0x6D, (byte) 0xE8, (byte) 0x65, (byte) 0xE7, - (byte) 0x2F, (byte) 0x07, (byte) 0xF3, (byte) 0xAA, (byte) 0x27, (byte) 0x7B, (byte) 0x85, (byte) 0xB0, (byte) 0x26, (byte) 0xFD, (byte) 0x8B, (byte) 0xA9, (byte) 0xFA, (byte) 0xBE, (byte) 0xA8, (byte) 0xD7, - (byte) 0xCB, (byte) 0xCC, (byte) 0x92, (byte) 0xDA, (byte) 0xF9, (byte) 0x93, (byte) 0x60, (byte) 0x2D, (byte) 0xDD, (byte) 0xD2, (byte) 0xA2, (byte) 0x9B, (byte) 0x39, (byte) 0x5F, (byte) 0x82, (byte) 0x21, - (byte) 0x4C, (byte) 0x69, (byte) 0xF8, (byte) 0x31, (byte) 0x87, (byte) 0xEE, (byte) 0x8E, (byte) 0xAD, (byte) 0x8C, (byte) 0x6A, (byte) 0xBC, (byte) 0xB5, (byte) 0x6B, (byte) 0x59, (byte) 0x13, (byte) 0xF1, - (byte) 0x04, (byte) 0x00, (byte) 0xF6, (byte) 0x5A, (byte) 0x35, (byte) 0x79, (byte) 0x48, (byte) 0x8F, (byte) 0x15, (byte) 0xCD, (byte) 0x97, (byte) 0x57, (byte) 0x12, (byte) 0x3E, (byte) 0x37, (byte) 0xFF, - (byte) 0x9D, (byte) 0x4F, (byte) 0x51, (byte) 0xF5, (byte) 0xA3, (byte) 0x70, (byte) 0xBB, (byte) 0x14, (byte) 0x75, (byte) 0xC2, (byte) 0xB8, (byte) 0x72, (byte) 0xC0, (byte) 0xED, (byte) 0x7D, (byte) 0x68, - (byte) 0xC9, (byte) 0x2E, (byte) 0x0D, (byte) 0x62, (byte) 0x46, (byte) 0x17, (byte) 0x11, (byte) 0x4D, (byte) 0x6C, (byte) 0xC4, (byte) 0x7E, (byte) 0x53, (byte) 0xC1, (byte) 0x25, (byte) 0xC7, (byte) 0x9A, - (byte) 0x1C, (byte) 0x88, (byte) 0x58, (byte) 0x2C, (byte) 0x89, (byte) 0xDC, (byte) 0x02, (byte) 0x64, (byte) 0x40, (byte) 0x01, (byte) 0x5D, (byte) 0x38, (byte) 0xA5, (byte) 0xE2, (byte) 0xAF, (byte) 0x55, - (byte) 0xD5, (byte) 0xEF, (byte) 0x1A, (byte) 0x7C, (byte) 0xA7, (byte) 0x5B, (byte) 0xA6, (byte) 0x6F, (byte) 0x86, (byte) 0x9F, (byte) 0x73, (byte) 0xE6, (byte) 0x0A, (byte) 0xDE, (byte) 0x2B, (byte) 0x99, - (byte) 0x4A, (byte) 0x47, (byte) 0x9C, (byte) 0xDF, (byte) 0x09, (byte) 0x76, (byte) 0x9E, (byte) 0x30, (byte) 0x0E, (byte) 0xE4, (byte) 0xB2, (byte) 0x94, (byte) 0xA0, (byte) 0x3B, (byte) 0x34, (byte) 0x1D, - (byte) 0x28, (byte) 0x0F, (byte) 0x36, (byte) 0xE3, (byte) 0x23, (byte) 0xB4, (byte) 0x03, (byte) 0xD8, (byte) 0x90, (byte) 0xC8, (byte) 0x3C, (byte) 0xFE, (byte) 0x5E, (byte) 0x32, (byte) 0x24, (byte) 0x50, - (byte) 0x1F, (byte) 0x3A, (byte) 0x43, (byte) 0x8A, (byte) 0x96, (byte) 0x41, (byte) 0x74, (byte) 0xAC, (byte) 0x52, (byte) 0x33, (byte) 0xF0, (byte) 0xD9, (byte) 0x29, (byte) 0x80, (byte) 0xB1, (byte) 0x16, - (byte) 0xD3, (byte) 0xAB, (byte) 0x91, (byte) 0xB9, (byte) 0x84, (byte) 0x7F, (byte) 0x61, (byte) 0x1E, (byte) 0xCF, (byte) 0xC5, (byte) 0xD1, (byte) 0x56, (byte) 0x3D, (byte) 0xCA, (byte) 0xF4, (byte) 0x05, - (byte) 0xC6, (byte) 0xE5, (byte) 0x08, (byte) 0x49}; + private static final byte[] funnyBytes = new byte[]{ + (byte) 0xEC, (byte) 0x3F, (byte) 0x77, (byte) 0xA4, (byte) 0x45, (byte) 0xD0, (byte) 0x71, (byte) 0xBF, + (byte) 0xB7, (byte) 0x98, (byte) 0x20, (byte) 0xFC, (byte) 0x4B, (byte) 0xE9, (byte) 0xB3, (byte) 0xE1, + (byte) 0x5C, (byte) 0x22, (byte) 0xF7, (byte) 0x0C, (byte) 0x44, (byte) 0x1B, (byte) 0x81, (byte) 0xBD, + (byte) 0x63, (byte) 0x8D, (byte) 0xD4, (byte) 0xC3, (byte) 0xF2, (byte) 0x10, (byte) 0x19, (byte) 0xE0, + (byte) 0xFB, (byte) 0xA1, (byte) 0x6E, (byte) 0x66, (byte) 0xEA, (byte) 0xAE, (byte) 0xD6, (byte) 0xCE, + (byte) 0x06, (byte) 0x18, (byte) 0x4E, (byte) 0xEB, (byte) 0x78, (byte) 0x95, (byte) 0xDB, (byte) 0xBA, + (byte) 0xB6, (byte) 0x42, (byte) 0x7A, (byte) 0x2A, (byte) 0x83, (byte) 0x0B, (byte) 0x54, (byte) 0x67, + (byte) 0x6D, (byte) 0xE8, (byte) 0x65, (byte) 0xE7, (byte) 0x2F, (byte) 0x07, (byte) 0xF3, (byte) 0xAA, + (byte) 0x27, (byte) 0x7B, (byte) 0x85, (byte) 0xB0, (byte) 0x26, (byte) 0xFD, (byte) 0x8B, (byte) 0xA9, + (byte) 0xFA, (byte) 0xBE, (byte) 0xA8, (byte) 0xD7, (byte) 0xCB, (byte) 0xCC, (byte) 0x92, (byte) 0xDA, + (byte) 0xF9, (byte) 0x93, (byte) 0x60, (byte) 0x2D, (byte) 0xDD, (byte) 0xD2, (byte) 0xA2, (byte) 0x9B, + (byte) 0x39, (byte) 0x5F, (byte) 0x82, (byte) 0x21, (byte) 0x4C, (byte) 0x69, (byte) 0xF8, (byte) 0x31, + (byte) 0x87, (byte) 0xEE, (byte) 0x8E, (byte) 0xAD, (byte) 0x8C, (byte) 0x6A, (byte) 0xBC, (byte) 0xB5, + (byte) 0x6B, (byte) 0x59, (byte) 0x13, (byte) 0xF1, (byte) 0x04, (byte) 0x00, (byte) 0xF6, (byte) 0x5A, + (byte) 0x35, (byte) 0x79, (byte) 0x48, (byte) 0x8F, (byte) 0x15, (byte) 0xCD, (byte) 0x97, (byte) 0x57, + (byte) 0x12, (byte) 0x3E, (byte) 0x37, (byte) 0xFF, (byte) 0x9D, (byte) 0x4F, (byte) 0x51, (byte) 0xF5, + (byte) 0xA3, (byte) 0x70, (byte) 0xBB, (byte) 0x14, (byte) 0x75, (byte) 0xC2, (byte) 0xB8, (byte) 0x72, + (byte) 0xC0, (byte) 0xED, (byte) 0x7D, (byte) 0x68, (byte) 0xC9, (byte) 0x2E, (byte) 0x0D, (byte) 0x62, + (byte) 0x46, (byte) 0x17, (byte) 0x11, (byte) 0x4D, (byte) 0x6C, (byte) 0xC4, (byte) 0x7E, (byte) 0x53, + (byte) 0xC1, (byte) 0x25, (byte) 0xC7, (byte) 0x9A, (byte) 0x1C, (byte) 0x88, (byte) 0x58, (byte) 0x2C, + (byte) 0x89, (byte) 0xDC, (byte) 0x02, (byte) 0x64, (byte) 0x40, (byte) 0x01, (byte) 0x5D, (byte) 0x38, + (byte) 0xA5, (byte) 0xE2, (byte) 0xAF, (byte) 0x55, (byte) 0xD5, (byte) 0xEF, (byte) 0x1A, (byte) 0x7C, + (byte) 0xA7, (byte) 0x5B, (byte) 0xA6, (byte) 0x6F, (byte) 0x86, (byte) 0x9F, (byte) 0x73, (byte) 0xE6, + (byte) 0x0A, (byte) 0xDE, (byte) 0x2B, (byte) 0x99, (byte) 0x4A, (byte) 0x47, (byte) 0x9C, (byte) 0xDF, + (byte) 0x09, (byte) 0x76, (byte) 0x9E, (byte) 0x30, (byte) 0x0E, (byte) 0xE4, (byte) 0xB2, (byte) 0x94, + (byte) 0xA0, (byte) 0x3B, (byte) 0x34, (byte) 0x1D, (byte) 0x28, (byte) 0x0F, (byte) 0x36, (byte) 0xE3, + (byte) 0x23, (byte) 0xB4, (byte) 0x03, (byte) 0xD8, (byte) 0x90, (byte) 0xC8, (byte) 0x3C, (byte) 0xFE, + (byte) 0x5E, (byte) 0x32, (byte) 0x24, (byte) 0x50, (byte) 0x1F, (byte) 0x3A, (byte) 0x43, (byte) 0x8A, + (byte) 0x96, (byte) 0x41, (byte) 0x74, (byte) 0xAC, (byte) 0x52, (byte) 0x33, (byte) 0xF0, (byte) 0xD9, + (byte) 0x29, (byte) 0x80, (byte) 0xB1, (byte) 0x16, (byte) 0xD3, (byte) 0xAB, (byte) 0x91, (byte) 0xB9, + (byte) 0x84, (byte) 0x7F, (byte) 0x61, (byte) 0x1E, (byte) 0xCF, (byte) 0xC5, (byte) 0xD1, (byte) 0x56, + (byte) 0x3D, (byte) 0xCA, (byte) 0xF4, (byte) 0x05, (byte) 0xC6, (byte) 0xE5, (byte) 0x08, (byte) 0x49}; - public MapleAESOFB(byte[] iv, short mapleVersion) { + private final short mapleVersion; + private final Cipher cipher; + private byte[] iv; + + public MapleAESOFB(InitializationVector iv, short mapleVersion) { try { cipher = Cipher.getInstance("AES"); cipher.init(Cipher.ENCRYPT_MODE, skey); - } catch (NoSuchAlgorithmException e) { - System.out.println("ERROR " + e); - } catch (NoSuchPaddingException e) { - System.out.println("ERROR " + e); - } catch (InvalidKeyException e) { - System.out.println("Error initializing the encryption cipher. Make sure you're using the Unlimited Strength cryptography jar files."); + } catch (NoSuchAlgorithmException | NoSuchPaddingException | InvalidKeyException e) { + log.warn("Cypher initialization error with skey: {}", skey, e); + throw new RuntimeException(e); } - this.setIv(iv); + + this.iv = iv.getBytes(); this.mapleVersion = (short) (((mapleVersion >> 8) & 0xFF) | ((mapleVersion << 8) & 0xFF00)); } - private void setIv(byte[] iv) { - this.iv = iv; - } - private static byte[] multiplyBytes(byte[] in, int count, int mul) { - byte[] ret = new byte[count * mul]; - for (int x = 0; x < count * mul; x++) { + final int size = count * mul; + byte[] ret = new byte[size]; + for (int x = 0; x < size; x++) { ret[x] = in[x % count]; } return ret; @@ -94,12 +119,8 @@ public class MapleAESOFB { if ((x - start) % myIv.length == 0) { try { byte[] newIv = cipher.doFinal(myIv); - for (int j = 0; j < myIv.length; j++) { - myIv[j] = newIv[j]; - } - } catch (IllegalBlockSizeException e) { - e.printStackTrace(); - } catch (BadPaddingException e) { + System.arraycopy(newIv, 0, myIv, 0, myIv.length); + } catch (IllegalBlockSizeException | BadPaddingException e) { e.printStackTrace(); } } @@ -138,7 +159,8 @@ public class MapleAESOFB { } public boolean checkPacket(byte[] packet) { - return ((((packet[0] ^ iv[2]) & 0xFF) == ((mapleVersion >> 8) & 0xFF)) && (((packet[1] ^ iv[3]) & 0xFF) == (mapleVersion & 0xFF))); + return ((((packet[0] ^ iv[2]) & 0xFF) == ((mapleVersion >> 8) & 0xFF)) && + (((packet[1] ^ iv[3]) & 0xFF) == (mapleVersion & 0xFF))); } public boolean checkPacket(int packetHeader) { diff --git a/src/main/java/tools/MaplePacketCreator.java b/src/main/java/tools/MaplePacketCreator.java index e135021a86..1f163c2efd 100644 --- a/src/main/java/tools/MaplePacketCreator.java +++ b/src/main/java/tools/MaplePacketCreator.java @@ -36,6 +36,7 @@ import constants.inventory.ItemConstants; import constants.skills.Buccaneer; import constants.skills.Corsair; import constants.skills.ThunderBreaker; +import net.netty.InitializationVector; import net.opcodes.SendOpcode; import net.server.PlayerCoolDownValueHolder; import net.server.Server; @@ -547,14 +548,14 @@ public class MaplePacketCreator { * @param recvIv the IV in use by the server for receiving * @return */ - public static byte[] getHello(short mapleVersion, byte[] sendIv, byte[] recvIv) { + public static byte[] getHello(short mapleVersion, InitializationVector sendIv, InitializationVector recvIv) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(8); mplew.writeShort(0x0E); mplew.writeShort(mapleVersion); mplew.writeShort(1); mplew.write(49); - mplew.write(recvIv); - mplew.write(sendIv); + mplew.write(recvIv.getBytes()); + mplew.write(sendIv.getBytes()); mplew.write(8); return mplew.getPacket(); } From 171215e6531c8f102befb28eb57d4f495173fd5a Mon Sep 17 00:00:00 2001 From: P0nk Date: Tue, 22 Jun 2021 17:15:19 +0200 Subject: [PATCH 03/37] Clean up old In/OutPacket --- src/main/java/net/netty/InPacket.java | 4 ---- src/main/java/net/netty/OutPacket.java | 5 ----- 2 files changed, 9 deletions(-) delete mode 100644 src/main/java/net/netty/InPacket.java delete mode 100644 src/main/java/net/netty/OutPacket.java diff --git a/src/main/java/net/netty/InPacket.java b/src/main/java/net/netty/InPacket.java deleted file mode 100644 index b4c536cc59..0000000000 --- a/src/main/java/net/netty/InPacket.java +++ /dev/null @@ -1,4 +0,0 @@ -package net.netty; - -public class InPacket { -} diff --git a/src/main/java/net/netty/OutPacket.java b/src/main/java/net/netty/OutPacket.java deleted file mode 100644 index f7f8f1dc46..0000000000 --- a/src/main/java/net/netty/OutPacket.java +++ /dev/null @@ -1,5 +0,0 @@ -package net.netty; - -public interface OutPacket { - byte[] getBytes(); -} From 54311503627db355093de8b19b870a2aa9a36f05 Mon Sep 17 00:00:00 2001 From: P0nk Date: Tue, 22 Jun 2021 20:32:35 +0200 Subject: [PATCH 04/37] Set up packet codec and channel initialization --- src/main/java/client/MapleClient.java | 5 +---- src/main/java/net/mina/MaplePacketDecoder.java | 2 +- src/main/java/net/netty/ClientInitializer.java | 4 ++-- src/main/java/net/netty/PacketCodec.java | 9 +++++++++ src/main/java/net/netty/PacketDecoder.java | 2 +- src/main/java/net/netty/PacketEncoder.java | 1 + src/main/java/tools/MapleAESOFB.java | 4 ++-- 7 files changed, 17 insertions(+), 10 deletions(-) create mode 100644 src/main/java/net/netty/PacketCodec.java diff --git a/src/main/java/client/MapleClient.java b/src/main/java/client/MapleClient.java index 203a47b826..a2bd6ba1c2 100644 --- a/src/main/java/client/MapleClient.java +++ b/src/main/java/client/MapleClient.java @@ -26,7 +26,6 @@ import config.YamlConfig; import constants.game.GameConstants; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; -import net.netty.ClientCyphers; import net.netty.InvalidPacketHeaderException; import net.server.Server; import net.server.audit.locks.MonitoredLockType; @@ -122,9 +121,7 @@ public class MapleClient extends ChannelInboundHandlerAdapter { return lastPacket; } - public MapleClient(ClientCyphers cyphers) { - this.send = cyphers.getSendCypher(); - this.receive = cyphers.getReceiveCypher(); + public MapleClient() { this.session = null; // TODO remove once the other constructor is removed } diff --git a/src/main/java/net/mina/MaplePacketDecoder.java b/src/main/java/net/mina/MaplePacketDecoder.java index aa182f1528..c66a595b33 100644 --- a/src/main/java/net/mina/MaplePacketDecoder.java +++ b/src/main/java/net/mina/MaplePacketDecoder.java @@ -59,7 +59,7 @@ public class MaplePacketDecoder extends CumulativeProtocolDecoder { MapleAESOFB rcvdCrypto = client.getReceiveCrypto(); if (in.remaining() >= 4 && decoderState.packetlength == -1) { int packetHeader = in.getInt(); - if (!rcvdCrypto.checkPacket(packetHeader)) { + if (!rcvdCrypto.isValidHeader(packetHeader)) { MapleSessionCoordinator.getInstance().closeSession(session, true); return false; } diff --git a/src/main/java/net/netty/ClientInitializer.java b/src/main/java/net/netty/ClientInitializer.java index 2c52725cbe..8a7397e5df 100644 --- a/src/main/java/net/netty/ClientInitializer.java +++ b/src/main/java/net/netty/ClientInitializer.java @@ -14,7 +14,7 @@ public class ClientInitializer extends ChannelInitializer { final String clientIp = socketChannel.remoteAddress().getHostName(); log.debug("Client initiated new connection from: {}", clientIp); - MapleClient client = new MapleClient(ClientCyphers.generateNew()); - socketChannel.pipeline().addLast("MapleClient", client); + socketChannel.pipeline().addLast("PacketCodec", new PacketCodec(ClientCyphers.generateNew())); + socketChannel.pipeline().addLast("MapleClient", new MapleClient()); } } diff --git a/src/main/java/net/netty/PacketCodec.java b/src/main/java/net/netty/PacketCodec.java new file mode 100644 index 0000000000..6292096711 --- /dev/null +++ b/src/main/java/net/netty/PacketCodec.java @@ -0,0 +1,9 @@ +package net.netty; + +import io.netty.channel.CombinedChannelDuplexHandler; + +public class PacketCodec extends CombinedChannelDuplexHandler { + public PacketCodec(ClientCyphers clientCyphers) { + super(new PacketDecoder(clientCyphers.getReceiveCypher()), new PacketEncoder(clientCyphers.getSendCypher())); + } +} diff --git a/src/main/java/net/netty/PacketDecoder.java b/src/main/java/net/netty/PacketDecoder.java index 99a30bb9f3..777b02e829 100644 --- a/src/main/java/net/netty/PacketDecoder.java +++ b/src/main/java/net/netty/PacketDecoder.java @@ -19,7 +19,7 @@ public class PacketDecoder extends ReplayingDecoder { protected void decode(ChannelHandlerContext context, ByteBuf in, List out) { final int header = in.readInt(); - if (!receiveCypher.checkPacket(header)) { + if (!receiveCypher.isValidHeader(header)) { throw new InvalidPacketHeaderException("Attempted to decode a packet with an invalid header", header); } diff --git a/src/main/java/net/netty/PacketEncoder.java b/src/main/java/net/netty/PacketEncoder.java index 10bb25724e..3d424822c9 100644 --- a/src/main/java/net/netty/PacketEncoder.java +++ b/src/main/java/net/netty/PacketEncoder.java @@ -4,6 +4,7 @@ import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; import net.mina.MapleCustomEncryption; +import net.packet.OutPacket; import tools.MapleAESOFB; public class PacketEncoder extends MessageToByteEncoder { diff --git a/src/main/java/tools/MapleAESOFB.java b/src/main/java/tools/MapleAESOFB.java index 19ba74e279..3b1bd1e809 100644 --- a/src/main/java/tools/MapleAESOFB.java +++ b/src/main/java/tools/MapleAESOFB.java @@ -158,12 +158,12 @@ public class MapleAESOFB { return packetLength; } - public boolean checkPacket(byte[] packet) { + private boolean checkPacket(byte[] packet) { return ((((packet[0] ^ iv[2]) & 0xFF) == ((mapleVersion >> 8) & 0xFF)) && (((packet[1] ^ iv[3]) & 0xFF) == (mapleVersion & 0xFF))); } - public boolean checkPacket(int packetHeader) { + public boolean isValidHeader(int packetHeader) { byte[] packetHeaderBuf = new byte[2]; packetHeaderBuf[0] = (byte) ((packetHeader >> 24) & 0xFF); packetHeaderBuf[1] = (byte) ((packetHeader >> 16) & 0xFF); From 0fa6ad0e2431d2ec1811eefc26385c3b18e09c92 Mon Sep 17 00:00:00 2001 From: P0nk Date: Wed, 23 Jun 2021 16:08:30 +0200 Subject: [PATCH 05/37] Refactor PacketProcessor, split into more methods --- src/main/java/net/PacketProcessor.java | 398 ++++++++++++------------- 1 file changed, 199 insertions(+), 199 deletions(-) diff --git a/src/main/java/net/PacketProcessor.java b/src/main/java/net/PacketProcessor.java index 94e07b5441..663d192fe6 100644 --- a/src/main/java/net/PacketProcessor.java +++ b/src/main/java/net/PacketProcessor.java @@ -21,34 +21,15 @@ */ package net; -import java.util.LinkedHashMap; -import java.util.Map; - import net.opcodes.RecvOpcode; import net.server.channel.handlers.*; import net.server.handlers.CustomPacketHandler; import net.server.handlers.KeepAliveHandler; import net.server.handlers.LoginRequiringNoOpHandler; -import net.server.handlers.login.AcceptToSHandler; -import net.server.handlers.login.AfterLoginHandler; -import net.server.handlers.login.CharSelectedHandler; -import net.server.handlers.login.CharSelectedWithPicHandler; -import net.server.handlers.login.CharlistRequestHandler; -import net.server.handlers.login.CheckCharNameHandler; -import net.server.handlers.login.CreateCharHandler; -import net.server.handlers.login.DeleteCharHandler; -import net.server.handlers.login.GuestLoginHandler; -import net.server.handlers.login.LoginPasswordHandler; -import net.server.handlers.login.RegisterPicHandler; -import net.server.handlers.login.RegisterPinHandler; -import net.server.handlers.login.RelogRequestHandler; -import net.server.handlers.login.ServerStatusRequestHandler; -import net.server.handlers.login.ServerlistRequestHandler; -import net.server.handlers.login.SetGenderHandler; -import net.server.handlers.login.ViewAllCharHandler; -import net.server.handlers.login.ViewAllCharRegisterPicHandler; -import net.server.handlers.login.ViewAllCharSelectedHandler; -import net.server.handlers.login.ViewAllCharSelectedWithPicHandler; +import net.server.handlers.login.*; + +import java.util.LinkedHashMap; +import java.util.Map; public final class PacketProcessor { @@ -65,6 +46,14 @@ public final class PacketProcessor { handlers = new MaplePacketHandler[maxRecvOp + 1]; } + public static PacketProcessor getLoginServerProcessor() { + return getProcessor(-1, -1); + } + + public static PacketProcessor getChannelServerProcessor(int world, int channel) { + return getProcessor(world, channel); + } + public MaplePacketHandler getHandler(short packetId) { if (packetId > handlers.length) { return null; @@ -86,12 +75,12 @@ public final class PacketProcessor { } public synchronized static PacketProcessor getProcessor(int world, int channel) { - final String lolpair = world + " " + channel; - PacketProcessor processor = instances.get(lolpair); + final String processorId = world + " " + channel; + PacketProcessor processor = instances.get(processorId); if (processor == null) { processor = new PacketProcessor(); processor.reset(channel); - instances.put(lolpair, processor); + instances.put(processorId, processor); } return processor; } @@ -99,181 +88,192 @@ public final class PacketProcessor { public void reset(int channel) { handlers = new MaplePacketHandler[handlers.length]; - registerHandler(RecvOpcode.PONG, new KeepAliveHandler()); - registerHandler(RecvOpcode.CUSTOM_PACKET, new CustomPacketHandler()); + registerCommonHandlers(); + if (channel < 0) { - //LOGIN HANDLERS - registerHandler(RecvOpcode.ACCEPT_TOS, new AcceptToSHandler()); - registerHandler(RecvOpcode.AFTER_LOGIN, new AfterLoginHandler()); - registerHandler(RecvOpcode.SERVERLIST_REREQUEST, new ServerlistRequestHandler()); - registerHandler(RecvOpcode.CHARLIST_REQUEST, new CharlistRequestHandler()); - registerHandler(RecvOpcode.CHAR_SELECT, new CharSelectedHandler()); - registerHandler(RecvOpcode.LOGIN_PASSWORD, new LoginPasswordHandler()); - registerHandler(RecvOpcode.RELOG, new RelogRequestHandler()); - registerHandler(RecvOpcode.SERVERLIST_REQUEST, new ServerlistRequestHandler()); - registerHandler(RecvOpcode.SERVERSTATUS_REQUEST, new ServerStatusRequestHandler()); - registerHandler(RecvOpcode.CHECK_CHAR_NAME, new CheckCharNameHandler()); - registerHandler(RecvOpcode.CREATE_CHAR, new CreateCharHandler()); - registerHandler(RecvOpcode.DELETE_CHAR, new DeleteCharHandler()); - registerHandler(RecvOpcode.VIEW_ALL_CHAR, new ViewAllCharHandler()); - registerHandler(RecvOpcode.PICK_ALL_CHAR, new ViewAllCharSelectedHandler()); - registerHandler(RecvOpcode.REGISTER_PIN, new RegisterPinHandler()); - registerHandler(RecvOpcode.GUEST_LOGIN, new GuestLoginHandler()); - registerHandler(RecvOpcode.REGISTER_PIC, new RegisterPicHandler()); - registerHandler(RecvOpcode.CHAR_SELECT_WITH_PIC, new CharSelectedWithPicHandler()); - registerHandler(RecvOpcode.SET_GENDER, new SetGenderHandler()); - registerHandler(RecvOpcode.VIEW_ALL_WITH_PIC, new ViewAllCharSelectedWithPicHandler()); - registerHandler(RecvOpcode.VIEW_ALL_PIC_REGISTER, new ViewAllCharRegisterPicHandler()); + registerLoginHandlers(); } else { - //CHANNEL HANDLERS - registerHandler(RecvOpcode.NAME_TRANSFER, new TransferNameHandler()); - registerHandler(RecvOpcode.CHECK_CHAR_NAME, new TransferNameResultHandler()); - registerHandler(RecvOpcode.WORLD_TRANSFER, new TransferWorldHandler()); - registerHandler(RecvOpcode.CHANGE_CHANNEL, new ChangeChannelHandler()); - registerHandler(RecvOpcode.STRANGE_DATA, LoginRequiringNoOpHandler.getInstance()); - registerHandler(RecvOpcode.GENERAL_CHAT, new GeneralChatHandler()); - registerHandler(RecvOpcode.WHISPER, new WhisperHandler()); - registerHandler(RecvOpcode.NPC_TALK, new NPCTalkHandler()); - registerHandler(RecvOpcode.NPC_TALK_MORE, new NPCMoreTalkHandler()); - registerHandler(RecvOpcode.QUEST_ACTION, new QuestActionHandler()); - registerHandler(RecvOpcode.GRENADE_EFFECT, new GrenadeEffectHandler()); - registerHandler(RecvOpcode.NPC_SHOP, new NPCShopHandler()); - registerHandler(RecvOpcode.ITEM_SORT, new InventoryMergeHandler()); - registerHandler(RecvOpcode.ITEM_MOVE, new ItemMoveHandler()); - registerHandler(RecvOpcode.MESO_DROP, new MesoDropHandler()); - registerHandler(RecvOpcode.PLAYER_LOGGEDIN, new PlayerLoggedinHandler()); - registerHandler(RecvOpcode.CHANGE_MAP, new ChangeMapHandler()); - registerHandler(RecvOpcode.MOVE_LIFE, new MoveLifeHandler()); - registerHandler(RecvOpcode.CLOSE_RANGE_ATTACK, new CloseRangeDamageHandler()); - registerHandler(RecvOpcode.RANGED_ATTACK, new RangedAttackHandler()); - registerHandler(RecvOpcode.MAGIC_ATTACK, new MagicDamageHandler()); - registerHandler(RecvOpcode.TAKE_DAMAGE, new TakeDamageHandler()); - registerHandler(RecvOpcode.MOVE_PLAYER, new MovePlayerHandler()); - registerHandler(RecvOpcode.USE_CASH_ITEM, new UseCashItemHandler()); - registerHandler(RecvOpcode.USE_ITEM, new UseItemHandler()); - registerHandler(RecvOpcode.USE_RETURN_SCROLL, new UseItemHandler()); - registerHandler(RecvOpcode.USE_UPGRADE_SCROLL, new ScrollHandler()); - registerHandler(RecvOpcode.USE_SUMMON_BAG, new UseSummonBagHandler()); - registerHandler(RecvOpcode.FACE_EXPRESSION, new FaceExpressionHandler()); - registerHandler(RecvOpcode.HEAL_OVER_TIME, new HealOvertimeHandler()); - registerHandler(RecvOpcode.ITEM_PICKUP, new ItemPickupHandler()); - registerHandler(RecvOpcode.CHAR_INFO_REQUEST, new CharInfoRequestHandler()); - registerHandler(RecvOpcode.SPECIAL_MOVE, new SpecialMoveHandler()); - registerHandler(RecvOpcode.USE_INNER_PORTAL, new InnerPortalHandler()); - registerHandler(RecvOpcode.CANCEL_BUFF, new CancelBuffHandler()); - registerHandler(RecvOpcode.CANCEL_ITEM_EFFECT, new CancelItemEffectHandler()); - registerHandler(RecvOpcode.PLAYER_INTERACTION, new PlayerInteractionHandler()); - registerHandler(RecvOpcode.RPS_ACTION, new RPSActionHandler()); - registerHandler(RecvOpcode.DISTRIBUTE_AP, new DistributeAPHandler()); - registerHandler(RecvOpcode.DISTRIBUTE_SP, new DistributeSPHandler()); - registerHandler(RecvOpcode.CHANGE_KEYMAP, new KeymapChangeHandler()); - registerHandler(RecvOpcode.CHANGE_MAP_SPECIAL, new ChangeMapSpecialHandler()); - registerHandler(RecvOpcode.STORAGE, new StorageHandler()); - registerHandler(RecvOpcode.GIVE_FAME, new GiveFameHandler()); - registerHandler(RecvOpcode.PARTY_OPERATION, new PartyOperationHandler()); - registerHandler(RecvOpcode.DENY_PARTY_REQUEST, new DenyPartyRequestHandler()); - registerHandler(RecvOpcode.MULTI_CHAT, new MultiChatHandler()); - registerHandler(RecvOpcode.USE_DOOR, new DoorHandler()); - registerHandler(RecvOpcode.ENTER_MTS, new EnterMTSHandler()); - registerHandler(RecvOpcode.ENTER_CASHSHOP, new EnterCashShopHandler()); - registerHandler(RecvOpcode.DAMAGE_SUMMON, new DamageSummonHandler()); - registerHandler(RecvOpcode.MOVE_SUMMON, new MoveSummonHandler()); - registerHandler(RecvOpcode.SUMMON_ATTACK, new SummonDamageHandler()); - registerHandler(RecvOpcode.BUDDYLIST_MODIFY, new BuddylistModifyHandler()); - registerHandler(RecvOpcode.USE_ITEMEFFECT, new UseItemEffectHandler()); - registerHandler(RecvOpcode.USE_CHAIR, new UseChairHandler()); - registerHandler(RecvOpcode.CANCEL_CHAIR, new CancelChairHandler()); - registerHandler(RecvOpcode.DAMAGE_REACTOR, new ReactorHitHandler()); - registerHandler(RecvOpcode.GUILD_OPERATION, new GuildOperationHandler()); - registerHandler(RecvOpcode.DENY_GUILD_REQUEST, new DenyGuildRequestHandler()); - registerHandler(RecvOpcode.BBS_OPERATION, new BBSOperationHandler()); - registerHandler(RecvOpcode.SKILL_EFFECT, new SkillEffectHandler()); - registerHandler(RecvOpcode.MESSENGER, new MessengerHandler()); - registerHandler(RecvOpcode.NPC_ACTION, new NPCAnimationHandler()); - registerHandler(RecvOpcode.CHECK_CASH, new TouchingCashShopHandler()); - registerHandler(RecvOpcode.CASHSHOP_OPERATION, new CashOperationHandler()); - registerHandler(RecvOpcode.COUPON_CODE, new CouponCodeHandler()); - registerHandler(RecvOpcode.SPAWN_PET, new SpawnPetHandler()); - registerHandler(RecvOpcode.MOVE_PET, new MovePetHandler()); - registerHandler(RecvOpcode.PET_CHAT, new PetChatHandler()); - registerHandler(RecvOpcode.PET_COMMAND, new PetCommandHandler()); - registerHandler(RecvOpcode.PET_FOOD, new PetFoodHandler()); - registerHandler(RecvOpcode.PET_LOOT, new PetLootHandler()); - registerHandler(RecvOpcode.AUTO_AGGRO, new AutoAggroHandler()); - registerHandler(RecvOpcode.MONSTER_BOMB, new MonsterBombHandler()); - registerHandler(RecvOpcode.CANCEL_DEBUFF, new CancelDebuffHandler()); - registerHandler(RecvOpcode.USE_SKILL_BOOK, new SkillBookHandler()); - registerHandler(RecvOpcode.SKILL_MACRO, new SkillMacroHandler()); - registerHandler(RecvOpcode.NOTE_ACTION, new NoteActionHandler()); - registerHandler(RecvOpcode.CLOSE_CHALKBOARD, new CloseChalkboardHandler()); - registerHandler(RecvOpcode.USE_MOUNT_FOOD, new UseMountFoodHandler()); - registerHandler(RecvOpcode.MTS_OPERATION, new MTSHandler()); - registerHandler(RecvOpcode.RING_ACTION, new RingActionHandler()); - registerHandler(RecvOpcode.SPOUSE_CHAT, new SpouseChatHandler()); - registerHandler(RecvOpcode.PET_AUTO_POT, new PetAutoPotHandler()); - registerHandler(RecvOpcode.PET_EXCLUDE_ITEMS, new PetExcludeItemsHandler()); - registerHandler(RecvOpcode.OWL_ACTION, new UseOwlOfMinervaHandler()); - registerHandler(RecvOpcode.OWL_WARP, new OwlWarpHandler()); - registerHandler(RecvOpcode.TOUCH_MONSTER_ATTACK, new TouchMonsterDamageHandler()); - registerHandler(RecvOpcode.TROCK_ADD_MAP, new TrockAddMapHandler()); - registerHandler(RecvOpcode.HIRED_MERCHANT_REQUEST, new HiredMerchantRequest()); - registerHandler(RecvOpcode.MOB_BANISH_PLAYER, new MobBanishPlayerHandler()); - registerHandler(RecvOpcode.MOB_DAMAGE_MOB, new MobDamageMobHandler()); - registerHandler(RecvOpcode.REPORT, new ReportHandler()); - registerHandler(RecvOpcode.MONSTER_BOOK_COVER, new MonsterBookCoverHandler()); - registerHandler(RecvOpcode.AUTO_DISTRIBUTE_AP, new AutoAssignHandler()); - registerHandler(RecvOpcode.MAKER_SKILL, new MakerSkillHandler()); - registerHandler(RecvOpcode.OPEN_FAMILY_PEDIGREE, new OpenFamilyPedigreeHandler()); - registerHandler(RecvOpcode.OPEN_FAMILY, new OpenFamilyHandler()); - registerHandler(RecvOpcode.ADD_FAMILY, new FamilyAddHandler()); - registerHandler(RecvOpcode.SEPARATE_FAMILY_BY_SENIOR, new FamilySeparateHandler()); - registerHandler(RecvOpcode.SEPARATE_FAMILY_BY_JUNIOR, new FamilySeparateHandler()); - registerHandler(RecvOpcode.USE_FAMILY, new FamilyUseHandler()); - registerHandler(RecvOpcode.CHANGE_FAMILY_MESSAGE, new FamilyPreceptsHandler()); - registerHandler(RecvOpcode.FAMILY_SUMMON_RESPONSE, new FamilySummonResponseHandler()); - registerHandler(RecvOpcode.USE_HAMMER, new UseHammerHandler()); - registerHandler(RecvOpcode.SCRIPTED_ITEM, new ScriptedItemHandler()); - registerHandler(RecvOpcode.TOUCHING_REACTOR, new TouchReactorHandler()); - registerHandler(RecvOpcode.BEHOLDER, new BeholderHandler()); - registerHandler(RecvOpcode.ADMIN_COMMAND, new AdminCommandHandler()); - registerHandler(RecvOpcode.ADMIN_LOG, new AdminLogHandler()); - registerHandler(RecvOpcode.ALLIANCE_OPERATION, new AllianceOperationHandler()); - registerHandler(RecvOpcode.DENY_ALLIANCE_REQUEST, new DenyAllianceRequestHandler()); - registerHandler(RecvOpcode.USE_SOLOMON_ITEM, new UseSolomonHandler()); - registerHandler(RecvOpcode.USE_GACHA_EXP, new UseGachaExpHandler()); - registerHandler(RecvOpcode.NEW_YEAR_CARD_REQUEST, new NewYearCardHandler()); - registerHandler(RecvOpcode.CASHSHOP_SURPRISE, new CashShopSurpriseHandler()); - registerHandler(RecvOpcode.USE_ITEM_REWARD, new ItemRewardHandler()); - registerHandler(RecvOpcode.USE_REMOTE, new RemoteGachaponHandler()); - registerHandler(RecvOpcode.ACCEPT_FAMILY, new AcceptFamilyHandler()); - registerHandler(RecvOpcode.DUEY_ACTION, new DueyHandler()); - registerHandler(RecvOpcode.USE_DEATHITEM, new UseDeathItemHandler()); - registerHandler(RecvOpcode.PLAYER_MAP_TRANSFER, new PlayerMapTransitionHandler()); - registerHandler(RecvOpcode.USE_MAPLELIFE, new UseMapleLifeHandler()); - registerHandler(RecvOpcode.USE_CATCH_ITEM, new UseCatchItemHandler()); - registerHandler(RecvOpcode.FIELD_DAMAGE_MOB, new FieldDamageMobHandler()); - registerHandler(RecvOpcode.MOB_DAMAGE_MOB_FRIENDLY, new MobDamageMobFriendlyHandler()); - registerHandler(RecvOpcode.PARTY_SEARCH_REGISTER, new PartySearchRegisterHandler()); - registerHandler(RecvOpcode.PARTY_SEARCH_START, new PartySearchStartHandler()); - registerHandler(RecvOpcode.PARTY_SEARCH_UPDATE, new PartySearchUpdateHandler()); - registerHandler(RecvOpcode.ITEM_SORT2, new InventorySortHandler()); - registerHandler(RecvOpcode.LEFT_KNOCKBACK, new LeftKnockbackHandler()); - registerHandler(RecvOpcode.SNOWBALL, new SnowballHandler()); - registerHandler(RecvOpcode.COCONUT, new CoconutHandler()); - registerHandler(RecvOpcode.ARAN_COMBO_COUNTER, new AranComboHandler()); - registerHandler(RecvOpcode.CLICK_GUIDE, new ClickGuideHandler()); - registerHandler(RecvOpcode.FREDRICK_ACTION, new FredrickHandler()); - registerHandler(RecvOpcode.MONSTER_CARNIVAL, new MonsterCarnivalHandler()); - registerHandler(RecvOpcode.REMOTE_STORE, new RemoteStoreHandler()); - registerHandler(RecvOpcode.WEDDING_ACTION, new WeddingHandler()); - registerHandler(RecvOpcode.WEDDING_TALK, new WeddingTalkHandler()); - registerHandler(RecvOpcode.WEDDING_TALK_MORE, new WeddingTalkMoreHandler()); - registerHandler(RecvOpcode.WATER_OF_LIFE, new UseWaterOfLifeHandler()); - registerHandler(RecvOpcode.ADMIN_CHAT, new AdminChatHandler()); - registerHandler(RecvOpcode.MOVE_DRAGON, new MoveDragonHandler()); - registerHandler(RecvOpcode.OPEN_ITEMUI, new RaiseUIStateHandler()); - registerHandler(RecvOpcode.USE_ITEMUI, new RaiseIncExpHandler()); - registerHandler(RecvOpcode.CHANGE_QUICKSLOT, new QuickslotKeyMappedModifiedHandler()); + registerChannelHandlers(); } } + + private void registerCommonHandlers() { + registerHandler(RecvOpcode.PONG, new KeepAliveHandler()); + registerHandler(RecvOpcode.CUSTOM_PACKET, new CustomPacketHandler()); + } + + private void registerLoginHandlers() { + registerHandler(RecvOpcode.ACCEPT_TOS, new AcceptToSHandler()); + registerHandler(RecvOpcode.AFTER_LOGIN, new AfterLoginHandler()); + registerHandler(RecvOpcode.SERVERLIST_REREQUEST, new ServerlistRequestHandler()); + registerHandler(RecvOpcode.CHARLIST_REQUEST, new CharlistRequestHandler()); + registerHandler(RecvOpcode.CHAR_SELECT, new CharSelectedHandler()); + registerHandler(RecvOpcode.LOGIN_PASSWORD, new LoginPasswordHandler()); + registerHandler(RecvOpcode.RELOG, new RelogRequestHandler()); + registerHandler(RecvOpcode.SERVERLIST_REQUEST, new ServerlistRequestHandler()); + registerHandler(RecvOpcode.SERVERSTATUS_REQUEST, new ServerStatusRequestHandler()); + registerHandler(RecvOpcode.CHECK_CHAR_NAME, new CheckCharNameHandler()); + registerHandler(RecvOpcode.CREATE_CHAR, new CreateCharHandler()); + registerHandler(RecvOpcode.DELETE_CHAR, new DeleteCharHandler()); + registerHandler(RecvOpcode.VIEW_ALL_CHAR, new ViewAllCharHandler()); + registerHandler(RecvOpcode.PICK_ALL_CHAR, new ViewAllCharSelectedHandler()); + registerHandler(RecvOpcode.REGISTER_PIN, new RegisterPinHandler()); + registerHandler(RecvOpcode.GUEST_LOGIN, new GuestLoginHandler()); + registerHandler(RecvOpcode.REGISTER_PIC, new RegisterPicHandler()); + registerHandler(RecvOpcode.CHAR_SELECT_WITH_PIC, new CharSelectedWithPicHandler()); + registerHandler(RecvOpcode.SET_GENDER, new SetGenderHandler()); + registerHandler(RecvOpcode.VIEW_ALL_WITH_PIC, new ViewAllCharSelectedWithPicHandler()); + registerHandler(RecvOpcode.VIEW_ALL_PIC_REGISTER, new ViewAllCharRegisterPicHandler()); + } + + private void registerChannelHandlers() { + registerHandler(RecvOpcode.NAME_TRANSFER, new TransferNameHandler()); + registerHandler(RecvOpcode.CHECK_CHAR_NAME, new TransferNameResultHandler()); + registerHandler(RecvOpcode.WORLD_TRANSFER, new TransferWorldHandler()); + registerHandler(RecvOpcode.CHANGE_CHANNEL, new ChangeChannelHandler()); + registerHandler(RecvOpcode.STRANGE_DATA, LoginRequiringNoOpHandler.getInstance()); + registerHandler(RecvOpcode.GENERAL_CHAT, new GeneralChatHandler()); + registerHandler(RecvOpcode.WHISPER, new WhisperHandler()); + registerHandler(RecvOpcode.NPC_TALK, new NPCTalkHandler()); + registerHandler(RecvOpcode.NPC_TALK_MORE, new NPCMoreTalkHandler()); + registerHandler(RecvOpcode.QUEST_ACTION, new QuestActionHandler()); + registerHandler(RecvOpcode.GRENADE_EFFECT, new GrenadeEffectHandler()); + registerHandler(RecvOpcode.NPC_SHOP, new NPCShopHandler()); + registerHandler(RecvOpcode.ITEM_SORT, new InventoryMergeHandler()); + registerHandler(RecvOpcode.ITEM_MOVE, new ItemMoveHandler()); + registerHandler(RecvOpcode.MESO_DROP, new MesoDropHandler()); + registerHandler(RecvOpcode.PLAYER_LOGGEDIN, new PlayerLoggedinHandler()); + registerHandler(RecvOpcode.CHANGE_MAP, new ChangeMapHandler()); + registerHandler(RecvOpcode.MOVE_LIFE, new MoveLifeHandler()); + registerHandler(RecvOpcode.CLOSE_RANGE_ATTACK, new CloseRangeDamageHandler()); + registerHandler(RecvOpcode.RANGED_ATTACK, new RangedAttackHandler()); + registerHandler(RecvOpcode.MAGIC_ATTACK, new MagicDamageHandler()); + registerHandler(RecvOpcode.TAKE_DAMAGE, new TakeDamageHandler()); + registerHandler(RecvOpcode.MOVE_PLAYER, new MovePlayerHandler()); + registerHandler(RecvOpcode.USE_CASH_ITEM, new UseCashItemHandler()); + registerHandler(RecvOpcode.USE_ITEM, new UseItemHandler()); + registerHandler(RecvOpcode.USE_RETURN_SCROLL, new UseItemHandler()); + registerHandler(RecvOpcode.USE_UPGRADE_SCROLL, new ScrollHandler()); + registerHandler(RecvOpcode.USE_SUMMON_BAG, new UseSummonBagHandler()); + registerHandler(RecvOpcode.FACE_EXPRESSION, new FaceExpressionHandler()); + registerHandler(RecvOpcode.HEAL_OVER_TIME, new HealOvertimeHandler()); + registerHandler(RecvOpcode.ITEM_PICKUP, new ItemPickupHandler()); + registerHandler(RecvOpcode.CHAR_INFO_REQUEST, new CharInfoRequestHandler()); + registerHandler(RecvOpcode.SPECIAL_MOVE, new SpecialMoveHandler()); + registerHandler(RecvOpcode.USE_INNER_PORTAL, new InnerPortalHandler()); + registerHandler(RecvOpcode.CANCEL_BUFF, new CancelBuffHandler()); + registerHandler(RecvOpcode.CANCEL_ITEM_EFFECT, new CancelItemEffectHandler()); + registerHandler(RecvOpcode.PLAYER_INTERACTION, new PlayerInteractionHandler()); + registerHandler(RecvOpcode.RPS_ACTION, new RPSActionHandler()); + registerHandler(RecvOpcode.DISTRIBUTE_AP, new DistributeAPHandler()); + registerHandler(RecvOpcode.DISTRIBUTE_SP, new DistributeSPHandler()); + registerHandler(RecvOpcode.CHANGE_KEYMAP, new KeymapChangeHandler()); + registerHandler(RecvOpcode.CHANGE_MAP_SPECIAL, new ChangeMapSpecialHandler()); + registerHandler(RecvOpcode.STORAGE, new StorageHandler()); + registerHandler(RecvOpcode.GIVE_FAME, new GiveFameHandler()); + registerHandler(RecvOpcode.PARTY_OPERATION, new PartyOperationHandler()); + registerHandler(RecvOpcode.DENY_PARTY_REQUEST, new DenyPartyRequestHandler()); + registerHandler(RecvOpcode.MULTI_CHAT, new MultiChatHandler()); + registerHandler(RecvOpcode.USE_DOOR, new DoorHandler()); + registerHandler(RecvOpcode.ENTER_MTS, new EnterMTSHandler()); + registerHandler(RecvOpcode.ENTER_CASHSHOP, new EnterCashShopHandler()); + registerHandler(RecvOpcode.DAMAGE_SUMMON, new DamageSummonHandler()); + registerHandler(RecvOpcode.MOVE_SUMMON, new MoveSummonHandler()); + registerHandler(RecvOpcode.SUMMON_ATTACK, new SummonDamageHandler()); + registerHandler(RecvOpcode.BUDDYLIST_MODIFY, new BuddylistModifyHandler()); + registerHandler(RecvOpcode.USE_ITEMEFFECT, new UseItemEffectHandler()); + registerHandler(RecvOpcode.USE_CHAIR, new UseChairHandler()); + registerHandler(RecvOpcode.CANCEL_CHAIR, new CancelChairHandler()); + registerHandler(RecvOpcode.DAMAGE_REACTOR, new ReactorHitHandler()); + registerHandler(RecvOpcode.GUILD_OPERATION, new GuildOperationHandler()); + registerHandler(RecvOpcode.DENY_GUILD_REQUEST, new DenyGuildRequestHandler()); + registerHandler(RecvOpcode.BBS_OPERATION, new BBSOperationHandler()); + registerHandler(RecvOpcode.SKILL_EFFECT, new SkillEffectHandler()); + registerHandler(RecvOpcode.MESSENGER, new MessengerHandler()); + registerHandler(RecvOpcode.NPC_ACTION, new NPCAnimationHandler()); + registerHandler(RecvOpcode.CHECK_CASH, new TouchingCashShopHandler()); + registerHandler(RecvOpcode.CASHSHOP_OPERATION, new CashOperationHandler()); + registerHandler(RecvOpcode.COUPON_CODE, new CouponCodeHandler()); + registerHandler(RecvOpcode.SPAWN_PET, new SpawnPetHandler()); + registerHandler(RecvOpcode.MOVE_PET, new MovePetHandler()); + registerHandler(RecvOpcode.PET_CHAT, new PetChatHandler()); + registerHandler(RecvOpcode.PET_COMMAND, new PetCommandHandler()); + registerHandler(RecvOpcode.PET_FOOD, new PetFoodHandler()); + registerHandler(RecvOpcode.PET_LOOT, new PetLootHandler()); + registerHandler(RecvOpcode.AUTO_AGGRO, new AutoAggroHandler()); + registerHandler(RecvOpcode.MONSTER_BOMB, new MonsterBombHandler()); + registerHandler(RecvOpcode.CANCEL_DEBUFF, new CancelDebuffHandler()); + registerHandler(RecvOpcode.USE_SKILL_BOOK, new SkillBookHandler()); + registerHandler(RecvOpcode.SKILL_MACRO, new SkillMacroHandler()); + registerHandler(RecvOpcode.NOTE_ACTION, new NoteActionHandler()); + registerHandler(RecvOpcode.CLOSE_CHALKBOARD, new CloseChalkboardHandler()); + registerHandler(RecvOpcode.USE_MOUNT_FOOD, new UseMountFoodHandler()); + registerHandler(RecvOpcode.MTS_OPERATION, new MTSHandler()); + registerHandler(RecvOpcode.RING_ACTION, new RingActionHandler()); + registerHandler(RecvOpcode.SPOUSE_CHAT, new SpouseChatHandler()); + registerHandler(RecvOpcode.PET_AUTO_POT, new PetAutoPotHandler()); + registerHandler(RecvOpcode.PET_EXCLUDE_ITEMS, new PetExcludeItemsHandler()); + registerHandler(RecvOpcode.OWL_ACTION, new UseOwlOfMinervaHandler()); + registerHandler(RecvOpcode.OWL_WARP, new OwlWarpHandler()); + registerHandler(RecvOpcode.TOUCH_MONSTER_ATTACK, new TouchMonsterDamageHandler()); + registerHandler(RecvOpcode.TROCK_ADD_MAP, new TrockAddMapHandler()); + registerHandler(RecvOpcode.HIRED_MERCHANT_REQUEST, new HiredMerchantRequest()); + registerHandler(RecvOpcode.MOB_BANISH_PLAYER, new MobBanishPlayerHandler()); + registerHandler(RecvOpcode.MOB_DAMAGE_MOB, new MobDamageMobHandler()); + registerHandler(RecvOpcode.REPORT, new ReportHandler()); + registerHandler(RecvOpcode.MONSTER_BOOK_COVER, new MonsterBookCoverHandler()); + registerHandler(RecvOpcode.AUTO_DISTRIBUTE_AP, new AutoAssignHandler()); + registerHandler(RecvOpcode.MAKER_SKILL, new MakerSkillHandler()); + registerHandler(RecvOpcode.OPEN_FAMILY_PEDIGREE, new OpenFamilyPedigreeHandler()); + registerHandler(RecvOpcode.OPEN_FAMILY, new OpenFamilyHandler()); + registerHandler(RecvOpcode.ADD_FAMILY, new FamilyAddHandler()); + registerHandler(RecvOpcode.SEPARATE_FAMILY_BY_SENIOR, new FamilySeparateHandler()); + registerHandler(RecvOpcode.SEPARATE_FAMILY_BY_JUNIOR, new FamilySeparateHandler()); + registerHandler(RecvOpcode.USE_FAMILY, new FamilyUseHandler()); + registerHandler(RecvOpcode.CHANGE_FAMILY_MESSAGE, new FamilyPreceptsHandler()); + registerHandler(RecvOpcode.FAMILY_SUMMON_RESPONSE, new FamilySummonResponseHandler()); + registerHandler(RecvOpcode.USE_HAMMER, new UseHammerHandler()); + registerHandler(RecvOpcode.SCRIPTED_ITEM, new ScriptedItemHandler()); + registerHandler(RecvOpcode.TOUCHING_REACTOR, new TouchReactorHandler()); + registerHandler(RecvOpcode.BEHOLDER, new BeholderHandler()); + registerHandler(RecvOpcode.ADMIN_COMMAND, new AdminCommandHandler()); + registerHandler(RecvOpcode.ADMIN_LOG, new AdminLogHandler()); + registerHandler(RecvOpcode.ALLIANCE_OPERATION, new AllianceOperationHandler()); + registerHandler(RecvOpcode.DENY_ALLIANCE_REQUEST, new DenyAllianceRequestHandler()); + registerHandler(RecvOpcode.USE_SOLOMON_ITEM, new UseSolomonHandler()); + registerHandler(RecvOpcode.USE_GACHA_EXP, new UseGachaExpHandler()); + registerHandler(RecvOpcode.NEW_YEAR_CARD_REQUEST, new NewYearCardHandler()); + registerHandler(RecvOpcode.CASHSHOP_SURPRISE, new CashShopSurpriseHandler()); + registerHandler(RecvOpcode.USE_ITEM_REWARD, new ItemRewardHandler()); + registerHandler(RecvOpcode.USE_REMOTE, new RemoteGachaponHandler()); + registerHandler(RecvOpcode.ACCEPT_FAMILY, new AcceptFamilyHandler()); + registerHandler(RecvOpcode.DUEY_ACTION, new DueyHandler()); + registerHandler(RecvOpcode.USE_DEATHITEM, new UseDeathItemHandler()); + registerHandler(RecvOpcode.PLAYER_MAP_TRANSFER, new PlayerMapTransitionHandler()); + registerHandler(RecvOpcode.USE_MAPLELIFE, new UseMapleLifeHandler()); + registerHandler(RecvOpcode.USE_CATCH_ITEM, new UseCatchItemHandler()); + registerHandler(RecvOpcode.FIELD_DAMAGE_MOB, new FieldDamageMobHandler()); + registerHandler(RecvOpcode.MOB_DAMAGE_MOB_FRIENDLY, new MobDamageMobFriendlyHandler()); + registerHandler(RecvOpcode.PARTY_SEARCH_REGISTER, new PartySearchRegisterHandler()); + registerHandler(RecvOpcode.PARTY_SEARCH_START, new PartySearchStartHandler()); + registerHandler(RecvOpcode.PARTY_SEARCH_UPDATE, new PartySearchUpdateHandler()); + registerHandler(RecvOpcode.ITEM_SORT2, new InventorySortHandler()); + registerHandler(RecvOpcode.LEFT_KNOCKBACK, new LeftKnockbackHandler()); + registerHandler(RecvOpcode.SNOWBALL, new SnowballHandler()); + registerHandler(RecvOpcode.COCONUT, new CoconutHandler()); + registerHandler(RecvOpcode.ARAN_COMBO_COUNTER, new AranComboHandler()); + registerHandler(RecvOpcode.CLICK_GUIDE, new ClickGuideHandler()); + registerHandler(RecvOpcode.FREDRICK_ACTION, new FredrickHandler()); + registerHandler(RecvOpcode.MONSTER_CARNIVAL, new MonsterCarnivalHandler()); + registerHandler(RecvOpcode.REMOTE_STORE, new RemoteStoreHandler()); + registerHandler(RecvOpcode.WEDDING_ACTION, new WeddingHandler()); + registerHandler(RecvOpcode.WEDDING_TALK, new WeddingTalkHandler()); + registerHandler(RecvOpcode.WEDDING_TALK_MORE, new WeddingTalkMoreHandler()); + registerHandler(RecvOpcode.WATER_OF_LIFE, new UseWaterOfLifeHandler()); + registerHandler(RecvOpcode.ADMIN_CHAT, new AdminChatHandler()); + registerHandler(RecvOpcode.MOVE_DRAGON, new MoveDragonHandler()); + registerHandler(RecvOpcode.OPEN_ITEMUI, new RaiseUIStateHandler()); + registerHandler(RecvOpcode.USE_ITEMUI, new RaiseIncExpHandler()); + registerHandler(RecvOpcode.CHANGE_QUICKSLOT, new QuickslotKeyMappedModifiedHandler()); + } } \ No newline at end of file From 9638d5c417d1bf0632b9db22166e5e02ce557b92 Mon Sep 17 00:00:00 2001 From: P0nk Date: Wed, 23 Jun 2021 18:20:08 +0200 Subject: [PATCH 06/37] Initial Netty implementation for networking Split into 1 LoginServer and 1 ChannelServer per channel. There is still a lot of cleanup and refactoring to be done. Currently, the reliance on IoSession holding client state is the most pressing issue to be addressed. --- src/main/java/client/MapleClient.java | 36 ++++++++++++++++- src/main/java/net/PacketProcessor.java | 3 +- src/main/java/net/netty/AbstractServer.java | 12 ++++++ src/main/java/net/netty/ChannelServer.java | 40 +++++++++++++++++++ .../net/netty/ChannelServerInitializer.java | 31 ++++++++++++++ src/main/java/net/netty/ClientCyphers.java | 6 +-- .../java/net/netty/ClientInitializer.java | 20 ---------- src/main/java/net/netty/LoginServer.java | 38 ++++++++++++++++++ .../net/netty/LoginServerInitializer.java | 23 +++++++++++ src/main/java/net/netty/PacketDecoder.java | 4 +- .../net/netty/ServerChannelInitializer.java | 22 ++++++++++ src/main/java/net/server/Server.java | 37 ++++++++++++----- src/main/java/net/server/channel/Channel.java | 31 ++++++++++---- src/main/resources/log4j2.xml | 1 + 14 files changed, 259 insertions(+), 45 deletions(-) create mode 100644 src/main/java/net/netty/AbstractServer.java create mode 100644 src/main/java/net/netty/ChannelServer.java create mode 100644 src/main/java/net/netty/ChannelServerInitializer.java delete mode 100644 src/main/java/net/netty/ClientInitializer.java create mode 100644 src/main/java/net/netty/LoginServer.java create mode 100644 src/main/java/net/netty/LoginServerInitializer.java create mode 100644 src/main/java/net/netty/ServerChannelInitializer.java diff --git a/src/main/java/client/MapleClient.java b/src/main/java/client/MapleClient.java index a2bd6ba1c2..2641c45359 100644 --- a/src/main/java/client/MapleClient.java +++ b/src/main/java/client/MapleClient.java @@ -26,7 +26,10 @@ import config.YamlConfig; import constants.game.GameConstants; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; +import net.MaplePacketHandler; +import net.PacketProcessor; import net.netty.InvalidPacketHeaderException; +import net.packet.InPacket; import net.server.Server; import net.server.audit.locks.MonitoredLockType; import net.server.audit.locks.factory.MonitoredReentrantLockFactory; @@ -38,6 +41,8 @@ import net.server.guild.MapleGuild; import net.server.guild.MapleGuildCharacter; import net.server.world.*; import org.apache.mina.core.session.IoSession; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import scripting.AbstractPlayerInteraction; import scripting.event.EventInstanceManager; import scripting.event.EventManager; @@ -51,6 +56,8 @@ import server.maps.FieldLimit; import server.maps.MapleMap; import server.maps.MapleMiniDungeonInfo; import tools.*; +import tools.data.input.ByteArrayByteStream; +import tools.data.input.GenericSeekableLittleEndianAccessor; import javax.script.ScriptEngine; import java.io.IOException; @@ -65,6 +72,7 @@ import java.util.concurrent.Semaphore; import java.util.concurrent.locks.Lock; public class MapleClient extends ChannelInboundHandlerAdapter { + private static final Logger log = LoggerFactory.getLogger(MapleClient.class); public static final int LOGIN_NOTLOGGEDIN = 0; public static final int LOGIN_SERVER_TRANSITION = 1; @@ -77,6 +85,7 @@ public class MapleClient extends ChannelInboundHandlerAdapter { private MapleAESOFB send; private MapleAESOFB receive; private final IoSession session; + private PacketProcessor packetProcessor; private MapleCharacter player; private int channel = 1; private int accId = -4; @@ -121,7 +130,10 @@ public class MapleClient extends ChannelInboundHandlerAdapter { return lastPacket; } - public MapleClient() { + public MapleClient(PacketProcessor packetProcessor, int world, int channel) { + this.packetProcessor = packetProcessor; + this.world = world; + this.channel = channel; this.session = null; // TODO remove once the other constructor is removed } @@ -133,6 +145,28 @@ public class MapleClient extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + // InPacket packet = new ByteBufInPacket((ByteBuf) msg); + if (!(msg instanceof InPacket packet)) { + log.warn("Received invalid message: {}", msg); + return; + } + + short opcode = packet.readShort(); + final MaplePacketHandler handler = packetProcessor.getHandler(opcode); + if (handler != null && handler.validateState(this)) { + // TODO: pass InPacket directly to handler once all handlers have been ported, + // this is just a temporary workaround + GenericSeekableLittleEndianAccessor accessor = new GenericSeekableLittleEndianAccessor(new ByteArrayByteStream(packet.getBytes())); + try { + MapleLogger.logRecv(this, opcode, msg); + handler.handlePacket(accessor, this); + } catch (final Throwable t) { + FilePrinter.printError(FilePrinter.PACKET_HANDLER + handler.getClass().getName() + ".txt", t, "Error for " + (getPlayer() == null ? "" : "player ; " + getPlayer() + " on map ; " + getPlayer().getMapId() + " - ") + "account ; " + getAccountName() + "\r\n" + accessor); + //client.announce(MaplePacketCreator.enableActions());//bugs sometimes + } + } + + updateLastPacket(); super.channelRead(ctx, msg); } diff --git a/src/main/java/net/PacketProcessor.java b/src/main/java/net/PacketProcessor.java index 663d192fe6..d7b45602bd 100644 --- a/src/main/java/net/PacketProcessor.java +++ b/src/main/java/net/PacketProcessor.java @@ -21,6 +21,7 @@ */ package net; +import net.netty.LoginServer; import net.opcodes.RecvOpcode; import net.server.channel.handlers.*; import net.server.handlers.CustomPacketHandler; @@ -47,7 +48,7 @@ public final class PacketProcessor { } public static PacketProcessor getLoginServerProcessor() { - return getProcessor(-1, -1); + return getProcessor(LoginServer.WORLD, LoginServer.CHANNEL); } public static PacketProcessor getChannelServerProcessor(int world, int channel) { diff --git a/src/main/java/net/netty/AbstractServer.java b/src/main/java/net/netty/AbstractServer.java new file mode 100644 index 0000000000..1fd77f7a9c --- /dev/null +++ b/src/main/java/net/netty/AbstractServer.java @@ -0,0 +1,12 @@ +package net.netty; + +public abstract class AbstractServer { + final int port; + + AbstractServer(int port) { + this.port = port; + } + + public abstract void start(); + public abstract void stop(); +} diff --git a/src/main/java/net/netty/ChannelServer.java b/src/main/java/net/netty/ChannelServer.java new file mode 100644 index 0000000000..cd620db608 --- /dev/null +++ b/src/main/java/net/netty/ChannelServer.java @@ -0,0 +1,40 @@ +package net.netty; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.Channel; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioServerSocketChannel; + +public class ChannelServer extends AbstractServer { + private final int world; + private final int channel; + private Channel nettyChannel; + + public ChannelServer(int port, int world, int channel) { + super(port); + this.world = world; + this.channel = channel; + } + + @Override + public void start() { + EventLoopGroup parentGroup = new NioEventLoopGroup(); + EventLoopGroup childGroup = new NioEventLoopGroup(); + ServerBootstrap bootstrap = new ServerBootstrap() + .group(parentGroup, childGroup) + .channel(NioServerSocketChannel.class) + .childHandler(new ChannelServerInitializer(world, channel)); + + this.nettyChannel = bootstrap.bind(port).syncUninterruptibly().channel(); + } + + @Override + public void stop() { + if (nettyChannel == null) { + throw new IllegalStateException("Must start ChannelServer before stopping it"); + } + + nettyChannel.close().syncUninterruptibly(); + } +} diff --git a/src/main/java/net/netty/ChannelServerInitializer.java b/src/main/java/net/netty/ChannelServerInitializer.java new file mode 100644 index 0000000000..49887268bc --- /dev/null +++ b/src/main/java/net/netty/ChannelServerInitializer.java @@ -0,0 +1,31 @@ +package net.netty; + +import client.MapleClient; +import io.netty.channel.socket.SocketChannel; +import net.PacketProcessor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class ChannelServerInitializer extends ServerChannelInitializer { + private static final Logger log = LoggerFactory.getLogger(ChannelServerInitializer.class); + + private final int world; + private final int channel; + + public ChannelServerInitializer(int world, int channel) { + this.world = world; + this.channel = channel; + } + + @Override + public void initChannel(SocketChannel socketChannel) { + final String clientIp = socketChannel.remoteAddress().getHostName(); + log.debug("Client connected to world {}, channel {} from {}", world, channel, clientIp); + + PacketProcessor packetProcessor = PacketProcessor.getChannelServerProcessor(world, channel); + final MapleClient client = new MapleClient(packetProcessor, world, channel); + client.setSessionId(sessionId.getAndIncrement()); + + initPipeline(socketChannel, client); + } +} diff --git a/src/main/java/net/netty/ClientCyphers.java b/src/main/java/net/netty/ClientCyphers.java index ea97b44ded..6e46fb80ca 100644 --- a/src/main/java/net/netty/ClientCyphers.java +++ b/src/main/java/net/netty/ClientCyphers.java @@ -12,9 +12,9 @@ public class ClientCyphers { this.receive = receive; } - public static ClientCyphers generateNew() { - MapleAESOFB send = new MapleAESOFB(InitializationVector.generateSend(), ServerConstants.VERSION); - MapleAESOFB receive = new MapleAESOFB(InitializationVector.generateReceive(), ServerConstants.VERSION); + public static ClientCyphers of(InitializationVector sendIv, InitializationVector receiveIv) { + MapleAESOFB send = new MapleAESOFB(sendIv, (short) (0xFFFF - ServerConstants.VERSION)); + MapleAESOFB receive = new MapleAESOFB(receiveIv, ServerConstants.VERSION); return new ClientCyphers(send, receive); } diff --git a/src/main/java/net/netty/ClientInitializer.java b/src/main/java/net/netty/ClientInitializer.java deleted file mode 100644 index 8a7397e5df..0000000000 --- a/src/main/java/net/netty/ClientInitializer.java +++ /dev/null @@ -1,20 +0,0 @@ -package net.netty; - -import client.MapleClient; -import io.netty.channel.ChannelInitializer; -import io.netty.channel.socket.SocketChannel; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -public class ClientInitializer extends ChannelInitializer { - private static final Logger log = LoggerFactory.getLogger(ClientInitializer.class); - - @Override - public void initChannel(SocketChannel socketChannel) { - final String clientIp = socketChannel.remoteAddress().getHostName(); - log.debug("Client initiated new connection from: {}", clientIp); - - socketChannel.pipeline().addLast("PacketCodec", new PacketCodec(ClientCyphers.generateNew())); - socketChannel.pipeline().addLast("MapleClient", new MapleClient()); - } -} diff --git a/src/main/java/net/netty/LoginServer.java b/src/main/java/net/netty/LoginServer.java new file mode 100644 index 0000000000..6f4c7196c4 --- /dev/null +++ b/src/main/java/net/netty/LoginServer.java @@ -0,0 +1,38 @@ +package net.netty; + +import io.netty.bootstrap.ServerBootstrap; +import io.netty.channel.Channel; +import io.netty.channel.EventLoopGroup; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.nio.NioServerSocketChannel; + +public class LoginServer extends AbstractServer { + public static final int WORLD = -1; + public static final int CHANNEL = -1; + private Channel channel; + + public LoginServer(int port) { + super(port); + } + + @Override + public void start() { + EventLoopGroup parentGroup = new NioEventLoopGroup(); + EventLoopGroup childGroup = new NioEventLoopGroup(); + ServerBootstrap bootstrap = new ServerBootstrap() + .group(parentGroup, childGroup) + .channel(NioServerSocketChannel.class) + .childHandler(new LoginServerInitializer()); + + this.channel = bootstrap.bind(port).syncUninterruptibly().channel(); + } + + @Override + public void stop() { + if (channel == null) { + throw new IllegalStateException("Must start LoginServer before stopping it"); + } + + channel.close().syncUninterruptibly(); + } +} diff --git a/src/main/java/net/netty/LoginServerInitializer.java b/src/main/java/net/netty/LoginServerInitializer.java new file mode 100644 index 0000000000..7b9c301459 --- /dev/null +++ b/src/main/java/net/netty/LoginServerInitializer.java @@ -0,0 +1,23 @@ +package net.netty; + +import client.MapleClient; +import io.netty.channel.socket.SocketChannel; +import net.PacketProcessor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class LoginServerInitializer extends ServerChannelInitializer { + private static final Logger log = LoggerFactory.getLogger(LoginServerInitializer.class); + + @Override + public void initChannel(SocketChannel socketChannel) { + final String clientIp = socketChannel.remoteAddress().getHostName(); + log.debug("Client connected to login server from {} ", clientIp); + + PacketProcessor packetProcessor = PacketProcessor.getLoginServerProcessor(); + final MapleClient client = new MapleClient(packetProcessor, LoginServer.WORLD, LoginServer.CHANNEL); + client.setSessionId(sessionId.getAndIncrement()); + + initPipeline(socketChannel, client); + } +} diff --git a/src/main/java/net/netty/PacketDecoder.java b/src/main/java/net/netty/PacketDecoder.java index 777b02e829..79cc0fafb5 100644 --- a/src/main/java/net/netty/PacketDecoder.java +++ b/src/main/java/net/netty/PacketDecoder.java @@ -1,9 +1,11 @@ package net.netty; import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ReplayingDecoder; import net.mina.MapleCustomEncryption; +import net.packet.ByteBufInPacket; import tools.MapleAESOFB; import java.util.List; @@ -28,7 +30,7 @@ public class PacketDecoder extends ReplayingDecoder { in.readBytes(packet); receiveCypher.crypt(packet); MapleCustomEncryption.decryptData(packet); - out.add(packet); + out.add(new ByteBufInPacket(Unpooled.wrappedBuffer(packet))); // TODO conditionally log the packet } diff --git a/src/main/java/net/netty/ServerChannelInitializer.java b/src/main/java/net/netty/ServerChannelInitializer.java new file mode 100644 index 0000000000..869d47ab2f --- /dev/null +++ b/src/main/java/net/netty/ServerChannelInitializer.java @@ -0,0 +1,22 @@ +package net.netty; + +import client.MapleClient; +import constants.net.ServerConstants; +import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.socket.SocketChannel; +import tools.MaplePacketCreator; + +import java.util.concurrent.atomic.AtomicLong; + +public abstract class ServerChannelInitializer extends ChannelInitializer { + static final AtomicLong sessionId = new AtomicLong(7777); + + void initPipeline(SocketChannel socketChannel, MapleClient client) { + final InitializationVector sendIv = InitializationVector.generateSend(); + final InitializationVector recvIv = InitializationVector.generateReceive(); + socketChannel.writeAndFlush(Unpooled.wrappedBuffer(MaplePacketCreator.getHello(ServerConstants.VERSION, sendIv, recvIv))); + socketChannel.pipeline().addLast("PacketCodec", new PacketCodec(ClientCyphers.of(sendIv, recvIv))); + socketChannel.pipeline().addLast("MapleClient", client); + } +} diff --git a/src/main/java/net/server/Server.java b/src/main/java/net/server/Server.java index c053bbb369..40f227a386 100644 --- a/src/main/java/net/server/Server.java +++ b/src/main/java/net/server/Server.java @@ -37,6 +37,7 @@ import constants.net.OpcodeConstants; import constants.net.ServerConstants; import net.MapleServerHandler; import net.mina.MapleCodecFactory; +import net.netty.LoginServer; import net.server.audit.ThreadTracker; import net.server.audit.locks.MonitoredLockType; import net.server.audit.locks.MonitoredReadLock; @@ -106,6 +107,7 @@ public class Server { private static final List activeCoupons = new LinkedList<>(); private IoAcceptor acceptor; + private LoginServer loginServer; private List> channels = new LinkedList<>(); private List worlds = new ArrayList<>(); private final Properties subnetInfo = new Properties(); @@ -906,17 +908,8 @@ public class Server { } } - IoBuffer.setUseDirectBuffer(false); // join IO operations performed by lxconan - IoBuffer.setAllocator(new SimpleBufferAllocator()); - acceptor = new NioSocketAcceptor(); - acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new MapleCodecFactory())); - acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 30); - acceptor.setHandler(new MapleServerHandler()); - try { - acceptor.bind(new InetSocketAddress(8484)); - } catch (IOException ex) { - ex.printStackTrace(); - } + // acceptor = initAcceptor(8484); + loginServer = initLoginServer(8484); log.info("Listening on port 8484"); @@ -932,6 +925,28 @@ public class Server { } } + private LoginServer initLoginServer(int port) { + LoginServer loginServer = new LoginServer(port); + loginServer.start(); + return loginServer; + } + + private IoAcceptor initAcceptor(int port) { + IoBuffer.setUseDirectBuffer(false); // join IO operations performed by lxconan + IoBuffer.setAllocator(new SimpleBufferAllocator()); + acceptor = new NioSocketAcceptor(); + acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new MapleCodecFactory())); + acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 30); + acceptor.setHandler(new MapleServerHandler()); + try { + acceptor.bind(new InetSocketAddress(port)); + } catch (IOException ex) { + ex.printStackTrace(); + } + + return acceptor; + } + private static void setAllLoggedOut(Connection con) throws SQLException { try (PreparedStatement ps = con.prepareStatement("UPDATE accounts SET loggedin = 0")) { ps.executeUpdate(); diff --git a/src/main/java/net/server/channel/Channel.java b/src/main/java/net/server/channel/Channel.java index 1956daf09e..2201f0292d 100644 --- a/src/main/java/net/server/channel/Channel.java +++ b/src/main/java/net/server/channel/Channel.java @@ -26,6 +26,7 @@ import config.YamlConfig; import constants.game.GameConstants; import net.MapleServerHandler; import net.mina.MapleCodecFactory; +import net.netty.ChannelServer; import net.server.PlayerStorage; import net.server.Server; import net.server.audit.LockCollector; @@ -58,6 +59,7 @@ import tools.MaplePacketCreator; import tools.Pair; import java.io.File; +import java.io.IOException; import java.net.InetSocketAddress; import java.util.*; import java.util.Map.Entry; @@ -69,6 +71,7 @@ public final class Channel { private int port = 7575; private PlayerStorage players = new PlayerStorage(); private int world, channel; + private ChannelServer channelServer; private IoAcceptor acceptor; private String ip, serverMessage; private MapleMapManager mapManager; @@ -122,14 +125,8 @@ public final class Channel { port = 7575 + this.channel - 1; port += (world * 100); ip = YamlConfig.config.server.HOST + ":" + port; - IoBuffer.setUseDirectBuffer(false); - IoBuffer.setAllocator(new SimpleBufferAllocator()); - acceptor = new NioSocketAcceptor(); - acceptor.setHandler(new MapleServerHandler(world, channel)); - acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 30); - acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new MapleCodecFactory())); - acceptor.bind(new InetSocketAddress(port)); - ((SocketSessionConfig) acceptor.getSessionConfig()).setTcpNoDelay(true); + // acceptor = initAcceptor(); + channelServer = initServer(port, world, channel); expedType.addAll(Arrays.asList(MapleExpeditionType.values())); if (Server.getInstance().isOnline()) { // postpone event loading to improve boot time... thanks Riizade, daronhudson for noticing slow startup times @@ -156,6 +153,24 @@ public final class Channel { e.printStackTrace(); } } + + private IoAcceptor initAcceptor() throws IOException { + IoBuffer.setUseDirectBuffer(false); + IoBuffer.setAllocator(new SimpleBufferAllocator()); + IoAcceptor acceptor = new NioSocketAcceptor(); + acceptor.setHandler(new MapleServerHandler(world, channel)); + acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 30); + acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new MapleCodecFactory())); + acceptor.bind(new InetSocketAddress(port)); + ((SocketSessionConfig) acceptor.getSessionConfig()).setTcpNoDelay(true); + return acceptor; + } + + private ChannelServer initServer(int port, int world, int channel) { + this.channelServer = new ChannelServer(port, world, channel); + channelServer.start(); + return channelServer; + } public synchronized void reloadEventScriptManager(){ if (finishedShutdown) { diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml index 286496cbf0..cf3d8fda41 100644 --- a/src/main/resources/log4j2.xml +++ b/src/main/resources/log4j2.xml @@ -26,5 +26,6 @@ + \ No newline at end of file From 5651bee2b52854797868545577c920976b6c5628 Mon Sep 17 00:00:00 2001 From: P0nk Date: Wed, 23 Jun 2021 19:03:54 +0200 Subject: [PATCH 07/37] Move client transition state and hostAddress to fields --- src/main/java/client/MapleClient.java | 27 +++++++++++++++++++++-- src/main/java/net/MapleServerHandler.java | 2 +- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/main/java/client/MapleClient.java b/src/main/java/client/MapleClient.java index 2641c45359..d0657975dc 100644 --- a/src/main/java/client/MapleClient.java +++ b/src/main/java/client/MapleClient.java @@ -62,6 +62,7 @@ import tools.data.input.GenericSeekableLittleEndianAccessor; import javax.script.ScriptEngine; import java.io.IOException; import java.net.InetAddress; +import java.net.InetSocketAddress; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; @@ -81,7 +82,10 @@ public class MapleClient extends ChannelInboundHandlerAdapter { public static final String CLIENT_HWID = "HWID"; public static final String CLIENT_NIBBLEHWID = "HWID2"; public static final String CLIENT_REMOTE_ADDRESS = "REMOTE_IP"; - public static final String CLIENT_TRANSITION = "TRANSITION"; + + private String hostAddress; + private volatile boolean inTransition; + private MapleAESOFB send; private MapleAESOFB receive; private final IoSession session; @@ -143,6 +147,17 @@ public class MapleClient extends ChannelInboundHandlerAdapter { this.session = session; } + @Override + public void channelActive(ChannelHandlerContext ctx) { + String hostAddress = "null"; + try { + hostAddress = ((InetSocketAddress) ctx.channel().remoteAddress()).getAddress().getHostAddress(); + } catch (NullPointerException npe) { + log.warn("Unable to get remote address for client", npe); + } + this.hostAddress = hostAddress; + } + @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { // InPacket packet = new ByteBufInPacket((ByteBuf) msg); @@ -191,6 +206,14 @@ public class MapleClient extends ChannelInboundHandlerAdapter { return session; } + public String getHostAddress() { + return hostAddress; + } + + public boolean isInTransition() { + return inTransition; + } + public EventManager getEventManager(String event) { return getChannelServer().getEventSM().getEventManager(event); } @@ -997,7 +1020,7 @@ public class MapleClient extends ChannelInboundHandlerAdapter { public void setCharacterOnSessionTransitionState(int cid) { this.updateLoginState(MapleClient.LOGIN_SERVER_TRANSITION); - session.setAttribute(MapleClient.CLIENT_TRANSITION); + this.inTransition = true; Server.getInstance().setCharacteridInTransition(this, cid); } diff --git a/src/main/java/net/MapleServerHandler.java b/src/main/java/net/MapleServerHandler.java index 554366803d..3a6afca5da 100644 --- a/src/main/java/net/MapleServerHandler.java +++ b/src/main/java/net/MapleServerHandler.java @@ -154,7 +154,7 @@ public class MapleServerHandler extends IoHandlerAdapter { if (client != null) { try { // client freeze issues on session transition states found thanks to yolinlin, Omo Oppa, Nozphex - if (!session.containsAttribute(MapleClient.CLIENT_TRANSITION)) { + if (!client.isInTransition()) { client.disconnect(false, false); } } catch (Throwable t) { From 80cacd609a2116fa993f989537900b6bd9edc19e Mon Sep 17 00:00:00 2001 From: P0nk Date: Thu, 24 Jun 2021 22:07:22 +0200 Subject: [PATCH 08/37] Disconnect client if idle Thanks SharpAceX for Guida, which I used as a reference. --- pom.xml | 6 +- src/main/java/client/MapleClient.java | 80 +++++++++++++++++-- .../net/netty/ServerChannelInitializer.java | 2 + .../handlers/login/LoginPasswordHandler.java | 8 +- 4 files changed, 83 insertions(+), 13 deletions(-) diff --git a/pom.xml b/pom.xml index e95d8b9b3c..b41b3494d1 100644 --- a/pom.xml +++ b/pom.xml @@ -66,7 +66,11 @@ netty-buffer 4.1.65.Final - + + io.netty + netty-handler + 4.1.65.Final + diff --git a/src/main/java/client/MapleClient.java b/src/main/java/client/MapleClient.java index d0657975dc..fc9ee2f33b 100644 --- a/src/main/java/client/MapleClient.java +++ b/src/main/java/client/MapleClient.java @@ -26,6 +26,7 @@ import config.YamlConfig; import constants.game.GameConstants; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.handler.timeout.IdleStateEvent; import net.MaplePacketHandler; import net.PacketProcessor; import net.netty.InvalidPacketHeaderException; @@ -51,6 +52,7 @@ import scripting.npc.NPCScriptManager; import scripting.quest.QuestActionManager; import scripting.quest.QuestScriptManager; import server.ThreadManager; +import server.TimerManager; import server.life.MapleMonster; import server.maps.FieldLimit; import server.maps.MapleMap; @@ -70,10 +72,12 @@ import java.sql.*; import java.util.Date; import java.util.*; import java.util.concurrent.Semaphore; +import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.Lock; public class MapleClient extends ChannelInboundHandlerAdapter { private static final Logger log = LoggerFactory.getLogger(MapleClient.class); + private static final Set ignoredDebugRecvPackets = Set.of((short) 167, (short) 197, (short) 89, (short) 91, (short) 41, (short) 188, (short) 107); public static final int LOGIN_NOTLOGGEDIN = 0; public static final int LOGIN_SERVER_TRANSITION = 1; @@ -83,12 +87,14 @@ public class MapleClient extends ChannelInboundHandlerAdapter { public static final String CLIENT_NIBBLEHWID = "HWID2"; public static final String CLIENT_REMOTE_ADDRESS = "REMOTE_IP"; - private String hostAddress; + private String remoteAddress; private volatile boolean inTransition; private MapleAESOFB send; private MapleAESOFB receive; private final IoSession session; + + private io.netty.channel.Channel ioChannel; private PacketProcessor packetProcessor; private MapleCharacter player; private int channel = 1; @@ -98,7 +104,7 @@ public class MapleClient extends ChannelInboundHandlerAdapter { private Calendar birthday = null; private String accountName = null; private int world; - private long lastPong; + private volatile long lastPong; private int gmlevel; private Set macs = new HashSet<>(); private Map engines = new HashMap<>(); @@ -149,13 +155,19 @@ public class MapleClient extends ChannelInboundHandlerAdapter { @Override public void channelActive(ChannelHandlerContext ctx) { + if (!Server.getInstance().isOnline()) { + ctx.channel().close(); + return; + } + String hostAddress = "null"; try { hostAddress = ((InetSocketAddress) ctx.channel().remoteAddress()).getAddress().getHostAddress(); } catch (NullPointerException npe) { log.warn("Unable to get remote address for client", npe); } - this.hostAddress = hostAddress; + this.remoteAddress = hostAddress; + this.ioChannel = ctx.channel(); } @Override @@ -168,6 +180,11 @@ public class MapleClient extends ChannelInboundHandlerAdapter { short opcode = packet.readShort(); final MaplePacketHandler handler = packetProcessor.getHandler(opcode); + + if (YamlConfig.config.server.USE_DEBUG_SHOW_RCVD_PACKET && !ignoredDebugRecvPackets.contains(opcode)) { + log.debug("Received packet id {}", opcode); + } + if (handler != null && handler.validateState(this)) { // TODO: pass InPacket directly to handler once all handlers have been ported, // this is just a temporary workaround @@ -185,15 +202,50 @@ public class MapleClient extends ChannelInboundHandlerAdapter { super.channelRead(ctx, msg); } + @Override + public void userEventTriggered(ChannelHandlerContext ctx, Object event) { + if (event instanceof IdleStateEvent idleEvent) { + checkIfIdle(idleEvent); + } + } + @Override public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + if (player != null) { + log.warn("Exception caught by {}", player, cause); + } + if (cause instanceof InvalidPacketHeaderException) { // TODO close session through MapleSessionCoordinator + } else if (cause instanceof IOException) { + closeSession(); + } else { + } super.exceptionCaught(ctx, cause); } + @Override + public void channelInactive(ChannelHandlerContext ctx) { + try { + // client freeze issues on session transition states found thanks to yolinlin, Omo Oppa, Nozphex + if (!inTransition) { + disconnect(false, false); + } + } catch (Throwable t) { + log.warn("Account stuck", t); + } + } + + public void closeSession() { + ioChannel.close(); + } + + public void disconnectSession() { + ioChannel.disconnect(); + } + public MapleAESOFB getReceiveCrypto() { return receive; } @@ -206,8 +258,8 @@ public class MapleClient extends ChannelInboundHandlerAdapter { return session; } - public String getHostAddress() { - return hostAddress; + public String getRemoteAddress() { + return remoteAddress; } public boolean isInTransition() { @@ -1101,6 +1153,24 @@ public class MapleClient extends ChannelInboundHandlerAdapter { } } + public void checkIfIdle(final IdleStateEvent event) { + final long pingedAt = System.currentTimeMillis(); + announce(MaplePacketCreator.getPing()); + TimerManager.getInstance().schedule(() -> { + try { + if (lastPong < pingedAt) { + if (ioChannel.isActive()) { + log.info("Disconnected {} due to being idle. Idle state: {}", remoteAddress, event.state()); + updateLoginState(MapleClient.LOGIN_NOTLOGGEDIN); + disconnectSession(); + } + } + } catch (NullPointerException e) { + e.printStackTrace(); + } + }, TimeUnit.SECONDS.toMillis(15)); + } + public String getHWID() { return hwid; } diff --git a/src/main/java/net/netty/ServerChannelInitializer.java b/src/main/java/net/netty/ServerChannelInitializer.java index 869d47ab2f..927e20cb05 100644 --- a/src/main/java/net/netty/ServerChannelInitializer.java +++ b/src/main/java/net/netty/ServerChannelInitializer.java @@ -5,6 +5,7 @@ import constants.net.ServerConstants; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelInitializer; import io.netty.channel.socket.SocketChannel; +import io.netty.handler.timeout.IdleStateHandler; import tools.MaplePacketCreator; import java.util.concurrent.atomic.AtomicLong; @@ -16,6 +17,7 @@ public abstract class ServerChannelInitializer extends ChannelInitializer Date: Fri, 25 Jun 2021 14:43:05 +0200 Subject: [PATCH 09/37] Packet sending during Netty migration --- src/main/java/client/MapleClient.java | 30 +++++++++++++++++-- .../net/netty/LoginServerInitializer.java | 4 +-- .../java/net/packet/ByteBufOutPacket.java | 5 ++++ 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/src/main/java/client/MapleClient.java b/src/main/java/client/MapleClient.java index fc9ee2f33b..e7a1406454 100644 --- a/src/main/java/client/MapleClient.java +++ b/src/main/java/client/MapleClient.java @@ -30,7 +30,9 @@ import io.netty.handler.timeout.IdleStateEvent; import net.MaplePacketHandler; import net.PacketProcessor; import net.netty.InvalidPacketHeaderException; +import net.packet.ByteBufOutPacket; import net.packet.InPacket; +import net.packet.OutPacket; import net.server.Server; import net.server.audit.locks.MonitoredLockType; import net.server.audit.locks.factory.MonitoredReentrantLockFactory; @@ -1160,7 +1162,7 @@ public class MapleClient extends ChannelInboundHandlerAdapter { try { if (lastPong < pingedAt) { if (ioChannel.isActive()) { - log.info("Disconnected {} due to being idle. Idle state: {}", remoteAddress, event.state()); + log.info("Disconnected {} due to being idle. Cause: {}", remoteAddress, event.state()); updateLoginState(MapleClient.LOGIN_NOTLOGGEDIN); disconnectSession(); } @@ -1462,10 +1464,34 @@ public class MapleClient extends ChannelInboundHandlerAdapter { } } + @Deprecated(forRemoval = true, since = "Netty migration") public void announce(final byte[] packet) { // thanks GitGud for noticing an opportunity for improvement by overcoming "synchronized announce" announcerLock.lock(); try { - session.write(packet); + // session.write(packet); + sendPacket(packet); + } finally { + announcerLock.unlock(); + } + } + + // Workaround for old packets. All uses of Client#announce(byte[]) should be migrated to Client#sendPacket(OutPacket) + private void sendPacket(final byte[] packet) { + announcerLock.lock(); + try { + OutPacket outPacket = new ByteBufOutPacket(); + outPacket.writeBytes(packet); + + ioChannel.writeAndFlush(outPacket); + } finally { + announcerLock.unlock(); + } + } + + public void sendPacket(OutPacket outPacket) { + announcerLock.lock(); + try { + ioChannel.writeAndFlush(outPacket.getBytes()); } finally { announcerLock.unlock(); } diff --git a/src/main/java/net/netty/LoginServerInitializer.java b/src/main/java/net/netty/LoginServerInitializer.java index 7b9c301459..592589861b 100644 --- a/src/main/java/net/netty/LoginServerInitializer.java +++ b/src/main/java/net/netty/LoginServerInitializer.java @@ -11,11 +11,11 @@ public class LoginServerInitializer extends ServerChannelInitializer { @Override public void initChannel(SocketChannel socketChannel) { - final String clientIp = socketChannel.remoteAddress().getHostName(); + final String clientIp = socketChannel.remoteAddress().getHostString(); log.debug("Client connected to login server from {} ", clientIp); PacketProcessor packetProcessor = PacketProcessor.getLoginServerProcessor(); - final MapleClient client = new MapleClient(packetProcessor, LoginServer.WORLD, LoginServer.CHANNEL); + final MapleClient client = new MapleClient(packetProcessor, LoginServer.WORLD_ID, LoginServer.CHANNEL_ID); client.setSessionId(sessionId.getAndIncrement()); initPipeline(socketChannel, client); diff --git a/src/main/java/net/packet/ByteBufOutPacket.java b/src/main/java/net/packet/ByteBufOutPacket.java index 5aa7a20b79..b062afcd3b 100644 --- a/src/main/java/net/packet/ByteBufOutPacket.java +++ b/src/main/java/net/packet/ByteBufOutPacket.java @@ -12,6 +12,11 @@ import java.awt.*; public class ByteBufOutPacket implements OutPacket { private final ByteBuf byteBuf; + @Deprecated(forRemoval = true) + public ByteBufOutPacket() { + this.byteBuf = Unpooled.buffer(); + } + public ByteBufOutPacket(SendOpcode op) { ByteBuf byteBuf = Unpooled.buffer(); byteBuf.writeShortLE((short) op.getValue()); From b19f65f7ee885cf220e3c2ae637527c2b4c4abb7 Mon Sep 17 00:00:00 2001 From: P0nk Date: Sat, 26 Jun 2021 23:40:59 +0200 Subject: [PATCH 10/37] Encapsulate ip address logic in new class --- src/main/java/net/MapleServerHandler.java | 3 +- src/main/java/net/server/Server.java | 5 ++- .../coordinator/session/IpAddresses.java | 39 ++++++++++++++++++ .../session/MapleSessionCoordinator.java | 40 ------------------- 4 files changed, 44 insertions(+), 43 deletions(-) create mode 100644 src/main/java/net/server/coordinator/session/IpAddresses.java diff --git a/src/main/java/net/MapleServerHandler.java b/src/main/java/net/MapleServerHandler.java index 3a6afca5da..78ee117c28 100644 --- a/src/main/java/net/MapleServerHandler.java +++ b/src/main/java/net/MapleServerHandler.java @@ -30,6 +30,7 @@ import net.server.audit.LockCollector; import net.server.audit.locks.MonitoredLockType; import net.server.audit.locks.MonitoredReentrantLock; import net.server.audit.locks.factory.MonitoredReentrantLockFactory; +import net.server.coordinator.session.IpAddresses; import net.server.coordinator.session.MapleSessionCoordinator; import org.apache.mina.core.service.IoHandlerAdapter; import org.apache.mina.core.session.IdleStatus; @@ -105,7 +106,7 @@ public class MapleServerHandler extends IoHandlerAdapter { if (remoteHost == null) { remoteHost = "null"; } else { - remoteHost = MapleSessionCoordinator.fetchRemoteAddress(remoteHost); // thanks dyz for noticing Local/LAN/WAN connections not interacting properly + remoteHost = IpAddresses.evaluateRemoteAddress(remoteHost); // thanks dyz for noticing Local/LAN/WAN connections not interacting properly } } catch (NullPointerException npe) { // thanks Agassy, Alchemist for pointing out possibility of remoteHost = null. remoteHost = "null"; diff --git a/src/main/java/net/server/Server.java b/src/main/java/net/server/Server.java index 40f227a386..0acf9c4b32 100644 --- a/src/main/java/net/server/Server.java +++ b/src/main/java/net/server/Server.java @@ -47,6 +47,7 @@ import net.server.audit.locks.factory.MonitoredReadLockFactory; import net.server.audit.locks.factory.MonitoredReentrantLockFactory; import net.server.audit.locks.factory.MonitoredWriteLockFactory; import net.server.channel.Channel; +import net.server.coordinator.session.IpAddresses; import net.server.coordinator.session.MapleSessionCoordinator; import net.server.guild.MapleAlliance; import net.server.guild.MapleGuild; @@ -297,9 +298,9 @@ public class Server { String remoteIp = MapleSessionCoordinator.getSessionRemoteAddress(session); String[] hostAddress = getIP(world, channel).split(":"); - if (MapleSessionCoordinator.isLocalAddress(remoteIp)) { + if (IpAddresses.isLocalAddress(remoteIp)) { hostAddress[0] = YamlConfig.config.server.LOCALHOST; - } else if (MapleSessionCoordinator.isLanAddress(remoteIp)) { + } else if (IpAddresses.isLanAddress(remoteIp)) { hostAddress[0] = YamlConfig.config.server.LANHOST; } diff --git a/src/main/java/net/server/coordinator/session/IpAddresses.java b/src/main/java/net/server/coordinator/session/IpAddresses.java new file mode 100644 index 0000000000..16d551f6e5 --- /dev/null +++ b/src/main/java/net/server/coordinator/session/IpAddresses.java @@ -0,0 +1,39 @@ +package net.server.coordinator.session; + +import config.YamlConfig; + +import java.util.List; +import java.util.regex.Pattern; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class IpAddresses { + private static final List LOCAL_ADDRESS_PATTERNS = loadLocalAddressPatterns(); + + private static List loadLocalAddressPatterns() { + return Stream.of("10\\.", "192\\.168\\.", "172\\.(1[6-9]|2[0-9]|3[0-1])\\.") + .map(Pattern::compile) + .collect(Collectors.toList()); + } + + public static String evaluateRemoteAddress(String inetAddress) { + if (isLocalAddress(inetAddress) || isLanAddress(inetAddress)) { + return YamlConfig.config.server.HOST; + } else { + return inetAddress; + } + } + + public static boolean isLocalAddress(String inetAddress) { + return inetAddress.startsWith("127."); + } + + public static boolean isLanAddress(String inetAddress) { + return LOCAL_ADDRESS_PATTERNS.stream() + .anyMatch(pattern -> matchesPattern(pattern, inetAddress)); + } + + private static boolean matchesPattern(Pattern pattern, String searchTerm) { + return pattern.matcher(searchTerm).find(); + } +} diff --git a/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java b/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java index 93e4135649..a41b296c11 100644 --- a/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java +++ b/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java @@ -209,46 +209,6 @@ public class MapleSessionCoordinator { return poolLock.get(Math.abs(remoteHost.hashCode()) % 100); } - private static List localComms = loadLocalCommPatterns(); - - private static List loadLocalCommPatterns() { - String[] localComms = {"10\\.", "192\\.168\\.", "172\\.(1[6-9]|2[0-9]|3[0-1])\\."}; - List llc = new ArrayList<>(localComms.length); - - for (String lc : localComms) { - llc.add(Pattern.compile(lc)); - } - - return llc; - } - - private static boolean matchesLanAddress(Pattern inetPattern, String inetAddress) { - Matcher searchM = inetPattern.matcher(inetAddress); - return searchM.find(); - } - - public static boolean isLanAddress(String inetAddress) { - for (Pattern lanPattern : localComms) { - if (matchesLanAddress(lanPattern, inetAddress)) { - return true; - } - } - - return false; - } - - public static boolean isLocalAddress(String inetAddress) { - return inetAddress.startsWith("127."); - } - - public static String fetchRemoteAddress(String inetAddress) { - if (isLocalAddress(inetAddress) || isLanAddress(inetAddress)) { - return YamlConfig.config.server.HOST; - } else { - return inetAddress; - } - } - public static String getSessionRemoteAddress(IoSession session) { return (String) session.getAttribute(MapleClient.CLIENT_REMOTE_ADDRESS); } From 8041ccd6bd85e0e117478ce38ad8f294b9b649f2 Mon Sep 17 00:00:00 2001 From: P0nk Date: Sun, 27 Jun 2021 09:43:40 +0200 Subject: [PATCH 11/37] Move some session database logic to dedicated class I need to clean up this class before attempting to migrate away from IoSession for session handling. --- .../session/MapleSessionCoordinator.java | 68 +++++++------------ .../coordinator/session/SessionDAO.java | 53 +++++++++++++++ 2 files changed, 77 insertions(+), 44 deletions(-) create mode 100644 src/main/java/net/server/coordinator/session/SessionDAO.java diff --git a/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java b/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java index a41b296c11..032f9acf99 100644 --- a/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java +++ b/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java @@ -33,13 +33,13 @@ import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; 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.locks.Lock; import java.util.concurrent.locks.ReentrantLock; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import java.util.stream.Collectors; /** * @@ -129,38 +129,23 @@ public class MapleSessionCoordinator { ps.executeUpdate(); } } - - private static void registerAccessAccount(Connection con, String remoteHwid, int accountId) throws SQLException { - try (PreparedStatement ps = con.prepareStatement("INSERT INTO hwidaccounts (accountid, hwid, expiresat) VALUES (?, ?, ?)")) { - ps.setInt(1, accountId); - ps.setString(2, remoteHwid); - ps.setTimestamp(3, new java.sql.Timestamp(Server.getInstance().getCurrentTime() + hwidExpirationUpdate(0))); - - ps.executeUpdate(); - } - } - + + /** + * @return false if it was already associated, true if a new association was created in this call + */ private static boolean associateHwidAccountIfAbsent(String remoteHwid, int accountId) { try (Connection con = DatabaseConnection.getConnection()) { - int hwidCount = 0; + List hwids = SessionDAO.getHwidsForAccount(con, accountId); - try (PreparedStatement ps = con.prepareStatement("SELECT SQL_CACHE hwid FROM hwidaccounts WHERE accountid = ?")) { - ps.setInt(1, accountId); - try (ResultSet rs = ps.executeQuery()) { - while (rs.next()) { - String rsHwid = rs.getString("hwid"); - if (rsHwid.contentEquals(remoteHwid)) { - return false; - } + boolean containsRemoteHwid = hwids.stream().anyMatch(hwid -> hwid.contentEquals(remoteHwid)); + if (containsRemoteHwid) { + return false; + } - hwidCount++; - } - } - - if (hwidCount < YamlConfig.config.server.MAX_ALLOWED_ACCOUNT_HWID) { - registerAccessAccount(con, remoteHwid, accountId); - return true; - } + if (hwids.size() < YamlConfig.config.server.MAX_ALLOWED_ACCOUNT_HWID) { + Instant expiry = Instant.ofEpochMilli(Server.getInstance().getCurrentTime() + hwidExpirationUpdate(0)); + SessionDAO.registerAccountAccess(con, accountId, remoteHwid, expiry); + return true; } } catch (SQLException ex) { ex.printStackTrace(); @@ -551,12 +536,7 @@ public class MapleSessionCoordinator { } public void runUpdateHwidHistory() { - try (Connection con = DatabaseConnection.getConnection(); - PreparedStatement ps = con.prepareStatement("DELETE FROM hwidaccounts WHERE expiresat < CURRENT_TIMESTAMP")) { - ps.executeUpdate(); - } catch (SQLException ex) { - ex.printStackTrace(); - } + SessionDAO.deleteExpiredHwidAccounts(); long timeNow = Server.getInstance().getCurrentTime(); List toRemove = new LinkedList<>(); @@ -581,12 +561,13 @@ public class MapleSessionCoordinator { public void printSessionTrace() { if (!onlineClients.isEmpty()) { List> elist = new ArrayList<>(onlineClients.entrySet()); - elist.sort((e1, e2) -> e1.getKey().compareTo(e2.getKey())); + String commaSeparatedClients = elist.stream() + .map(Entry::getKey) + .sorted(Integer::compareTo) + .map(Object::toString) + .collect(Collectors.joining(", ")); - System.out.println("Current online clients: "); - for (Entry e : elist) { - System.out.println(" " + e.getKey()); - } + System.out.println("Current online clients: " + commaSeparatedClients); } if (!onlineRemoteHwids.isEmpty()) { @@ -601,8 +582,7 @@ public class MapleSessionCoordinator { if (!loginRemoteHosts.isEmpty()) { List>> elist = new ArrayList<>(loginRemoteHosts.entrySet()); - - elist.sort((e1, e2) -> e1.getKey().compareTo(e2.getKey())); + elist.sort(Entry.comparingByKey()); System.out.println("Current login sessions: "); for (Entry> e : elist) { @@ -616,7 +596,7 @@ public class MapleSessionCoordinator { if (!onlineClients.isEmpty()) { List> elist = new ArrayList<>(onlineClients.entrySet()); - elist.sort((e1, e2) -> e1.getKey().compareTo(e2.getKey())); + elist.sort(Entry.comparingByKey()); str += ("Current online clients:\r\n"); for (Entry e : elist) { diff --git a/src/main/java/net/server/coordinator/session/SessionDAO.java b/src/main/java/net/server/coordinator/session/SessionDAO.java new file mode 100644 index 0000000000..5d822a4cc1 --- /dev/null +++ b/src/main/java/net/server/coordinator/session/SessionDAO.java @@ -0,0 +1,53 @@ +package net.server.coordinator.session; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import tools.DatabaseConnection; + +import java.sql.*; +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; + +public class SessionDAO { + private static final Logger log = LoggerFactory.getLogger(SessionDAO.class); + + public static void deleteExpiredHwidAccounts() { + final String query = "DELETE FROM hwidaccounts WHERE expiresat < CURRENT_TIMESTAMP"; + try (Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement(query)) { + ps.executeUpdate(); + } catch (SQLException e) { + log.warn("Failed to delete expired hwidaccounts", e); + } + } + + public static List getHwidsForAccount(Connection con, int accountId) throws SQLException { + final List hwids = new ArrayList<>(); + + final String query = "SELECT hwid FROM hwidaccounts WHERE accountid = ?"; + try (PreparedStatement ps = con.prepareStatement(query)) { + ps.setInt(1, accountId); + + try (ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + hwids.add(rs.getString("hwid")); + } + } + } + + return hwids; + } + + public static void registerAccountAccess(Connection con, int accountId, String remoteHwid, Instant expiry) + throws SQLException { + final String query = "INSERT INTO hwidaccounts (accountid, hwid, expiresat) VALUES (?, ?, ?)"; + try (PreparedStatement ps = con.prepareStatement(query)) { + ps.setInt(1, accountId); + ps.setString(2, remoteHwid); + ps.setTimestamp(3, Timestamp.from(expiry)); + + ps.executeUpdate(); + } + } +} From 2c18f7a4a8613b600a852bc3259a8aa2106f9259 Mon Sep 17 00:00:00 2001 From: P0nk Date: Sun, 27 Jun 2021 21:35:43 +0200 Subject: [PATCH 12/37] Move the remaining session database logic --- .../coordinator/session/HwidRelevance.java | 4 + .../session/MapleSessionCoordinator.java | 76 ++++++++----------- .../coordinator/session/SessionDAO.java | 32 ++++++++ 3 files changed, 67 insertions(+), 45 deletions(-) create mode 100644 src/main/java/net/server/coordinator/session/HwidRelevance.java diff --git a/src/main/java/net/server/coordinator/session/HwidRelevance.java b/src/main/java/net/server/coordinator/session/HwidRelevance.java new file mode 100644 index 0000000000..bfd1e47219 --- /dev/null +++ b/src/main/java/net/server/coordinator/session/HwidRelevance.java @@ -0,0 +1,4 @@ +package net.server.coordinator.session; + +public record HwidRelevance(String hwid, int relevance) { +} diff --git a/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java b/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java index 032f9acf99..2029cb494e 100644 --- a/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java +++ b/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java @@ -27,6 +27,8 @@ import net.server.audit.locks.MonitoredLockType; import net.server.audit.locks.factory.MonitoredReentrantLockFactory; import net.server.coordinator.login.LoginStorage; import org.apache.mina.core.session.IoSession; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import tools.DatabaseConnection; import java.sql.Connection; @@ -46,8 +48,8 @@ import java.util.stream.Collectors; * @author Ronan */ public class MapleSessionCoordinator { - - private final static MapleSessionCoordinator instance = new MapleSessionCoordinator(); + private static final Logger log = LoggerFactory.getLogger(MapleSessionCoordinator.class); + private static final MapleSessionCoordinator instance = new MapleSessionCoordinator(); public static MapleSessionCoordinator getInstance() { return instance; @@ -78,9 +80,15 @@ public class MapleSessionCoordinator { poolLock.add(MonitoredReentrantLockFactory.createLock(MonitoredLockType.SERVER_LOGIN_COORD)); } } + + private static Instant getHwidAccountExpiry(int relevance) { + return Instant.ofEpochMilli(Server.getInstance().getCurrentTime() + hwidExpirationUpdate(relevance)); + } private static long hwidExpirationUpdate(int relevance) { - int degree = 1, i = relevance, subdegree; + int degree = 1; + int i = relevance; + int subdegree; while ((subdegree = 5 * degree) <= i) { i -= subdegree; degree++; @@ -113,22 +121,6 @@ public class MapleSessionCoordinator { return 3600000 * (baseTime + subdegreeTime); } - - private static void updateAccessAccount(Connection con, String remoteHwid, int accountId, int loginRelevance) throws SQLException { - java.sql.Timestamp nextTimestamp = new java.sql.Timestamp(Server.getInstance().getCurrentTime() + hwidExpirationUpdate(loginRelevance)); - if(loginRelevance < Byte.MAX_VALUE) { - loginRelevance++; - } - - try (PreparedStatement ps = con.prepareStatement("UPDATE hwidaccounts SET relevance = ?, expiresat = ? WHERE accountid = ? AND hwid LIKE ?")) { - ps.setInt(1, loginRelevance); - ps.setTimestamp(2, nextTimestamp); - ps.setInt(3, accountId); - ps.setString(4, remoteHwid); - - ps.executeUpdate(); - } - } /** * @return false if it was already associated, true if a new association was created in this call @@ -143,7 +135,7 @@ public class MapleSessionCoordinator { } if (hwids.size() < YamlConfig.config.server.MAX_ALLOWED_ACCOUNT_HWID) { - Instant expiry = Instant.ofEpochMilli(Server.getInstance().getCurrentTime() + hwidExpirationUpdate(0)); + Instant expiry = getHwidAccountExpiry(0); SessionDAO.registerAccountAccess(con, accountId, remoteHwid, expiry); return true; } @@ -154,37 +146,31 @@ public class MapleSessionCoordinator { return false; } - private static boolean attemptAccessAccount(String nibbleHwid, int accountId, boolean routineCheck) { + private static boolean attemptAccountAccess(int accountId, String nibbleHwid, boolean routineCheck) { try (Connection con = DatabaseConnection.getConnection()) { - int hwidCount = 0; - - try (PreparedStatement ps = con.prepareStatement("SELECT SQL_CACHE * FROM hwidaccounts WHERE accountid = ?")) { - ps.setInt(1, accountId); - - try (ResultSet rs = ps.executeQuery()) { - while (rs.next()) { - String rsHwid = rs.getString("hwid"); - if (rsHwid.endsWith(nibbleHwid)) { - if (!routineCheck) { - // better update HWID relevance as soon as the login is authenticated - - int loginRelevance = rs.getInt("relevance"); - updateAccessAccount(con, rsHwid, accountId, loginRelevance); - } - - return true; + List hwidRelevances = SessionDAO.getHwidRelevance(con, accountId); + for (HwidRelevance hwidRelevance : hwidRelevances) { + if (hwidRelevance.hwid().endsWith(nibbleHwid)) { + if (!routineCheck) { + // better update HWID relevance as soon as the login is authenticated + Instant expiry = getHwidAccountExpiry(hwidRelevance.relevance()); + int relevance = hwidRelevance.relevance(); + if (relevance < Byte.MAX_VALUE) { + relevance++; } - hwidCount++; + SessionDAO.updateAccountAccess(con, nibbleHwid, accountId, expiry, relevance); } - } - if (hwidCount < YamlConfig.config.server.MAX_ALLOWED_ACCOUNT_HWID) { return true; } } - } catch (SQLException ex) { - ex.printStackTrace(); + + if (hwidRelevances.size() < YamlConfig.config.server.MAX_ALLOWED_ACCOUNT_HWID) { + return true; + } + } catch (SQLException e) { + log.warn("Failed to update account access. Account id: {}, nibbleHwid: {}", accountId, nibbleHwid, e); } return false; @@ -363,14 +349,14 @@ public class MapleSessionCoordinator { return AntiMulticlientResult.REMOTE_LOGGEDIN; } - if (!attemptAccessAccount(nibbleHwid, accountId, routineCheck)) { + if (!attemptAccountAccess(accountId, nibbleHwid, routineCheck)) { return AntiMulticlientResult.REMOTE_REACHED_LIMIT; } session.setAttribute(MapleClient.CLIENT_NIBBLEHWID, nibbleHwid); onlineRemoteHwids.add(nibbleHwid); } else { - if (!attemptAccessAccount(nibbleHwid, accountId, routineCheck)) { + if (!attemptAccountAccess(accountId, nibbleHwid, routineCheck)) { return AntiMulticlientResult.REMOTE_REACHED_LIMIT; } } diff --git a/src/main/java/net/server/coordinator/session/SessionDAO.java b/src/main/java/net/server/coordinator/session/SessionDAO.java index 5d822a4cc1..b4edafff87 100644 --- a/src/main/java/net/server/coordinator/session/SessionDAO.java +++ b/src/main/java/net/server/coordinator/session/SessionDAO.java @@ -50,4 +50,36 @@ public class SessionDAO { ps.executeUpdate(); } } + + public static List getHwidRelevance(Connection con, int accountId) throws SQLException { + final List hwidRelevances = new ArrayList<>(); + + final String query = "SELECT * FROM hwidaccounts WHERE accountid = ?"; + try (PreparedStatement ps = con.prepareStatement(query)) { + ps.setInt(1, accountId); + + try (ResultSet rs = ps.executeQuery()) { + while (rs.next()) { + String hwid = rs.getString("hwid"); + int relevance = rs.getInt("relevance"); + hwidRelevances.add(new HwidRelevance(hwid, relevance)); + } + } + } + + return hwidRelevances; + } + + public static void updateAccountAccess(Connection con, String remoteHwid, int accountId, Instant expiry, int loginRelevance) + throws SQLException { + final String query = "UPDATE hwidaccounts SET relevance = ?, expiresat = ? WHERE accountid = ? AND hwid LIKE ?"; + try (PreparedStatement ps = con.prepareStatement(query)) { + ps.setInt(1, loginRelevance); + ps.setTimestamp(2, Timestamp.from(expiry)); + ps.setInt(3, accountId); + ps.setString(4, remoteHwid); + + ps.executeUpdate(); + } + } } From b35c4d3de8a9293d678cb3ef7a829dc839f8b968 Mon Sep 17 00:00:00 2001 From: P0nk Date: Mon, 28 Jun 2021 10:37:06 +0200 Subject: [PATCH 13/37] Handle hwid assocation expiry in dedicated class --- .../session/HwidAssociationExpiry.java | 41 +++++++++++++++ .../session/MapleSessionCoordinator.java | 51 +++---------------- 2 files changed, 48 insertions(+), 44 deletions(-) create mode 100644 src/main/java/net/server/coordinator/session/HwidAssociationExpiry.java diff --git a/src/main/java/net/server/coordinator/session/HwidAssociationExpiry.java b/src/main/java/net/server/coordinator/session/HwidAssociationExpiry.java new file mode 100644 index 0000000000..5c93f96802 --- /dev/null +++ b/src/main/java/net/server/coordinator/session/HwidAssociationExpiry.java @@ -0,0 +1,41 @@ +package net.server.coordinator.session; + +import net.server.Server; + +import java.time.Instant; +import java.util.concurrent.TimeUnit; + +public class HwidAssociationExpiry { + public static Instant getHwidAccountExpiry(int relevance) { + return Instant.ofEpochMilli(Server.getInstance().getCurrentTime()).plusMillis(hwidExpirationUpdate(relevance)); + } + + private static long hwidExpirationUpdate(int relevance) { + int degree = getHwidExpirationDegree(relevance); + + final long baseHours = switch (degree) { + case 0 -> 2; + case 1 -> TimeUnit.DAYS.toHours(1); + case 2 -> TimeUnit.DAYS.toHours(7); + default -> TimeUnit.DAYS.toHours(70); + }; + + int subdegreeTime = (degree * 3) + 1; + if (subdegreeTime > 10) { + subdegreeTime = 10; + } + + return TimeUnit.HOURS.toMillis(baseHours + subdegreeTime); + } + + private static int getHwidExpirationDegree(int relevance) { + int degree = 1; + int subdegree; + while ((subdegree = 5 * degree) <= relevance) { + relevance -= subdegree; + degree++; + } + + return --degree; + } +} diff --git a/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java b/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java index 2029cb494e..823b71218f 100644 --- a/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java +++ b/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java @@ -81,47 +81,6 @@ public class MapleSessionCoordinator { } } - private static Instant getHwidAccountExpiry(int relevance) { - return Instant.ofEpochMilli(Server.getInstance().getCurrentTime() + hwidExpirationUpdate(relevance)); - } - - private static long hwidExpirationUpdate(int relevance) { - int degree = 1; - int i = relevance; - int subdegree; - while ((subdegree = 5 * degree) <= i) { - i -= subdegree; - degree++; - } - - degree--; - int baseTime, subdegreeTime; - if (degree > 2) { - subdegreeTime = 10; - } else { - subdegreeTime = 1 + (3 * degree); - } - - switch(degree) { - case 0: - baseTime = 2; // 2 hours - break; - - case 1: - baseTime = 24; // 1 day - break; - - case 2: - baseTime = 168; // 7 days - break; - - default: - baseTime = 1680; // 70 days - } - - return 3600000 * (baseTime + subdegreeTime); - } - /** * @return false if it was already associated, true if a new association was created in this call */ @@ -135,7 +94,7 @@ public class MapleSessionCoordinator { } if (hwids.size() < YamlConfig.config.server.MAX_ALLOWED_ACCOUNT_HWID) { - Instant expiry = getHwidAccountExpiry(0); + Instant expiry = HwidAssociationExpiry.getHwidAccountExpiry(0); SessionDAO.registerAccountAccess(con, accountId, remoteHwid, expiry); return true; } @@ -153,7 +112,7 @@ public class MapleSessionCoordinator { if (hwidRelevance.hwid().endsWith(nibbleHwid)) { if (!routineCheck) { // better update HWID relevance as soon as the login is authenticated - Instant expiry = getHwidAccountExpiry(hwidRelevance.relevance()); + Instant expiry = HwidAssociationExpiry.getHwidAccountExpiry(hwidRelevance.relevance()); int relevance = hwidRelevance.relevance(); if (relevance < Byte.MAX_VALUE) { relevance++; @@ -518,7 +477,11 @@ public class MapleSessionCoordinator { private void associateRemoteHostHwid(String remoteHost, String remoteHwid) { cachedHostHwids.put(remoteHost, remoteHwid); - cachedHostTimeout.put(remoteHost, Server.getInstance().getCurrentTime() + 604800000); // 1 week-time entry + cachedHostTimeout.put(remoteHost, getHostTimeout()); + } + + private static long getHostTimeout() { + return Server.getInstance().getCurrentTime() + TimeUnit.DAYS.toMillis(7); // 1 week-time entry } public void runUpdateHwidHistory() { From 69a4dd8c6dfb043c9a8bf335a4c0244f369057df Mon Sep 17 00:00:00 2001 From: P0nk Date: Mon, 28 Jun 2021 11:02:06 +0200 Subject: [PATCH 14/37] Factor out duplicated code that adds remote host to pool --- .../session/MapleSessionCoordinator.java | 283 ++++++++---------- 1 file changed, 120 insertions(+), 163 deletions(-) diff --git a/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java b/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java index 823b71218f..0d9042b0ea 100644 --- a/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java +++ b/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java @@ -32,13 +32,12 @@ import org.slf4j.LoggerFactory; import tools.DatabaseConnection; import java.sql.Connection; -import java.sql.PreparedStatement; -import java.sql.ResultSet; 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.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; @@ -81,30 +80,6 @@ public class MapleSessionCoordinator { } } - /** - * @return false if it was already associated, true if a new association was created in this call - */ - private static boolean associateHwidAccountIfAbsent(String remoteHwid, int accountId) { - try (Connection con = DatabaseConnection.getConnection()) { - List hwids = SessionDAO.getHwidsForAccount(con, accountId); - - boolean containsRemoteHwid = hwids.stream().anyMatch(hwid -> hwid.contentEquals(remoteHwid)); - if (containsRemoteHwid) { - return false; - } - - if (hwids.size() < YamlConfig.config.server.MAX_ALLOWED_ACCOUNT_HWID) { - Instant expiry = HwidAssociationExpiry.getHwidAccountExpiry(0); - SessionDAO.registerAccountAccess(con, accountId, remoteHwid, expiry); - return true; - } - } catch (SQLException ex) { - ex.printStackTrace(); - } - - return false; - } - private static boolean attemptAccountAccess(int accountId, String nibbleHwid, boolean routineCheck) { try (Connection con = DatabaseConnection.getConnection()) { List hwidRelevances = SessionDAO.getHwidRelevance(con, accountId); @@ -172,38 +147,20 @@ public class MapleSessionCoordinator { } public boolean canStartLoginSession(IoSession session) { - if (!YamlConfig.config.server.DETERRED_MULTICLIENT) return true; + if (!YamlConfig.config.server.DETERRED_MULTICLIENT) { + return true; + } String remoteHost = getSessionRemoteHost(session); Lock lock = getCoodinatorLock(remoteHost); - - try { - int tries = 0; - while (true) { - if (lock.tryLock()) { - try { - if (pooledRemoteHosts.contains(remoteHost)) { - return false; - } - - pooledRemoteHosts.add(remoteHost); - } finally { - lock.unlock(); - } - - break; - } else { - if(tries == 2) { - return true; - } - tries++; - - Thread.sleep(1777); - } + AntiMulticlientResult result = addToRemoteHostPoolWithRetries(lock, remoteHost); + switch (result) { + case REMOTE_PROCESSING -> { + return false; + } + case COORDINATOR_ERROR -> { + return true; } - } catch (Exception e) { - e.printStackTrace(); - return true; } try { @@ -267,35 +224,10 @@ public class MapleSessionCoordinator { } String remoteHost = getSessionRemoteHost(session); - Lock lock = getCoodinatorLock(remoteHost); - - try { - int tries = 0; - while (true) { - if (lock.tryLock()) { - try { - if (pooledRemoteHosts.contains(remoteHost)) { - return AntiMulticlientResult.REMOTE_PROCESSING; - } - - pooledRemoteHosts.add(remoteHost); - } finally { - lock.unlock(); - } - - break; - } else { - if(tries == 2) { - return AntiMulticlientResult.COORDINATOR_ERROR; - } - tries++; - - Thread.sleep(1777); - } - } - } catch (Exception e) { - e.printStackTrace(); - return AntiMulticlientResult.COORDINATOR_ERROR; + final Lock lock = getCoodinatorLock(remoteHost); + AntiMulticlientResult hostPoolAdditionResult = addToRemoteHostPoolWithRetries(lock, remoteHost); + if (hostPoolAdditionResult != AntiMulticlientResult.SUCCESS) { + return hostPoolAdditionResult; } try { @@ -332,68 +264,44 @@ public class MapleSessionCoordinator { } public AntiMulticlientResult attemptGameSession(IoSession session, int accountId, String remoteHwid) { - String remoteHost = getSessionRemoteHost(session); + 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... return AntiMulticlientResult.SUCCESS; } - - Lock lock = getCoodinatorLock(remoteHost); - try { - int tries = 0; - while (true) { - if (lock.tryLock()) { - try { - if (pooledRemoteHosts.contains(remoteHost)) { - return AntiMulticlientResult.REMOTE_PROCESSING; - } - - pooledRemoteHosts.add(remoteHost); - } finally { - lock.unlock(); - } - - break; - } else { - if(tries == 2) { - return AntiMulticlientResult.COORDINATOR_ERROR; - } - tries++; - - Thread.sleep(1777); - } - } - } catch (Exception e) { - e.printStackTrace(); - return AntiMulticlientResult.COORDINATOR_ERROR; + + final Lock lock = getCoodinatorLock(remoteHost); + AntiMulticlientResult hostPoolAdditionResult = addToRemoteHostPoolWithRetries(lock, remoteHost); + if (hostPoolAdditionResult != AntiMulticlientResult.SUCCESS) { + return hostPoolAdditionResult; } try { String nibbleHwid = (String) session.getAttribute(MapleClient.CLIENT_NIBBLEHWID); // thanks Paxum for noticing account stuck after PIC failure - if (nibbleHwid != null) { - onlineRemoteHwids.remove(nibbleHwid); - - if (remoteHwid.endsWith(nibbleHwid)) { - if (!onlineRemoteHwids.contains(remoteHwid)) { - // assumption: after a SUCCESSFUL login attempt, the incoming client WILL receive a new IoSession from the game server - - // 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); - associateHwidAccountIfAbsent(remoteHwid, accountId); - - return AntiMulticlientResult.SUCCESS; - } else { - return AntiMulticlientResult.REMOTE_LOGGEDIN; - } - } else { - return AntiMulticlientResult.REMOTE_NO_MATCH; - } - } else { + if (nibbleHwid == null) { return AntiMulticlientResult.REMOTE_NO_MATCH; } + + onlineRemoteHwids.remove(nibbleHwid); + + if (!remoteHwid.endsWith(nibbleHwid)) { + return AntiMulticlientResult.REMOTE_NO_MATCH; + } + + if (onlineRemoteHwids.contains(remoteHwid)) { + return AntiMulticlientResult.REMOTE_LOGGEDIN; + } + + // assumption: after a SUCCESSFUL login attempt, the incoming client WILL receive a new IoSession from the game server + + // 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); + associateHwidAccountIfAbsent(remoteHwid, accountId); + + return AntiMulticlientResult.SUCCESS; } finally { lock.lock(); try { @@ -403,34 +311,85 @@ public class MapleSessionCoordinator { } } } + + private AntiMulticlientResult addToRemoteHostPoolWithRetries(Lock lock, String remoteHost) { + try { + int tries = 0; + while (true) { + if (lock.tryLock()) { + try { + if (pooledRemoteHosts.contains(remoteHost)) { + return AntiMulticlientResult.REMOTE_PROCESSING; + } + + pooledRemoteHosts.add(remoteHost); + } finally { + lock.unlock(); + } + + break; + } else { + if(tries == 2) { + return AntiMulticlientResult.COORDINATOR_ERROR; + } + tries++; + + Thread.sleep(1777); + } + } + } catch (Exception e) { + e.printStackTrace(); + return AntiMulticlientResult.COORDINATOR_ERROR; + } + + return AntiMulticlientResult.SUCCESS; + } + + private static void associateHwidAccountIfAbsent(String remoteHwid, int accountId) { + try (Connection con = DatabaseConnection.getConnection()) { + List hwids = SessionDAO.getHwidsForAccount(con, accountId); + + boolean containsRemoteHwid = hwids.stream().anyMatch(hwid -> hwid.contentEquals(remoteHwid)); + if (containsRemoteHwid) { + return; + } + + if (hwids.size() < YamlConfig.config.server.MAX_ALLOWED_ACCOUNT_HWID) { + Instant expiry = HwidAssociationExpiry.getHwidAccountExpiry(0); + SessionDAO.registerAccountAccess(con, accountId, remoteHwid, expiry); + } + } catch (SQLException ex) { + log.warn("Failed to associate hwid {} with account id {}", remoteHwid, accountId, ex); + } + } private static MapleClient fetchInTransitionSessionClient(IoSession session) { String remoteHwid = MapleSessionCoordinator.getInstance().getGameSessionHwid(session); - - if (remoteHwid != null) { // maybe this session was currently in-transition? - int hwidLen = remoteHwid.length(); - if (hwidLen <= 8) { - session.setAttribute(MapleClient.CLIENT_NIBBLEHWID, remoteHwid); - } else { - session.setAttribute(MapleClient.CLIENT_HWID, remoteHwid); - session.setAttribute(MapleClient.CLIENT_NIBBLEHWID, remoteHwid.substring(hwidLen - 8, hwidLen)); - } - - MapleClient client = new MapleClient(null, null, session); - Integer cid = Server.getInstance().freeCharacteridInTransition(client); - if (cid != null) { - try { - client.setAccID(MapleCharacter.loadCharFromDB(cid, client, false).getAccountID()); - } catch (SQLException sqle) { - sqle.printStackTrace(); - } - } - - session.setAttribute(MapleClient.CLIENT_KEY, client); - return client; + + if (remoteHwid == null) { // maybe this session was currently in-transition? + return null; } - - return null; + + int hwidLen = remoteHwid.length(); + if (hwidLen <= 8) { + session.setAttribute(MapleClient.CLIENT_NIBBLEHWID, remoteHwid); + } else { + session.setAttribute(MapleClient.CLIENT_HWID, remoteHwid); + session.setAttribute(MapleClient.CLIENT_NIBBLEHWID, remoteHwid.substring(hwidLen - 8, hwidLen)); + } + + MapleClient client = new MapleClient(null, null, session); + Integer cid = Server.getInstance().freeCharacteridInTransition(client); + if (cid != null) { + try { + client.setAccID(MapleCharacter.loadCharFromDB(cid, client, false).getAccountID()); + } catch (SQLException sqle) { + sqle.printStackTrace(); + } + } + + session.setAttribute(MapleClient.CLIENT_KEY, client); + return client; } public void closeSession(IoSession session, Boolean immediately) { @@ -495,11 +454,9 @@ public class MapleSessionCoordinator { } } - if (!toRemove.isEmpty()) { - for (String s : toRemove) { - cachedHostHwids.remove(s); - cachedHostTimeout.remove(s); - } + for (String s : toRemove) { + cachedHostHwids.remove(s); + cachedHostTimeout.remove(s); } } From 5bc2f47883467d2ad097155396a74121dd26bcc6 Mon Sep 17 00:00:00 2001 From: P0nk Date: Mon, 28 Jun 2021 12:00:09 +0200 Subject: [PATCH 15/37] Move session init management to new class --- .../session/InitializationResult.java | 20 +++++ .../session/MapleSessionCoordinator.java | 88 +++---------------- .../session/SessionInitialization.java | 88 +++++++++++++++++++ 3 files changed, 121 insertions(+), 75 deletions(-) create mode 100644 src/main/java/net/server/coordinator/session/InitializationResult.java create mode 100644 src/main/java/net/server/coordinator/session/SessionInitialization.java diff --git a/src/main/java/net/server/coordinator/session/InitializationResult.java b/src/main/java/net/server/coordinator/session/InitializationResult.java new file mode 100644 index 0000000000..dc18ef502c --- /dev/null +++ b/src/main/java/net/server/coordinator/session/InitializationResult.java @@ -0,0 +1,20 @@ +package net.server.coordinator.session; + +import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientResult; + +enum InitializationResult { + SUCCESS(AntiMulticlientResult.SUCCESS), + ALREADY_INITIALIZED(AntiMulticlientResult.REMOTE_PROCESSING), + TIMED_OUT(AntiMulticlientResult.COORDINATOR_ERROR), + ERROR(AntiMulticlientResult.COORDINATOR_ERROR); + + private final AntiMulticlientResult antiMulticlientResult; + + InitializationResult(AntiMulticlientResult antiMulticlientResult) { + this.antiMulticlientResult = antiMulticlientResult; + } + + public AntiMulticlientResult getAntiMulticlientResult() { + return antiMulticlientResult; + } +} diff --git a/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java b/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java index 0d9042b0ea..07fca506a0 100644 --- a/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java +++ b/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java @@ -23,8 +23,6 @@ import client.MapleCharacter; import client.MapleClient; import config.YamlConfig; import net.server.Server; -import net.server.audit.locks.MonitoredLockType; -import net.server.audit.locks.factory.MonitoredReentrantLockFactory; import net.server.coordinator.login.LoginStorage; import org.apache.mina.core.session.IoSession; import org.slf4j.Logger; @@ -38,8 +36,6 @@ import java.util.*; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; import java.util.stream.Collectors; /** @@ -63,7 +59,8 @@ public class MapleSessionCoordinator { MANY_ACCOUNT_ATTEMPTS, COORDINATOR_ERROR } - + + private final SessionInitialization sessionInit = new SessionInitialization(); private final LoginStorage loginStorage = new LoginStorage(); private final Map onlineClients = new HashMap<>(); private final Set onlineRemoteHwids = new HashSet<>(); @@ -72,12 +69,8 @@ public class MapleSessionCoordinator { private final ConcurrentHashMap cachedHostHwids = new ConcurrentHashMap<>(); private final ConcurrentHashMap cachedHostTimeout = new ConcurrentHashMap<>(); - private final List poolLock = new ArrayList<>(100); private MapleSessionCoordinator() { - for(int i = 0; i < 100; i++) { - poolLock.add(MonitoredReentrantLockFactory.createLock(MonitoredLockType.SERVER_LOGIN_COORD)); - } } private static boolean attemptAccountAccess(int accountId, String nibbleHwid, boolean routineCheck) { @@ -109,10 +102,6 @@ public class MapleSessionCoordinator { return false; } - - private Lock getCoodinatorLock(String remoteHost) { - return poolLock.get(Math.abs(remoteHost.hashCode()) % 100); - } public static String getSessionRemoteAddress(IoSession session) { return (String) session.getAttribute(MapleClient.CLIENT_REMOTE_ADDRESS); @@ -152,9 +141,8 @@ public class MapleSessionCoordinator { } String remoteHost = getSessionRemoteHost(session); - Lock lock = getCoodinatorLock(remoteHost); - AntiMulticlientResult result = addToRemoteHostPoolWithRetries(lock, remoteHost); - switch (result) { + final InitializationResult initResult = sessionInit.initialize(remoteHost); + switch (initResult.getAntiMulticlientResult()) { case REMOTE_PROCESSING -> { return false; } @@ -181,12 +169,7 @@ public class MapleSessionCoordinator { return true; } finally { - lock.lock(); - try { - pooledRemoteHosts.remove(remoteHost); - } finally { - lock.unlock(); - } + sessionInit.finalize(remoteHost); } } @@ -224,10 +207,9 @@ public class MapleSessionCoordinator { } String remoteHost = getSessionRemoteHost(session); - final Lock lock = getCoodinatorLock(remoteHost); - AntiMulticlientResult hostPoolAdditionResult = addToRemoteHostPoolWithRetries(lock, remoteHost); - if (hostPoolAdditionResult != AntiMulticlientResult.SUCCESS) { - return hostPoolAdditionResult; + InitializationResult initResult = sessionInit.initialize(remoteHost); + if (initResult != InitializationResult.SUCCESS) { + return initResult.getAntiMulticlientResult(); } try { @@ -254,12 +236,7 @@ public class MapleSessionCoordinator { return AntiMulticlientResult.SUCCESS; } finally { - lock.lock(); - try { - pooledRemoteHosts.remove(remoteHost); - } finally { - lock.unlock(); - } + sessionInit.finalize(remoteHost); } } @@ -271,10 +248,9 @@ public class MapleSessionCoordinator { return AntiMulticlientResult.SUCCESS; } - final Lock lock = getCoodinatorLock(remoteHost); - AntiMulticlientResult hostPoolAdditionResult = addToRemoteHostPoolWithRetries(lock, remoteHost); - if (hostPoolAdditionResult != AntiMulticlientResult.SUCCESS) { - return hostPoolAdditionResult; + final InitializationResult initResult = sessionInit.initialize(remoteHost); + if (initResult != InitializationResult.SUCCESS) { + return initResult.getAntiMulticlientResult(); } try { @@ -303,48 +279,10 @@ public class MapleSessionCoordinator { return AntiMulticlientResult.SUCCESS; } finally { - lock.lock(); - try { - pooledRemoteHosts.remove(remoteHost); - } finally { - lock.unlock(); - } + sessionInit.finalize(remoteHost); } } - private AntiMulticlientResult addToRemoteHostPoolWithRetries(Lock lock, String remoteHost) { - try { - int tries = 0; - while (true) { - if (lock.tryLock()) { - try { - if (pooledRemoteHosts.contains(remoteHost)) { - return AntiMulticlientResult.REMOTE_PROCESSING; - } - - pooledRemoteHosts.add(remoteHost); - } finally { - lock.unlock(); - } - - break; - } else { - if(tries == 2) { - return AntiMulticlientResult.COORDINATOR_ERROR; - } - tries++; - - Thread.sleep(1777); - } - } - } catch (Exception e) { - e.printStackTrace(); - return AntiMulticlientResult.COORDINATOR_ERROR; - } - - return AntiMulticlientResult.SUCCESS; - } - private static void associateHwidAccountIfAbsent(String remoteHwid, int accountId) { try (Connection con = DatabaseConnection.getConnection()) { List hwids = SessionDAO.getHwidsForAccount(con, accountId); diff --git a/src/main/java/net/server/coordinator/session/SessionInitialization.java b/src/main/java/net/server/coordinator/session/SessionInitialization.java new file mode 100644 index 0000000000..6e160099de --- /dev/null +++ b/src/main/java/net/server/coordinator/session/SessionInitialization.java @@ -0,0 +1,88 @@ +package net.server.coordinator.session; + +import net.server.audit.locks.MonitoredLockType; +import net.server.audit.locks.factory.MonitoredReentrantLockFactory; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.concurrent.locks.Lock; + +/** + * Manages session initialization using remote host (ip address). + */ +public class SessionInitialization { + private final static Logger log = LoggerFactory.getLogger(SessionInitialization.class); + private static final int MAX_INIT_TRIES = 2; + private static final long RETRY_DELAY_MILLIS = 1777; + + private final Set remoteHostsInInitState = new HashSet<>(); + private final List locks = new ArrayList<>(100); + + SessionInitialization() { + for (int i = 0; i < 100; i++) { + locks.add(MonitoredReentrantLockFactory.createLock(MonitoredLockType.SERVER_LOGIN_COORD)); + } + } + + private Lock getLock(String remoteHost) { + return locks.get(Math.abs(remoteHost.hashCode()) % 100); + } + + /** + * Try to initialize a session. Should be called before any session initialization procedure. + * + * @return InitializationResult.SUCCESS if initialization was successful. + * If it was successful, finalize() needs to be called shortly after, + * or else the initialization will be left hanging in a bad state, + * which means any subsequent initialization from the same remote host will fail. + */ + public InitializationResult initialize(String remoteHost) { + final Lock lock = getLock(remoteHost); + try { + int tries = 0; + while (true) { + if (lock.tryLock()) { + try { + if (remoteHostsInInitState.contains(remoteHost)) { + return InitializationResult.ALREADY_INITIALIZED; + } + + remoteHostsInInitState.add(remoteHost); + } finally { + lock.unlock(); + } + + break; + } else { + if (tries++ == MAX_INIT_TRIES) { + return InitializationResult.TIMED_OUT; + } + + Thread.sleep(RETRY_DELAY_MILLIS); + } + } + } catch (Exception e) { + log.error("Failed to initialize session.", e); + return InitializationResult.ERROR; + } + + return InitializationResult.SUCCESS; + } + + /** + * Finalize an initialization. Should be called after any session initialization procedure. + */ + public void finalize(String remoteHost) { + final Lock lock = getLock(remoteHost); + lock.lock(); + try { + remoteHostsInInitState.remove(remoteHost); + } finally { + lock.unlock(); + } + } +} From 50e2b909f11382f2aaf1728be451a86eec827747 Mon Sep 17 00:00:00 2001 From: P0nk Date: Mon, 28 Jun 2021 12:29:01 +0200 Subject: [PATCH 16/37] Use Instant instead of long for time measurement --- .../coordinator/login/LoginStorage.java | 68 ++++++++----------- .../session/MapleSessionCoordinator.java | 2 +- 2 files changed, 30 insertions(+), 40 deletions(-) diff --git a/src/main/java/net/server/coordinator/login/LoginStorage.java b/src/main/java/net/server/coordinator/login/LoginStorage.java index c4957f6899..d8e14e49f4 100644 --- a/src/main/java/net/server/coordinator/login/LoginStorage.java +++ b/src/main/java/net/server/coordinator/login/LoginStorage.java @@ -22,70 +22,60 @@ package net.server.coordinator.login; import config.YamlConfig; import net.server.Server; +import java.time.Instant; +import java.util.ArrayList; import java.util.Collections; -import java.util.LinkedList; import java.util.List; import java.util.Map.Entry; import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; /** * * @author Ronan */ public class LoginStorage { - - private ConcurrentHashMap> loginHistory = new ConcurrentHashMap<>(); + private final ConcurrentHashMap> loginHistory = new ConcurrentHashMap<>(); // Key: accountId public boolean registerLogin(int accountId) { - List accHist = loginHistory.get(accountId); - if (accHist == null) { - accHist = new LinkedList<>(); - loginHistory.put(accountId, accHist); - } - - synchronized (accHist) { - if (accHist.size() > YamlConfig.config.server.MAX_ACCOUNT_LOGIN_ATTEMPT) { - long blockExpiration = Server.getInstance().getCurrentTime() + YamlConfig.config.server.LOGIN_ATTEMPT_DURATION; - Collections.fill(accHist, blockExpiration); + List attempts = loginHistory.computeIfAbsent(accountId, k -> new ArrayList<>()); + synchronized (attempts) { + final Instant attemptExpiry = Instant.ofEpochMilli(Server.getInstance().getCurrentTime() + YamlConfig.config.server.LOGIN_ATTEMPT_DURATION); + + if (attempts.size() > YamlConfig.config.server.MAX_ACCOUNT_LOGIN_ATTEMPT) { + Collections.fill(attempts, attemptExpiry); return false; } - accHist.add(Server.getInstance().getCurrentTime() + YamlConfig.config.server.LOGIN_ATTEMPT_DURATION); + attempts.add(attemptExpiry); return true; } } - public void updateLoginHistory() { - long timeNow = Server.getInstance().getCurrentTime(); - List toRemove = new LinkedList<>(); - List toRemoveAttempt = new LinkedList<>(); - - for (Entry> loginEntries : loginHistory.entrySet()) { - toRemoveAttempt.clear(); - - List accAttempts = loginEntries.getValue(); - synchronized (accAttempts) { - for (Long loginAttempt : accAttempts) { - if (loginAttempt < timeNow) { - toRemoveAttempt.add(loginAttempt); - } + public void clearExpiredAttempts() { + final Instant now = Instant.ofEpochMilli(Server.getInstance().getCurrentTime()); + List accountIdsToClear = new ArrayList<>(); + + for (Entry> loginEntries : loginHistory.entrySet()) { + final List attempts = loginEntries.getValue(); + synchronized (attempts) { + List attemptsToRemove = attempts.stream() + .filter(attempt -> attempt.isBefore(now)) + .collect(Collectors.toList()); + + for (Instant attemptToRemove : attemptsToRemove) { + attempts.remove(attemptToRemove); } - if (!toRemoveAttempt.isEmpty()) { - for (Long trAttempt : toRemoveAttempt) { - accAttempts.remove(trAttempt); - } - - if (accAttempts.isEmpty()) { - toRemove.add(loginEntries.getKey()); - } + if (attempts.isEmpty()) { + accountIdsToClear.add(loginEntries.getKey()); } } } - - for (Integer tr : toRemove) { - loginHistory.remove(tr); + + for (Integer accountId : accountIdsToClear) { + loginHistory.remove(accountId); } } } diff --git a/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java b/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java index 07fca506a0..dc1e45ca7b 100644 --- a/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java +++ b/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java @@ -399,7 +399,7 @@ public class MapleSessionCoordinator { } public void runUpdateLoginHistory() { - loginStorage.updateLoginHistory(); + loginStorage.clearExpiredAttempts(); } public void printSessionTrace() { From 671313ab5713d1025e2bb55e1514017632cdbb63 Mon Sep 17 00:00:00 2001 From: P0nk Date: Tue, 29 Jun 2021 08:29:29 +0200 Subject: [PATCH 17/37] Encapsulate hwid validation --- .../java/net/server/coordinator/session/Hwid.java | 11 +++++++++++ .../server/handlers/login/CharSelectedHandler.java | 3 ++- .../handlers/login/CharSelectedWithPicHandler.java | 3 ++- .../net/server/handlers/login/RegisterPicHandler.java | 3 ++- .../handlers/login/ViewAllCharRegisterPicHandler.java | 3 ++- .../handlers/login/ViewAllCharSelectedHandler.java | 3 ++- .../login/ViewAllCharSelectedWithPicHandler.java | 3 ++- 7 files changed, 23 insertions(+), 6 deletions(-) create mode 100644 src/main/java/net/server/coordinator/session/Hwid.java diff --git a/src/main/java/net/server/coordinator/session/Hwid.java b/src/main/java/net/server/coordinator/session/Hwid.java new file mode 100644 index 0000000000..8c8b707ae0 --- /dev/null +++ b/src/main/java/net/server/coordinator/session/Hwid.java @@ -0,0 +1,11 @@ +package net.server.coordinator.session; + +import java.util.regex.Pattern; + +public class Hwid { + private static final Pattern VALID_HWID_PATTERN = Pattern.compile("[0-9A-F]{12}_[0-9A-F]{8}"); + + public static boolean isValidHwid(String hwid) { + return VALID_HWID_PATTERN.matcher(hwid).matches(); + } +} diff --git a/src/main/java/net/server/handlers/login/CharSelectedHandler.java b/src/main/java/net/server/handlers/login/CharSelectedHandler.java index 9dfe1f5dad..f6dea38f99 100644 --- a/src/main/java/net/server/handlers/login/CharSelectedHandler.java +++ b/src/main/java/net/server/handlers/login/CharSelectedHandler.java @@ -26,6 +26,7 @@ import java.net.InetAddress; import java.net.UnknownHostException; import net.AbstractMaplePacketHandler; import net.server.Server; +import net.server.coordinator.session.Hwid; import net.server.coordinator.session.MapleSessionCoordinator; import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientResult; import net.server.world.World; @@ -61,7 +62,7 @@ public final class CharSelectedHandler extends AbstractMaplePacketHandler { String macs = slea.readMapleAsciiString(); String hwid = slea.readMapleAsciiString(); - if (!hwid.matches("[0-9A-F]{12}_[0-9A-F]{8}")) { + if (!Hwid.isValidHwid(hwid)) { c.announce(MaplePacketCreator.getAfterLoginError(17)); return; } diff --git a/src/main/java/net/server/handlers/login/CharSelectedWithPicHandler.java b/src/main/java/net/server/handlers/login/CharSelectedWithPicHandler.java index 7b8005e41a..e6d2269c8f 100644 --- a/src/main/java/net/server/handlers/login/CharSelectedWithPicHandler.java +++ b/src/main/java/net/server/handlers/login/CharSelectedWithPicHandler.java @@ -5,6 +5,7 @@ import java.net.UnknownHostException; import net.AbstractMaplePacketHandler; import net.server.Server; +import net.server.coordinator.session.Hwid; import net.server.coordinator.session.MapleSessionCoordinator; import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientResult; import net.server.world.World; @@ -42,7 +43,7 @@ public class CharSelectedWithPicHandler extends AbstractMaplePacketHandler { String macs = slea.readMapleAsciiString(); String hwid = slea.readMapleAsciiString(); - if (!hwid.matches("[0-9A-F]{12}_[0-9A-F]{8}")) { + if (!Hwid.isValidHwid(hwid)) { c.announce(MaplePacketCreator.getAfterLoginError(17)); return; } diff --git a/src/main/java/net/server/handlers/login/RegisterPicHandler.java b/src/main/java/net/server/handlers/login/RegisterPicHandler.java index f32fac4579..874e7fdd83 100644 --- a/src/main/java/net/server/handlers/login/RegisterPicHandler.java +++ b/src/main/java/net/server/handlers/login/RegisterPicHandler.java @@ -5,6 +5,7 @@ import java.net.UnknownHostException; import net.AbstractMaplePacketHandler; import net.server.Server; +import net.server.coordinator.session.Hwid; import net.server.world.World; import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; @@ -42,7 +43,7 @@ public final class RegisterPicHandler extends AbstractMaplePacketHandler { String macs = slea.readMapleAsciiString(); String hwid = slea.readMapleAsciiString(); - if (!hwid.matches("[0-9A-F]{12}_[0-9A-F]{8}")) { + if (!Hwid.isValidHwid(hwid)) { c.announce(MaplePacketCreator.getAfterLoginError(17)); return; } diff --git a/src/main/java/net/server/handlers/login/ViewAllCharRegisterPicHandler.java b/src/main/java/net/server/handlers/login/ViewAllCharRegisterPicHandler.java index f6fc999fd3..dbcb5cb8ab 100644 --- a/src/main/java/net/server/handlers/login/ViewAllCharRegisterPicHandler.java +++ b/src/main/java/net/server/handlers/login/ViewAllCharRegisterPicHandler.java @@ -5,6 +5,7 @@ import java.net.InetAddress; import java.net.UnknownHostException; import net.AbstractMaplePacketHandler; import net.server.Server; +import net.server.coordinator.session.Hwid; import net.server.coordinator.session.MapleSessionCoordinator; import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientResult; import net.server.world.World; @@ -43,7 +44,7 @@ public final class ViewAllCharRegisterPicHandler extends AbstractMaplePacketHand String mac = slea.readMapleAsciiString(); String hwid = slea.readMapleAsciiString(); - if (!hwid.matches("[0-9A-F]{12}_[0-9A-F]{8}")) { + if (!Hwid.isValidHwid(hwid)) { c.announce(MaplePacketCreator.getAfterLoginError(17)); return; } diff --git a/src/main/java/net/server/handlers/login/ViewAllCharSelectedHandler.java b/src/main/java/net/server/handlers/login/ViewAllCharSelectedHandler.java index 9bda9fc14b..aa4030ab14 100644 --- a/src/main/java/net/server/handlers/login/ViewAllCharSelectedHandler.java +++ b/src/main/java/net/server/handlers/login/ViewAllCharSelectedHandler.java @@ -26,6 +26,7 @@ import java.net.InetAddress; import java.net.UnknownHostException; import net.AbstractMaplePacketHandler; import net.server.Server; +import net.server.coordinator.session.Hwid; import net.server.coordinator.session.MapleSessionCoordinator; import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientResult; import net.server.world.World; @@ -63,7 +64,7 @@ public final class ViewAllCharSelectedHandler extends AbstractMaplePacketHandler String macs = slea.readMapleAsciiString(); String hwid = slea.readMapleAsciiString(); - if (!hwid.matches("[0-9A-F]{12}_[0-9A-F]{8}")) { + if (!Hwid.isValidHwid(hwid)) { c.announce(MaplePacketCreator.getAfterLoginError(17)); return; } diff --git a/src/main/java/net/server/handlers/login/ViewAllCharSelectedWithPicHandler.java b/src/main/java/net/server/handlers/login/ViewAllCharSelectedWithPicHandler.java index 019ff9f8d5..dff8b064d7 100644 --- a/src/main/java/net/server/handlers/login/ViewAllCharSelectedWithPicHandler.java +++ b/src/main/java/net/server/handlers/login/ViewAllCharSelectedWithPicHandler.java @@ -5,6 +5,7 @@ import java.net.UnknownHostException; import net.AbstractMaplePacketHandler; import net.server.Server; +import net.server.coordinator.session.Hwid; import net.server.world.World; import tools.MaplePacketCreator; import tools.Randomizer; @@ -45,7 +46,7 @@ public class ViewAllCharSelectedWithPicHandler extends AbstractMaplePacketHandle String macs = slea.readMapleAsciiString(); String hwid = slea.readMapleAsciiString(); - if (!hwid.matches("[0-9A-F]{12}_[0-9A-F]{8}")) { + if (!Hwid.isValidHwid(hwid)) { c.announce(MaplePacketCreator.getAfterLoginError(17)); return; } From d34798649b3267cbcb2b35a8c8785202aa3d6b14 Mon Sep 17 00:00:00 2001 From: P0nk Date: Tue, 29 Jun 2021 08:35:21 +0200 Subject: [PATCH 18/37] Add dedicated host hwid cache, further refactor session coordinator --- .../server/coordinator/session/HostHwid.java | 16 ++ .../coordinator/session/HostHwidCache.java | 48 +++++ .../coordinator/session/HwidRelevance.java | 3 + .../session/MapleSessionCoordinator.java | 165 +++++++----------- .../net/server/task/LoginCoordinatorTask.java | 2 +- 5 files changed, 134 insertions(+), 100 deletions(-) create mode 100644 src/main/java/net/server/coordinator/session/HostHwid.java create mode 100644 src/main/java/net/server/coordinator/session/HostHwidCache.java diff --git a/src/main/java/net/server/coordinator/session/HostHwid.java b/src/main/java/net/server/coordinator/session/HostHwid.java new file mode 100644 index 0000000000..b6c7a6590e --- /dev/null +++ b/src/main/java/net/server/coordinator/session/HostHwid.java @@ -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)); + } +} diff --git a/src/main/java/net/server/coordinator/session/HostHwidCache.java b/src/main/java/net/server/coordinator/session/HostHwidCache.java new file mode 100644 index 0000000000..7f75ae854f --- /dev/null +++ b/src/main/java/net/server/coordinator/session/HostHwidCache.java @@ -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 hostHwidCache = new ConcurrentHashMap<>(); // Key: remoteHost + + void clearExpired() { + SessionDAO.deleteExpiredHwidAccounts(); + + Instant now = Instant.ofEpochMilli(Server.getInstance().getCurrentTime()); + List remoteHostsToRemove = new ArrayList<>(); + for (Map.Entry 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(); + } + +} diff --git a/src/main/java/net/server/coordinator/session/HwidRelevance.java b/src/main/java/net/server/coordinator/session/HwidRelevance.java index bfd1e47219..6dbbf6f838 100644 --- a/src/main/java/net/server/coordinator/session/HwidRelevance.java +++ b/src/main/java/net/server/coordinator/session/HwidRelevance.java @@ -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; + } } diff --git a/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java b/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java index dc1e45ca7b..46d746339f 100644 --- a/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java +++ b/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java @@ -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 onlineClients = new HashMap<>(); - private final Set onlineRemoteHwids = new HashSet<>(); - private final Map> loginRemoteHosts = new HashMap<>(); - private final Set pooledRemoteHosts = new HashSet<>(); - - private final ConcurrentHashMap cachedHostHwids = new ConcurrentHashMap<>(); - private final ConcurrentHashMap cachedHostTimeout = new ConcurrentHashMap<>(); + private final Map onlineClients = new HashMap<>(); // Key: account id + private final Set onlineRemoteHwids = new HashSet<>(); // Hwid/nibblehwid + private final Map> 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 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 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 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 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 toRemove = new LinkedList<>(); - for (Entry 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() { diff --git a/src/main/java/net/server/task/LoginCoordinatorTask.java b/src/main/java/net/server/task/LoginCoordinatorTask.java index bf9109a40b..6efe23b2dd 100644 --- a/src/main/java/net/server/task/LoginCoordinatorTask.java +++ b/src/main/java/net/server/task/LoginCoordinatorTask.java @@ -29,6 +29,6 @@ public class LoginCoordinatorTask implements Runnable { @Override public void run() { - MapleSessionCoordinator.getInstance().runUpdateHwidHistory(); + MapleSessionCoordinator.getInstance().clearExpiredHwidHistory(); } } From 828c3c5345b26036a82a0c71fdef704e7cd7ea6e Mon Sep 17 00:00:00 2001 From: P0nk Date: Tue, 29 Jun 2021 20:26:14 +0200 Subject: [PATCH 19/37] Add Hwid class for "nibble hwid" part of "remote host" --- src/main/java/client/MapleClient.java | 63 +++++++++---------- .../handlers/PlayerLoggedinHandler.java | 1 + .../net/server/coordinator/session/Hwid.java | 28 +++++++-- .../session/MapleSessionCoordinator.java | 7 ++- .../handlers/login/CharSelectedHandler.java | 8 +-- .../login/CharSelectedWithPicHandler.java | 4 +- .../handlers/login/RegisterPicHandler.java | 4 +- .../login/ViewAllCharRegisterPicHandler.java | 4 +- .../login/ViewAllCharSelectedHandler.java | 4 +- .../ViewAllCharSelectedWithPicHandler.java | 4 +- 10 files changed, 73 insertions(+), 54 deletions(-) diff --git a/src/main/java/client/MapleClient.java b/src/main/java/client/MapleClient.java index e7a1406454..8a7adec9f0 100644 --- a/src/main/java/client/MapleClient.java +++ b/src/main/java/client/MapleClient.java @@ -89,6 +89,7 @@ public class MapleClient extends ChannelInboundHandlerAdapter { public static final String CLIENT_NIBBLEHWID = "HWID2"; public static final String CLIENT_REMOTE_ADDRESS = "REMOTE_IP"; + private Hwid hwid; private String remoteAddress; private volatile boolean inTransition; @@ -116,7 +117,6 @@ public class MapleClient extends ChannelInboundHandlerAdapter { private int pinattempt = 0; private String pic = ""; private int picattempt = 0; - private String hwid = null; private byte csattempt = 0; private byte gender = -1; private boolean disconnecting = false; @@ -260,6 +260,14 @@ public class MapleClient extends ChannelInboundHandlerAdapter { return session; } + public Hwid getHwid() { + return hwid; + } + + public void setHwid(Hwid hwid) { + this.hwid = hwid; + } + public String getRemoteAddress() { return remoteAddress; } @@ -387,7 +395,7 @@ public class MapleClient extends ChannelInboundHandlerAdapter { boolean ret = false; try (Connection con = DatabaseConnection.getConnection(); PreparedStatement ps = con.prepareStatement("SELECT COUNT(*) FROM hwidbans WHERE hwid LIKE ?")) { - ps.setString(1, hwid); + ps.setString(1, hwid.hwid()); try (ResultSet rs = ps.executeQuery()) { if (rs != null && rs.next()) { @@ -445,7 +453,7 @@ public class MapleClient extends ChannelInboundHandlerAdapter { try (ResultSet rs = ps.executeQuery()) { if (rs.next()) { - hwid = rs.getString("hwid"); + hwid = new Hwid(rs.getString("hwid")); } } } @@ -477,7 +485,7 @@ public class MapleClient extends ChannelInboundHandlerAdapter { try (Connection con = DatabaseConnection.getConnection(); PreparedStatement ps = con.prepareStatement("INSERT INTO hwidbans (hwid) VALUES (?)")) { - ps.setString(1, hwid); + ps.setString(1, hwid.hwid()); ps.executeUpdate(); } } catch (SQLException e) { @@ -739,30 +747,25 @@ public class MapleClient extends ChannelInboundHandlerAdapter { return ipAddress; } - public void updateHWID(String newHwid) { - String[] split = newHwid.split("_"); - if (split.length > 1 && split[1].length() == 8) { - StringBuilder hwid = new StringBuilder(); - String convert = split[1]; + public void updateHwid(String hwidClientString) { + final Hwid hwid; + try { + hwid = Hwid.fromClientString(hwidClientString); + } catch (IllegalArgumentException e) { + log.warn("Failed to create hwid from client string: {}", hwidClientString, e); + this.disconnect(false, false); + return; + } - int len = convert.length(); - for (int i = len - 2; i >= 0; i -= 2) { - hwid.append(convert, i, i + 2); - } - hwid.insert(4, "-"); + this.hwid = hwid; - this.hwid = hwid.toString(); - - try (Connection con = DatabaseConnection.getConnection(); - PreparedStatement ps = con.prepareStatement("UPDATE accounts SET hwid = ? WHERE id = ?")) { - ps.setString(1, this.hwid); - ps.setInt(2, accId); - ps.executeUpdate(); - } catch (SQLException e) { - e.printStackTrace(); - } - } else { - this.disconnect(false, false); // Invalid HWID... + try (Connection con = DatabaseConnection.getConnection(); + PreparedStatement ps = con.prepareStatement("UPDATE accounts SET hwid = ? WHERE id = ?")) { + ps.setString(1, hwid.hwid()); + ps.setInt(2, accId); + ps.executeUpdate(); + } catch (SQLException e) { + e.printStackTrace(); } } @@ -1173,14 +1176,6 @@ public class MapleClient extends ChannelInboundHandlerAdapter { }, TimeUnit.SECONDS.toMillis(15)); } - public String getHWID() { - return hwid; - } - - public void setHWID(String hwid) { - this.hwid = hwid; - } - public Set getMacs() { return Collections.unmodifiableSet(macs); } diff --git a/src/main/java/net/server/channel/handlers/PlayerLoggedinHandler.java b/src/main/java/net/server/channel/handlers/PlayerLoggedinHandler.java index 75c2d12958..6f0e350e74 100644 --- a/src/main/java/net/server/channel/handlers/PlayerLoggedinHandler.java +++ b/src/main/java/net/server/channel/handlers/PlayerLoggedinHandler.java @@ -31,6 +31,7 @@ import net.server.PlayerBuffValueHolder; import net.server.Server; import net.server.channel.Channel; import net.server.channel.CharacterIdChannelPair; +import net.server.coordinator.session.Hwid; import net.server.coordinator.session.MapleSessionCoordinator; import net.server.coordinator.world.MapleEventRecallCoordinator; import net.server.guild.MapleAlliance; diff --git a/src/main/java/net/server/coordinator/session/Hwid.java b/src/main/java/net/server/coordinator/session/Hwid.java index 8c8b707ae0..fdaed64b04 100644 --- a/src/main/java/net/server/coordinator/session/Hwid.java +++ b/src/main/java/net/server/coordinator/session/Hwid.java @@ -2,10 +2,30 @@ package net.server.coordinator.session; import java.util.regex.Pattern; -public class Hwid { - private static final Pattern VALID_HWID_PATTERN = Pattern.compile("[0-9A-F]{12}_[0-9A-F]{8}"); +public record Hwid (String hwid) { + private static final int HWID_LENGTH = 8; + // First part is a mac address (without dashes), second part is the hwid + private static final Pattern VALID_RAW_HWID_PATTERN = Pattern.compile("[0-9A-F]{12}_[0-9A-F]{8}"); - public static boolean isValidHwid(String hwid) { - return VALID_HWID_PATTERN.matcher(hwid).matches(); + public static boolean isValidRawHwid(String rawHwid) { + return VALID_RAW_HWID_PATTERN.matcher(rawHwid).matches(); + } + + public static Hwid fromClientString(String clientString) throws IllegalArgumentException { + String[] split = clientString.split("_"); + if (split.length != 2 || split[1].length() != HWID_LENGTH) { + throw new IllegalArgumentException("Hwid validation failed for hwid: " + clientString); + } + + StringBuilder newHwid = new StringBuilder(); + String convert = split[1]; + + int len = convert.length(); + for (int i = len - 2; i >= 0; i -= 2) { + newHwid.append(convert, i, i + 2); + } + newHwid.insert(4, "-"); + + return new Hwid(newHwid.toString()); } } diff --git a/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java b/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java index 46d746339f..25fe1253d5 100644 --- a/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java +++ b/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java @@ -297,8 +297,9 @@ public class MapleSessionCoordinator { return null; } - int hwidLen = remoteHwid.length(); - if (hwidLen <= 8) { + final int hwidLen = remoteHwid.length(); + final boolean isOnlyNibbleHwid = hwidLen <= 8; + if (isOnlyNibbleHwid) { session.setAttribute(MapleClient.CLIENT_NIBBLEHWID, remoteHwid); } else { session.setAttribute(MapleClient.CLIENT_HWID, remoteHwid); @@ -326,9 +327,11 @@ public class MapleSessionCoordinator { } String hwid = (String) session.removeAttribute(MapleClient.CLIENT_NIBBLEHWID); // making sure to clean up calls to this function on login phase + // TODO: client.setNibbleHwid(null); onlineRemoteHwids.remove(hwid); hwid = (String) session.removeAttribute(MapleClient.CLIENT_HWID); + // TODO: client.setHwid(null); onlineRemoteHwids.remove(hwid); if (client != null) { diff --git a/src/main/java/net/server/handlers/login/CharSelectedHandler.java b/src/main/java/net/server/handlers/login/CharSelectedHandler.java index f6dea38f99..9c927c3d1a 100644 --- a/src/main/java/net/server/handlers/login/CharSelectedHandler.java +++ b/src/main/java/net/server/handlers/login/CharSelectedHandler.java @@ -46,10 +46,10 @@ public final class CharSelectedHandler extends AbstractMaplePacketHandler { case REMOTE_NO_MATCH: return 17; - + case COORDINATOR_ERROR: return 8; - + default: return 9; } @@ -62,13 +62,13 @@ public final class CharSelectedHandler extends AbstractMaplePacketHandler { String macs = slea.readMapleAsciiString(); String hwid = slea.readMapleAsciiString(); - if (!Hwid.isValidHwid(hwid)) { + if (!Hwid.isValidRawHwid(hwid)) { c.announce(MaplePacketCreator.getAfterLoginError(17)); return; } c.updateMacs(macs); - c.updateHWID(hwid); + c.updateHwid(hwid); IoSession session = c.getSession(); AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptGameSession(session, c.getAccID(), hwid); diff --git a/src/main/java/net/server/handlers/login/CharSelectedWithPicHandler.java b/src/main/java/net/server/handlers/login/CharSelectedWithPicHandler.java index e6d2269c8f..ec7d9cbff3 100644 --- a/src/main/java/net/server/handlers/login/CharSelectedWithPicHandler.java +++ b/src/main/java/net/server/handlers/login/CharSelectedWithPicHandler.java @@ -43,13 +43,13 @@ public class CharSelectedWithPicHandler extends AbstractMaplePacketHandler { String macs = slea.readMapleAsciiString(); String hwid = slea.readMapleAsciiString(); - if (!Hwid.isValidHwid(hwid)) { + if (!Hwid.isValidRawHwid(hwid)) { c.announce(MaplePacketCreator.getAfterLoginError(17)); return; } c.updateMacs(macs); - c.updateHWID(hwid); + c.updateHwid(hwid); IoSession session = c.getSession(); diff --git a/src/main/java/net/server/handlers/login/RegisterPicHandler.java b/src/main/java/net/server/handlers/login/RegisterPicHandler.java index 874e7fdd83..161837f32c 100644 --- a/src/main/java/net/server/handlers/login/RegisterPicHandler.java +++ b/src/main/java/net/server/handlers/login/RegisterPicHandler.java @@ -43,13 +43,13 @@ public final class RegisterPicHandler extends AbstractMaplePacketHandler { String macs = slea.readMapleAsciiString(); String hwid = slea.readMapleAsciiString(); - if (!Hwid.isValidHwid(hwid)) { + if (!Hwid.isValidRawHwid(hwid)) { c.announce(MaplePacketCreator.getAfterLoginError(17)); return; } c.updateMacs(macs); - c.updateHWID(hwid); + c.updateHwid(hwid); IoSession session = c.getSession(); AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptGameSession(session, c.getAccID(), hwid); diff --git a/src/main/java/net/server/handlers/login/ViewAllCharRegisterPicHandler.java b/src/main/java/net/server/handlers/login/ViewAllCharRegisterPicHandler.java index dbcb5cb8ab..88b4a76698 100644 --- a/src/main/java/net/server/handlers/login/ViewAllCharRegisterPicHandler.java +++ b/src/main/java/net/server/handlers/login/ViewAllCharRegisterPicHandler.java @@ -44,13 +44,13 @@ public final class ViewAllCharRegisterPicHandler extends AbstractMaplePacketHand String mac = slea.readMapleAsciiString(); String hwid = slea.readMapleAsciiString(); - if (!Hwid.isValidHwid(hwid)) { + if (!Hwid.isValidRawHwid(hwid)) { c.announce(MaplePacketCreator.getAfterLoginError(17)); return; } c.updateMacs(mac); - c.updateHWID(hwid); + c.updateHwid(hwid); if (c.hasBannedMac() || c.hasBannedHWID()) { MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true); diff --git a/src/main/java/net/server/handlers/login/ViewAllCharSelectedHandler.java b/src/main/java/net/server/handlers/login/ViewAllCharSelectedHandler.java index aa4030ab14..655db30415 100644 --- a/src/main/java/net/server/handlers/login/ViewAllCharSelectedHandler.java +++ b/src/main/java/net/server/handlers/login/ViewAllCharSelectedHandler.java @@ -64,13 +64,13 @@ public final class ViewAllCharSelectedHandler extends AbstractMaplePacketHandler String macs = slea.readMapleAsciiString(); String hwid = slea.readMapleAsciiString(); - if (!Hwid.isValidHwid(hwid)) { + if (!Hwid.isValidRawHwid(hwid)) { c.announce(MaplePacketCreator.getAfterLoginError(17)); return; } c.updateMacs(macs); - c.updateHWID(hwid); + c.updateHwid(hwid); if (c.hasBannedMac() || c.hasBannedHWID()) { MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true); diff --git a/src/main/java/net/server/handlers/login/ViewAllCharSelectedWithPicHandler.java b/src/main/java/net/server/handlers/login/ViewAllCharSelectedWithPicHandler.java index dff8b064d7..95e878b811 100644 --- a/src/main/java/net/server/handlers/login/ViewAllCharSelectedWithPicHandler.java +++ b/src/main/java/net/server/handlers/login/ViewAllCharSelectedWithPicHandler.java @@ -46,13 +46,13 @@ public class ViewAllCharSelectedWithPicHandler extends AbstractMaplePacketHandle String macs = slea.readMapleAsciiString(); String hwid = slea.readMapleAsciiString(); - if (!Hwid.isValidHwid(hwid)) { + if (!Hwid.isValidRawHwid(hwid)) { c.announce(MaplePacketCreator.getAfterLoginError(17)); return; } c.updateMacs(macs); - c.updateHWID(hwid); + c.updateHwid(hwid); if (c.hasBannedMac() || c.hasBannedHWID()) { MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true); From 0e98abff41203a1e9845ef32c0bbf34cf015255c Mon Sep 17 00:00:00 2001 From: P0nk Date: Tue, 29 Jun 2021 20:27:32 +0200 Subject: [PATCH 20/37] Minor renaming and refactoring --- src/main/java/client/MapleClient.java | 36 +- src/main/java/net/PacketProcessor.java | 2 +- src/main/java/net/netty/LoginServer.java | 4 +- .../net/netty/ServerChannelInitializer.java | 9 + .../handlers/PlayerLoggedinHandler.java | 584 +++++++++--------- 5 files changed, 329 insertions(+), 306 deletions(-) diff --git a/src/main/java/client/MapleClient.java b/src/main/java/client/MapleClient.java index 8a7adec9f0..9cf408b875 100644 --- a/src/main/java/client/MapleClient.java +++ b/src/main/java/client/MapleClient.java @@ -38,6 +38,8 @@ import net.server.audit.locks.MonitoredLockType; import net.server.audit.locks.factory.MonitoredReentrantLockFactory; import net.server.channel.Channel; import net.server.coordinator.login.MapleLoginBypassCoordinator; +import net.server.coordinator.session.Hwid; +import net.server.coordinator.session.IpAddresses; import net.server.coordinator.session.MapleSessionCoordinator; import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientResult; import net.server.guild.MapleGuild; @@ -157,24 +159,32 @@ public class MapleClient extends ChannelInboundHandlerAdapter { @Override public void channelActive(ChannelHandlerContext ctx) { + final io.netty.channel.Channel channel = ctx.channel(); if (!Server.getInstance().isOnline()) { - ctx.channel().close(); + channel.close(); return; } - String hostAddress = "null"; + this.remoteAddress = getRemoteAddress(channel); + this.ioChannel = channel; + } + + private static String getRemoteAddress(io.netty.channel.Channel channel) { + String remoteAddress = "null"; try { - hostAddress = ((InetSocketAddress) ctx.channel().remoteAddress()).getAddress().getHostAddress(); + String hostAddress = ((InetSocketAddress) channel.remoteAddress()).getAddress().getHostAddress(); + if (hostAddress != null) { + remoteAddress = IpAddresses.evaluateRemoteAddress(hostAddress); // thanks dyz for noticing Local/LAN/WAN connections not interacting properly + } } catch (NullPointerException npe) { log.warn("Unable to get remote address for client", npe); } - this.remoteAddress = hostAddress; - this.ioChannel = ctx.channel(); + + return remoteAddress; } @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { - // InPacket packet = new ByteBufInPacket((ByteBuf) msg); if (!(msg instanceof InPacket packet)) { log.warn("Received invalid message: {}", msg); return; @@ -799,17 +809,17 @@ public class MapleClient extends ChannelInboundHandlerAdapter { return accId; } - public void updateLoginState(int newstate) { + public void updateLoginState(int newState) { // rules out possibility of multiple account entries - if (newstate == LOGIN_LOGGEDIN) { - MapleSessionCoordinator.getInstance().updateOnlineSession(this.getSession()); + if (newState == LOGIN_LOGGEDIN) { + MapleSessionCoordinator.getInstance().updateOnlineClient(session); } try (Connection con = DatabaseConnection.getConnection(); PreparedStatement ps = con.prepareStatement("UPDATE accounts SET loggedin = ?, lastlogin = ? WHERE id = ?")) { // using sql currenttime here could potentially break the login, thanks Arnah for pointing this out - ps.setInt(1, newstate); + ps.setInt(1, newState); ps.setTimestamp(2, new java.sql.Timestamp(Server.getInstance().getCurrentTime())); ps.setInt(3, getAccID()); ps.executeUpdate(); @@ -817,12 +827,12 @@ public class MapleClient extends ChannelInboundHandlerAdapter { e.printStackTrace(); } - if (newstate == LOGIN_NOTLOGGEDIN) { + if (newState == LOGIN_NOTLOGGEDIN) { loggedIn = false; serverTransition = false; setAccID(0); } else { - serverTransition = (newstate == LOGIN_SERVER_TRANSITION); + serverTransition = (newState == LOGIN_SERVER_TRANSITION); loggedIn = !serverTransition; } } @@ -1165,7 +1175,7 @@ public class MapleClient extends ChannelInboundHandlerAdapter { try { if (lastPong < pingedAt) { if (ioChannel.isActive()) { - log.info("Disconnected {} due to being idle. Cause: {}", remoteAddress, event.state()); + log.info("Disconnected {} due to idling. Reason: {}", remoteAddress, event.state()); updateLoginState(MapleClient.LOGIN_NOTLOGGEDIN); disconnectSession(); } diff --git a/src/main/java/net/PacketProcessor.java b/src/main/java/net/PacketProcessor.java index d7b45602bd..487a5c2985 100644 --- a/src/main/java/net/PacketProcessor.java +++ b/src/main/java/net/PacketProcessor.java @@ -48,7 +48,7 @@ public final class PacketProcessor { } public static PacketProcessor getLoginServerProcessor() { - return getProcessor(LoginServer.WORLD, LoginServer.CHANNEL); + return getProcessor(LoginServer.WORLD_ID, LoginServer.CHANNEL_ID); } public static PacketProcessor getChannelServerProcessor(int world, int channel) { diff --git a/src/main/java/net/netty/LoginServer.java b/src/main/java/net/netty/LoginServer.java index 6f4c7196c4..a34145f3f2 100644 --- a/src/main/java/net/netty/LoginServer.java +++ b/src/main/java/net/netty/LoginServer.java @@ -7,8 +7,8 @@ import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.socket.nio.NioServerSocketChannel; public class LoginServer extends AbstractServer { - public static final int WORLD = -1; - public static final int CHANNEL = -1; + public static final int WORLD_ID = -1; + public static final int CHANNEL_ID = -1; private Channel channel; public LoginServer(int port) { diff --git a/src/main/java/net/netty/ServerChannelInitializer.java b/src/main/java/net/netty/ServerChannelInitializer.java index 927e20cb05..868250151d 100644 --- a/src/main/java/net/netty/ServerChannelInitializer.java +++ b/src/main/java/net/netty/ServerChannelInitializer.java @@ -16,7 +16,16 @@ public abstract class ServerChannelInitializer extends ChannelInitializer attemptingLoginAccounts = new HashSet<>(); + private static final Set attemptingLoginAccounts = new HashSet<>(); private boolean tryAcquireAccount(int accId) { synchronized (attemptingLoginAccounts) { @@ -85,70 +85,76 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler { @Override public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { - final int cid = slea.readInt(); + final int cid = slea.readInt(); // TODO: investigate if this is the "client id" supplied in MaplePacketCreator#getServerIP() final Server server = Server.getInstance(); - if (c.tryacquireClient()) { // thanks MedicOP for assisting on concurrency protection here - try { - World wserv = server.getWorld(c.getWorld()); - if(wserv == null) { + if (!c.tryacquireClient()) { + // thanks MedicOP for assisting on concurrency protection here + c.announce(MaplePacketCreator.getAfterLoginError(10)); + } + + try { + World wserv = server.getWorld(c.getWorld()); + if (wserv == null) { + c.disconnect(true, false); + return; + } + + Channel cserv = wserv.getChannel(c.getChannel()); + if (cserv == null) { + c.setChannel(1); + cserv = wserv.getChannel(c.getChannel()); + + if (cserv == null) { c.disconnect(true, false); return; } + } - Channel cserv = wserv.getChannel(c.getChannel()); - if(cserv == null) { - c.setChannel(1); - cserv = wserv.getChannel(c.getChannel()); + MapleCharacter player = wserv.getPlayerStorage().getCharacterById(cid); + IoSession session = c.getSession(); - if(cserv == null) { - c.disconnect(true, false); - return; - } - } - - MapleCharacter player = wserv.getPlayerStorage().getCharacterById(cid); - IoSession session = c.getSession(); - - String remoteHwid; - if (player == null) { - remoteHwid = MapleSessionCoordinator.getInstance().pickLoginSessionHwid(session); - if (remoteHwid == null) { - c.disconnect(true, false); - return; - } - } else { - remoteHwid = player.getClient().getHWID(); - } - - int hwidLen = remoteHwid.length(); - session.setAttribute(MapleClient.CLIENT_HWID, remoteHwid); - session.setAttribute(MapleClient.CLIENT_NIBBLEHWID, remoteHwid.substring(hwidLen - 8, hwidLen)); - c.setHWID(remoteHwid); - - if (!server.validateCharacteridInTransition(c, cid)) { + String remoteHwid; + if (player == null) { + remoteHwid = MapleSessionCoordinator.getInstance().pickLoginSessionHwid(session); + if (remoteHwid == null) { c.disconnect(true, false); return; } - - boolean newcomer = false; - if (player == null) { - try { - player = MapleCharacter.loadCharFromDB(cid, c, true); - newcomer = true; - } catch (SQLException e) { - e.printStackTrace(); - } - - if (player == null) { //If you are still getting null here then please just uninstall the game >.>, we dont need you fucking with the logs - c.disconnect(true, false); - return; - } + } else { + Hwid clientHwid = player.getClient().getHwid(); + remoteHwid = clientHwid == null ? null : clientHwid.hwid(); + } + + int hwidLen = remoteHwid.length(); + session.setAttribute(MapleClient.CLIENT_HWID, remoteHwid); + String nibbleHwid = remoteHwid.substring(hwidLen - 8, hwidLen); + session.setAttribute(MapleClient.CLIENT_NIBBLEHWID, nibbleHwid); + c.setHwid(new Hwid(remoteHwid)); + + if (!server.validateCharacteridInTransition(c, cid)) { + c.disconnect(true, false); + return; + } + + boolean newcomer = false; + if (player == null) { + try { + player = MapleCharacter.loadCharFromDB(cid, c, true); + newcomer = true; + } catch (SQLException e) { + e.printStackTrace(); } - c.setPlayer(player); - c.setAccID(player.getAccountID()); - - boolean allowLogin = true; + + if (player == null) { //If you are still getting null here then please just uninstall the game >.>, we dont need you fucking with the logs + c.disconnect(true, false); + return; + } + } + c.setPlayer(player); + c.setAccID(player.getAccountID()); + + boolean allowLogin = true; /* is this check really necessary? if (state == MapleClient.LOGIN_SERVER_TRANSITION || state == MapleClient.LOGIN_NOTLOGGEDIN) { @@ -165,276 +171,274 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler { } } */ - - int accId = c.getAccID(); - if (tryAcquireAccount(accId)) { // Sync this to prevent wrong login state for double loggedin handling - try { - int state = c.getLoginState(); - if (state != MapleClient.LOGIN_SERVER_TRANSITION || !allowLogin) { - c.setPlayer(null); - c.setAccID(0); - if (state == MapleClient.LOGIN_LOGGEDIN) { - c.disconnect(true, false); - } else { - c.announce(MaplePacketCreator.getAfterLoginError(7)); - } + int accId = c.getAccID(); + if (tryAcquireAccount(accId)) { // Sync this to prevent wrong login state for double loggedin handling + try { + int state = c.getLoginState(); + if (state != MapleClient.LOGIN_SERVER_TRANSITION || !allowLogin) { + c.setPlayer(null); + c.setAccID(0); - return; - } - c.updateLoginState(MapleClient.LOGIN_LOGGEDIN); - } finally { - releaseAccount(accId); - } - } else { - c.setPlayer(null); - c.setAccID(0); - c.announce(MaplePacketCreator.getAfterLoginError(10)); - return; - } - - if (!newcomer) { - c.setLanguage(player.getClient().getLanguage()); - c.setCharacterSlots((byte) player.getClient().getCharacterSlots()); - player.newClient(c); - } - - cserv.addPlayer(player); - wserv.addPlayer(player); - player.setEnteredChannelWorld(); - - List buffs = server.getPlayerBuffStorage().getBuffsFromStorage(cid); - if (buffs != null) { - List> timedBuffs = getLocalStartTimes(buffs); - player.silentGiveBuffs(timedBuffs); - } - - Map> diseases = server.getPlayerBuffStorage().getDiseasesFromStorage(cid); - if (diseases != null) { - player.silentApplyDiseases(diseases); - } - - c.announce(MaplePacketCreator.getCharInfo(player)); - if (!player.isHidden()) { - if(player.isGM() && YamlConfig.config.server.USE_AUTOHIDE_GM) { - player.toggleHide(true); - } - } - player.sendKeymap(); - player.sendQuickmap(); - player.sendMacros(); - - // pot bindings being passed through other characters on the account detected thanks to Croosade dev team - MapleKeyBinding autohpPot = player.getKeymap().get(91); - player.announce(MaplePacketCreator.sendAutoHpPot(autohpPot != null ? autohpPot.getAction() : 0)); - - MapleKeyBinding autompPot = player.getKeymap().get(92); - player.announce(MaplePacketCreator.sendAutoMpPot(autompPot != null ? autompPot.getAction() : 0)); - - player.getMap().addPlayer(player); - player.visitMap(player.getMap()); - - BuddyList bl = player.getBuddylist(); - int[] buddyIds = bl.getBuddyIds(); - wserv.loggedOn(player.getName(), player.getId(), c.getChannel(), buddyIds); - for (CharacterIdChannelPair onlineBuddy : wserv.multiBuddyFind(player.getId(), buddyIds)) { - BuddylistEntry ble = bl.get(onlineBuddy.getCharacterId()); - ble.setChannel(onlineBuddy.getChannel()); - bl.put(ble); - } - c.announce(MaplePacketCreator.updateBuddylist(bl.getBuddies())); - - c.announce(MaplePacketCreator.loadFamily(player)); - if (player.getFamilyId() > 0) { - MapleFamily f = wserv.getFamily(player.getFamilyId()); - if(f != null) { - MapleFamilyEntry familyEntry = f.getEntryByID(player.getId()); - if(familyEntry != null) { - familyEntry.setCharacter(player); - player.setFamilyEntry(familyEntry); - - c.announce(MaplePacketCreator.getFamilyInfo(familyEntry)); - familyEntry.announceToSenior(MaplePacketCreator.sendFamilyLoginNotice(player.getName(), true), true); + if (state == MapleClient.LOGIN_LOGGEDIN) { + c.disconnect(true, false); } else { - FilePrinter.printError(FilePrinter.FAMILY_ERROR, "Player " + player.getName() + "'s family doesn't have an entry for them. (" + f.getID() + ")"); + c.announce(MaplePacketCreator.getAfterLoginError(7)); } + + return; + } + c.updateLoginState(MapleClient.LOGIN_LOGGEDIN); + } finally { + releaseAccount(accId); + } + } else { + c.setPlayer(null); + c.setAccID(0); + c.announce(MaplePacketCreator.getAfterLoginError(10)); + return; + } + + if (!newcomer) { + c.setLanguage(player.getClient().getLanguage()); + c.setCharacterSlots((byte) player.getClient().getCharacterSlots()); + player.newClient(c); + } + + cserv.addPlayer(player); + wserv.addPlayer(player); + player.setEnteredChannelWorld(); + + List buffs = server.getPlayerBuffStorage().getBuffsFromStorage(cid); + if (buffs != null) { + List> timedBuffs = getLocalStartTimes(buffs); + player.silentGiveBuffs(timedBuffs); + } + + Map> diseases = server.getPlayerBuffStorage().getDiseasesFromStorage(cid); + if (diseases != null) { + player.silentApplyDiseases(diseases); + } + + c.announce(MaplePacketCreator.getCharInfo(player)); + if (!player.isHidden()) { + if (player.isGM() && YamlConfig.config.server.USE_AUTOHIDE_GM) { + player.toggleHide(true); + } + } + player.sendKeymap(); + player.sendQuickmap(); + player.sendMacros(); + + // pot bindings being passed through other characters on the account detected thanks to Croosade dev team + MapleKeyBinding autohpPot = player.getKeymap().get(91); + player.announce(MaplePacketCreator.sendAutoHpPot(autohpPot != null ? autohpPot.getAction() : 0)); + + MapleKeyBinding autompPot = player.getKeymap().get(92); + player.announce(MaplePacketCreator.sendAutoMpPot(autompPot != null ? autompPot.getAction() : 0)); + + player.getMap().addPlayer(player); + player.visitMap(player.getMap()); + + BuddyList bl = player.getBuddylist(); + int[] buddyIds = bl.getBuddyIds(); + wserv.loggedOn(player.getName(), player.getId(), c.getChannel(), buddyIds); + for (CharacterIdChannelPair onlineBuddy : wserv.multiBuddyFind(player.getId(), buddyIds)) { + BuddylistEntry ble = bl.get(onlineBuddy.getCharacterId()); + ble.setChannel(onlineBuddy.getChannel()); + bl.put(ble); + } + c.announce(MaplePacketCreator.updateBuddylist(bl.getBuddies())); + + c.announce(MaplePacketCreator.loadFamily(player)); + if (player.getFamilyId() > 0) { + MapleFamily f = wserv.getFamily(player.getFamilyId()); + if (f != null) { + MapleFamilyEntry familyEntry = f.getEntryByID(player.getId()); + if (familyEntry != null) { + familyEntry.setCharacter(player); + player.setFamilyEntry(familyEntry); + + c.announce(MaplePacketCreator.getFamilyInfo(familyEntry)); + familyEntry.announceToSenior(MaplePacketCreator.sendFamilyLoginNotice(player.getName(), true), true); } else { - FilePrinter.printError(FilePrinter.FAMILY_ERROR, "Player " + player.getName() + " has an invalid family ID. (" + player.getFamilyId() + ")"); - c.announce(MaplePacketCreator.getFamilyInfo(null)); + FilePrinter.printError(FilePrinter.FAMILY_ERROR, "Player " + player.getName() + "'s family doesn't have an entry for them. (" + f.getID() + ")"); } } else { + FilePrinter.printError(FilePrinter.FAMILY_ERROR, "Player " + player.getName() + " has an invalid family ID. (" + player.getFamilyId() + ")"); c.announce(MaplePacketCreator.getFamilyInfo(null)); } - if (player.getGuildId() > 0) { - MapleGuild playerGuild = server.getGuild(player.getGuildId(), player.getWorld(), player); - if (playerGuild == null) { - player.deleteGuild(player.getGuildId()); - player.getMGC().setGuildId(0); - player.getMGC().setGuildRank(5); - } else { - playerGuild.getMGC(player.getId()).setCharacter(player); - player.setMGC(playerGuild.getMGC(player.getId())); - server.setGuildMemberOnline(player, true, c.getChannel()); - c.announce(MaplePacketCreator.showGuildInfo(player)); - int allianceId = player.getGuild().getAllianceId(); - if (allianceId > 0) { - MapleAlliance newAlliance = server.getAlliance(allianceId); - if (newAlliance == null) { - newAlliance = MapleAlliance.loadAlliance(allianceId); - if (newAlliance != null) { - server.addAlliance(allianceId, newAlliance); - } else { - player.getGuild().setAllianceId(0); - } - } + } else { + c.announce(MaplePacketCreator.getFamilyInfo(null)); + } + + if (player.getGuildId() > 0) { + MapleGuild playerGuild = server.getGuild(player.getGuildId(), player.getWorld(), player); + if (playerGuild == null) { + player.deleteGuild(player.getGuildId()); + player.getMGC().setGuildId(0); + player.getMGC().setGuildRank(5); + } else { + playerGuild.getMGC(player.getId()).setCharacter(player); + player.setMGC(playerGuild.getMGC(player.getId())); + server.setGuildMemberOnline(player, true, c.getChannel()); + c.announce(MaplePacketCreator.showGuildInfo(player)); + int allianceId = player.getGuild().getAllianceId(); + if (allianceId > 0) { + MapleAlliance newAlliance = server.getAlliance(allianceId); + if (newAlliance == null) { + newAlliance = MapleAlliance.loadAlliance(allianceId); if (newAlliance != null) { - c.announce(MaplePacketCreator.updateAllianceInfo(newAlliance, c.getWorld())); - c.announce(MaplePacketCreator.allianceNotice(newAlliance.getId(), newAlliance.getNotice())); + server.addAlliance(allianceId, newAlliance); + } else { + player.getGuild().setAllianceId(0); + } + } + if (newAlliance != null) { + c.announce(MaplePacketCreator.updateAllianceInfo(newAlliance, c.getWorld())); + c.announce(MaplePacketCreator.allianceNotice(newAlliance.getId(), newAlliance.getNotice())); - if (newcomer) { - server.allianceMessage(allianceId, MaplePacketCreator.allianceMemberOnline(player, true), player.getId(), -1); - } + if (newcomer) { + server.allianceMessage(allianceId, MaplePacketCreator.allianceMemberOnline(player, true), player.getId(), -1); } } } } + } - player.showNote(); - if (player.getParty() != null) { - MaplePartyCharacter pchar = player.getMPC(); + player.showNote(); + if (player.getParty() != null) { + MaplePartyCharacter pchar = player.getMPC(); - //Use this in case of enabling party HPbar HUD when logging in, however "you created a party" will appear on chat. - //c.announce(MaplePacketCreator.partyCreated(pchar)); + //Use this in case of enabling party HPbar HUD when logging in, however "you created a party" will appear on chat. + //c.announce(MaplePacketCreator.partyCreated(pchar)); - pchar.setChannel(c.getChannel()); - pchar.setMapId(player.getMapId()); - pchar.setOnline(true); - wserv.updateParty(player.getParty().getId(), PartyOperation.LOG_ONOFF, pchar); - player.updatePartyMemberHP(); + pchar.setChannel(c.getChannel()); + pchar.setMapId(player.getMapId()); + pchar.setOnline(true); + wserv.updateParty(player.getParty().getId(), PartyOperation.LOG_ONOFF, pchar); + player.updatePartyMemberHP(); + } + + MapleInventory eqpInv = player.getInventory(MapleInventoryType.EQUIPPED); + eqpInv.lockInventory(); + try { + for (Item it : eqpInv.list()) { + player.equippedItem((Equip) it); + } + } finally { + eqpInv.unlockInventory(); + } + + c.announce(MaplePacketCreator.updateBuddylist(player.getBuddylist().getBuddies())); + + CharacterNameAndId pendingBuddyRequest = c.getPlayer().getBuddylist().pollPendingRequest(); + if (pendingBuddyRequest != null) { + c.announce(MaplePacketCreator.requestBuddylistAdd(pendingBuddyRequest.getId(), c.getPlayer().getId(), pendingBuddyRequest.getName())); + } + + c.announce(MaplePacketCreator.updateGender(player)); + player.checkMessenger(); + c.announce(MaplePacketCreator.enableReport()); + player.changeSkillLevel(SkillFactory.getSkill(10000000 * player.getJobType() + 12), (byte) (player.getLinkedLevel() / 10), 20, -1); + player.checkBerserk(player.isHidden()); + + if (newcomer) { + for (MaplePet pet : player.getPets()) { + if (pet != null) { + wserv.registerPetHunger(player, player.getPetIndex(pet)); + } } - MapleInventory eqpInv = player.getInventory(MapleInventoryType.EQUIPPED); - eqpInv.lockInventory(); - try { - for(Item it : eqpInv.list()) { - player.equippedItem((Equip) it); - } - } finally { - eqpInv.unlockInventory(); + MapleMount mount = player.getMount(); // thanks Ari for noticing a scenario where Silver Mane quest couldn't be started + if (mount.getItemId() != 0) { + player.announce(MaplePacketCreator.updateMount(player.getId(), mount, false)); } - c.announce(MaplePacketCreator.updateBuddylist(player.getBuddylist().getBuddies())); - - CharacterNameAndId pendingBuddyRequest = c.getPlayer().getBuddylist().pollPendingRequest(); - if (pendingBuddyRequest != null) { - c.announce(MaplePacketCreator.requestBuddylistAdd(pendingBuddyRequest.getId(), c.getPlayer().getId(), pendingBuddyRequest.getName())); - } - - c.announce(MaplePacketCreator.updateGender(player)); - player.checkMessenger(); - c.announce(MaplePacketCreator.enableReport()); - player.changeSkillLevel(SkillFactory.getSkill(10000000 * player.getJobType() + 12), (byte) (player.getLinkedLevel() / 10), 20, -1); - player.checkBerserk(player.isHidden()); - - if (newcomer) { - for(MaplePet pet : player.getPets()) { - if(pet != null) { - wserv.registerPetHunger(player, player.getPetIndex(pet)); - } - } - - MapleMount mount = player.getMount(); // thanks Ari for noticing a scenario where Silver Mane quest couldn't be started - if (mount.getItemId() != 0) { - player.announce(MaplePacketCreator.updateMount(player.getId(), mount, false)); - } - - player.reloadQuestExpirations(); + player.reloadQuestExpirations(); /* if (!c.hasVotedAlready()){ player.announce(MaplePacketCreator.earnTitleMessage("You can vote now! Vote and earn a vote point!")); } */ - if (player.isGM()){ - Server.getInstance().broadcastGMMessage(c.getWorld(), MaplePacketCreator.earnTitleMessage((player.gmLevel() < 6 ? "GM " : "Admin ") + player.getName() + " has logged in")); - } - - if(diseases != null) { - for(Entry> e : diseases.entrySet()) { - final List> debuff = Collections.singletonList(new Pair<>(e.getKey(), e.getValue().getRight().getX())); - c.announce(MaplePacketCreator.giveDebuff(debuff, e.getValue().getRight())); - } - } - } else { - if(player.isRidingBattleship()) { - player.announceBattleshipHp(); - } - } - - player.buffExpireTask(); - player.diseaseExpireTask(); - player.skillCooldownTask(); - player.expirationTask(); - player.questExpirationTask(); - if (GameConstants.hasSPTable(player.getJob()) && player.getJob().getId() != 2001) { - player.createDragon(); + if (player.isGM()) { + Server.getInstance().broadcastGMMessage(c.getWorld(), MaplePacketCreator.earnTitleMessage((player.gmLevel() < 6 ? "GM " : "Admin ") + player.getName() + " has logged in")); } - player.commitExcludedItems(); - showDueyNotification(c, player); - - if (player.getMap().getHPDec() > 0) player.resetHpDecreaseTask(); - - player.resetPlayerRates(); - if(YamlConfig.config.server.USE_ADD_RATES_BY_LEVEL == true) player.setPlayerRates(); - player.setWorldRates(); - player.updateCouponRates(); - - player.receivePartyMemberHP(); - - if(player.getPartnerId() > 0) { - int partnerId = player.getPartnerId(); - final MapleCharacter partner = wserv.getPlayerStorage().getCharacterById(partnerId); - - if(partner != null && !partner.isAwayFromWorld()) { - player.announce(Wedding.OnNotifyWeddingPartnerTransfer(partnerId, partner.getMapId())); - partner.announce(Wedding.OnNotifyWeddingPartnerTransfer(player.getId(), player.getMapId())); + if (diseases != null) { + for (Entry> e : diseases.entrySet()) { + final List> debuff = Collections.singletonList(new Pair<>(e.getKey(), e.getValue().getRight().getX())); + c.announce(MaplePacketCreator.giveDebuff(debuff, e.getValue().getRight())); } } - - if (newcomer) { - EventInstanceManager eim = MapleEventRecallCoordinator.getInstance().recallEventInstance(cid); - if (eim != null) { - eim.registerPlayer(player); - } + } else { + if (player.isRidingBattleship()) { + player.announceBattleshipHp(); } - - // Tell the client to use the custom scripts available for the NPCs provided, instead of the WZ entries. - if (YamlConfig.config.server.USE_NPCS_SCRIPTABLE) { - - // Create a copy to prevent always adding entries to the server's list. - Map npcsIds = YamlConfig.config.server.NPCS_SCRIPTABLE - .entrySet().stream().collect(Collectors.toMap( - entry -> Integer.parseInt(entry.getKey()), - Entry::getValue - )); - - // Any npc be specified as the rebirth npc. Allow the npc to use custom scripts explicitly. - if (YamlConfig.config.server.USE_REBIRTH_SYSTEM) { - npcsIds.put(YamlConfig.config.server.REBIRTH_NPC_ID, "Rebirth"); - } - - c.announce(MaplePacketCreator.setNPCScriptable(npcsIds)); - } - - if(newcomer) player.setLoginTime(System.currentTimeMillis()); - } catch(Exception e) { - e.printStackTrace(); - } finally { - c.releaseClient(); } - } else { - c.announce(MaplePacketCreator.getAfterLoginError(10)); + + player.buffExpireTask(); + player.diseaseExpireTask(); + player.skillCooldownTask(); + player.expirationTask(); + player.questExpirationTask(); + if (GameConstants.hasSPTable(player.getJob()) && player.getJob().getId() != 2001) { + player.createDragon(); + } + + player.commitExcludedItems(); + showDueyNotification(c, player); + + if (player.getMap().getHPDec() > 0) player.resetHpDecreaseTask(); + + player.resetPlayerRates(); + if (YamlConfig.config.server.USE_ADD_RATES_BY_LEVEL == true) player.setPlayerRates(); + player.setWorldRates(); + player.updateCouponRates(); + + player.receivePartyMemberHP(); + + if (player.getPartnerId() > 0) { + int partnerId = player.getPartnerId(); + final MapleCharacter partner = wserv.getPlayerStorage().getCharacterById(partnerId); + + if (partner != null && !partner.isAwayFromWorld()) { + player.announce(Wedding.OnNotifyWeddingPartnerTransfer(partnerId, partner.getMapId())); + partner.announce(Wedding.OnNotifyWeddingPartnerTransfer(player.getId(), player.getMapId())); + } + } + + if (newcomer) { + EventInstanceManager eim = MapleEventRecallCoordinator.getInstance().recallEventInstance(cid); + if (eim != null) { + eim.registerPlayer(player); + } + } + + // Tell the client to use the custom scripts available for the NPCs provided, instead of the WZ entries. + if (YamlConfig.config.server.USE_NPCS_SCRIPTABLE) { + + // Create a copy to prevent always adding entries to the server's list. + Map npcsIds = YamlConfig.config.server.NPCS_SCRIPTABLE + .entrySet().stream().collect(Collectors.toMap( + entry -> Integer.parseInt(entry.getKey()), + Entry::getValue + )); + + // Any npc be specified as the rebirth npc. Allow the npc to use custom scripts explicitly. + if (YamlConfig.config.server.USE_REBIRTH_SYSTEM) { + npcsIds.put(YamlConfig.config.server.REBIRTH_NPC_ID, "Rebirth"); + } + + c.announce(MaplePacketCreator.setNPCScriptable(npcsIds)); + } + + if (newcomer) player.setLoginTime(System.currentTimeMillis()); + } catch (Exception e) { + e.printStackTrace(); + } finally { + c.releaseClient(); } } From fcb43af8e316031cb1f2fcd86b13ed67cbd5b3f9 Mon Sep 17 00:00:00 2001 From: P0nk Date: Sat, 10 Jul 2021 13:51:53 +0200 Subject: [PATCH 21/37] Minor refactors --- src/main/java/net/netty/ServerChannelInitializer.java | 11 ++++++----- .../coordinator/session/MapleSessionCoordinator.java | 4 ++-- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/main/java/net/netty/ServerChannelInitializer.java b/src/main/java/net/netty/ServerChannelInitializer.java index 868250151d..493d47a61e 100644 --- a/src/main/java/net/netty/ServerChannelInitializer.java +++ b/src/main/java/net/netty/ServerChannelInitializer.java @@ -4,6 +4,7 @@ import client.MapleClient; import constants.net.ServerConstants; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelInitializer; +import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import io.netty.handler.timeout.IdleStateHandler; import tools.MaplePacketCreator; @@ -17,17 +18,17 @@ public abstract class ServerChannelInitializer extends ChannelInitializer sessions = new HashSet<>(2); sessions.add(session); loginRemoteHosts.put(remoteHost, sessions); From 5a7d8e6b1f3d10694a5eefc27c035ba0aefd1889 Mon Sep 17 00:00:00 2001 From: P0nk Date: Sun, 11 Jul 2021 18:56:45 +0200 Subject: [PATCH 22/37] Use MapleClient for session management First working version of the Netty implementation, but there's a lot remaining to be done. --- src/main/java/client/MapleClient.java | 64 ++++---- .../command/commands/gm3/BanCommand.java | 2 +- .../command/commands/gm5/IpListCommand.java | 2 +- .../commands/gm6/WarpWorldCommand.java | 2 +- src/main/java/net/MapleServerHandler.java | 33 ++-- .../java/net/mina/MaplePacketDecoder.java | 6 +- .../java/net/mina/MaplePacketEncoder.java | 9 +- src/main/java/net/server/Server.java | 9 +- .../channel/handlers/AdminCommandHandler.java | 2 +- .../handlers/PlayerLoggedinHandler.java | 14 +- .../net/server/coordinator/session/Hwid.java | 4 + .../session/MapleSessionCoordinator.java | 142 +++++++++--------- .../handlers/login/AfterLoginHandler.java | 2 +- .../handlers/login/CharSelectedHandler.java | 40 ++--- .../login/CharSelectedWithPicHandler.java | 21 ++- .../handlers/login/RegisterPicHandler.java | 24 ++- .../handlers/login/RegisterPinHandler.java | 4 +- .../handlers/login/SetGenderHandler.java | 2 +- .../login/ViewAllCharRegisterPicHandler.java | 17 +-- .../login/ViewAllCharSelectedHandler.java | 17 +-- .../ViewAllCharSelectedWithPicHandler.java | 25 ++- src/main/java/server/maps/MapleMiniGame.java | 2 +- .../java/server/maps/MaplePlayerShop.java | 5 +- 23 files changed, 210 insertions(+), 238 deletions(-) diff --git a/src/main/java/client/MapleClient.java b/src/main/java/client/MapleClient.java index 9cf408b875..1eb5f0f71a 100644 --- a/src/main/java/client/MapleClient.java +++ b/src/main/java/client/MapleClient.java @@ -92,12 +92,12 @@ public class MapleClient extends ChannelInboundHandlerAdapter { public static final String CLIENT_REMOTE_ADDRESS = "REMOTE_IP"; private Hwid hwid; + private String remoteHwid; // Mac address + hwid in one. Retrieved from client when attempting to enter game. private String remoteAddress; private volatile boolean inTransition; private MapleAESOFB send; private MapleAESOFB receive; - private final IoSession session; private io.netty.channel.Channel ioChannel; private PacketProcessor packetProcessor; @@ -148,13 +148,11 @@ public class MapleClient extends ChannelInboundHandlerAdapter { this.packetProcessor = packetProcessor; this.world = world; this.channel = channel; - this.session = null; // TODO remove once the other constructor is removed } public MapleClient(MapleAESOFB send, MapleAESOFB receive, IoSession session) { this.send = send; this.receive = receive; - this.session = session; } @Override @@ -266,10 +264,6 @@ public class MapleClient extends ChannelInboundHandlerAdapter { return send; } - public IoSession getSession() { - return session; - } - public Hwid getHwid() { return hwid; } @@ -278,6 +272,14 @@ public class MapleClient extends ChannelInboundHandlerAdapter { this.hwid = hwid; } + public String getRemoteHwid() { + return remoteHwid; + } + + public void setRemoteHwid(String remoteHwid) { + this.remoteHwid = remoteHwid; + } + public String getRemoteAddress() { return remoteAddress; } @@ -352,7 +354,7 @@ public class MapleClient extends ChannelInboundHandlerAdapter { boolean ret = false; try (Connection con = DatabaseConnection.getConnection(); PreparedStatement ps = con.prepareStatement("SELECT COUNT(*) FROM ipbans WHERE ? LIKE CONCAT(ip, '%')")) { - ps.setString(1, session.getRemoteAddress().toString()); + ps.setString(1, remoteAddress); try (ResultSet rs = ps.executeQuery()) { rs.next(); if (rs.getInt(1) > 0) { @@ -576,7 +578,7 @@ public class MapleClient extends ChannelInboundHandlerAdapter { pinattempt++; if (pinattempt > 5) { - MapleSessionCoordinator.getInstance().closeSession(session, false); + MapleSessionCoordinator.getInstance().closeSession(this, false); } if (pin.equals(other)) { pinattempt = 0; @@ -609,7 +611,7 @@ public class MapleClient extends ChannelInboundHandlerAdapter { picattempt++; if (picattempt > 5) { - MapleSessionCoordinator.getInstance().closeSession(session, false); + MapleSessionCoordinator.getInstance().closeSession(this, false); } if (pic.equals(other)) { // thanks ryantpayton (HeavenClient) for noticing null pics being checked here picattempt = 0; @@ -625,7 +627,7 @@ public class MapleClient extends ChannelInboundHandlerAdapter { loginattempt++; if (loginattempt > 4) { loggedIn = false; - MapleSessionCoordinator.getInstance().closeSession(session, false); + MapleSessionCoordinator.getInstance().closeSession(this, false); return 6; // thanks Survival_Project for finding out an issue with AUTOMATIC_REGISTER here } @@ -677,7 +679,7 @@ public class MapleClient extends ChannelInboundHandlerAdapter { } if (loginok == 0 || loginok == 4) { - AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptLoginSession(session, nibbleHwid, accId, loginok == 4); + AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptLoginSession(this, nibbleHwid, accId, loginok == 4); switch (res) { case SUCCESS: @@ -812,7 +814,7 @@ public class MapleClient extends ChannelInboundHandlerAdapter { public void updateLoginState(int newState) { // rules out possibility of multiple account entries if (newState == LOGIN_LOGGEDIN) { - MapleSessionCoordinator.getInstance().updateOnlineClient(session); + MapleSessionCoordinator.getInstance().updateOnlineClient(this); } try (Connection con = DatabaseConnection.getConnection(); @@ -1046,18 +1048,14 @@ public class MapleClient extends ChannelInboundHandlerAdapter { } } } + + MapleSessionCoordinator.getInstance().closeSession(this, false); + if (!serverTransition && isLoggedIn()) { - MapleSessionCoordinator.getInstance().closeSession(session, false); updateLoginState(MapleClient.LOGIN_NOTLOGGEDIN); - session.removeAttribute(MapleClient.CLIENT_KEY); // prevents double dcing during login clear(); } else { - if (session.containsAttribute(MapleClient.CLIENT_KEY)) { - MapleSessionCoordinator.getInstance().closeSession(session, false); - session.removeAttribute(MapleClient.CLIENT_KEY); - } - if (!Server.getInstance().hasCharacteridInTransition(this)) { updateLoginState(MapleClient.LOGIN_NOTLOGGEDIN); } @@ -1154,18 +1152,9 @@ public class MapleClient extends ChannelInboundHandlerAdapter { lastPong = Server.getInstance().getCurrentTime(); } + @Deprecated(forRemoval = true) public void testPing(long timeThen) { - try { - if (lastPong < timeThen) { - if (session != null && session.isConnected()) { - MapleSessionCoordinator.getInstance().closeSession(session, false); - updateLoginState(MapleClient.LOGIN_NOTLOGGEDIN); - session.removeAttribute(MapleClient.CLIENT_KEY); - } - } - } catch (NullPointerException e) { - e.printStackTrace(); - } + throw new UnsupportedOperationException(); } public void checkIfIdle(final IdleStateEvent event) { @@ -1522,7 +1511,7 @@ public class MapleClient extends ChannelInboundHandlerAdapter { return; } - String[] socket = Server.getInstance().getInetSocket(this.getSession(), getWorld(), channel); + String[] socket = Server.getInstance().getInetSocket(this, getWorld(), channel); if (socket == null) { announce(MaplePacketCreator.serverNotice(1, "Channel " + channel + " is currently disabled. Try another channel.")); announce(MaplePacketCreator.enableActions()); @@ -1619,8 +1608,13 @@ public class MapleClient extends ChannelInboundHandlerAdapter { announce(MaplePacketCreator.enableCSUse(player)); } + @Deprecated public String getNibbleHWID() { - return (String) session.getAttribute(MapleClient.CLIENT_NIBBLEHWID); + if (hwid != null) { + return hwid.hwid(); + } + + return null; } public boolean canBypassPin() { @@ -1638,4 +1632,8 @@ public class MapleClient extends ChannelInboundHandlerAdapter { public void setLanguage(int lingua) { this.lang = lingua; } + + public static MapleClient getPlaceholder() { + return null; + } } \ No newline at end of file diff --git a/src/main/java/client/command/commands/gm3/BanCommand.java b/src/main/java/client/command/commands/gm3/BanCommand.java index 21080eb3a8..38a773852b 100644 --- a/src/main/java/client/command/commands/gm3/BanCommand.java +++ b/src/main/java/client/command/commands/gm3/BanCommand.java @@ -52,7 +52,7 @@ public class BanCommand extends Command { MapleCharacter target = c.getChannelServer().getPlayerStorage().getCharacterByName(ign); if (target != null) { String readableTargetName = MapleCharacter.makeMapleReadable(target.getName()); - String ip = target.getClient().getSession().getRemoteAddress().toString().split(":")[0]; + String ip = target.getClient().getRemoteAddress(); //Ban ip try (Connection con = DatabaseConnection.getConnection()) { if (ip.matches("/[0-9]{1,3}\\..*")) { diff --git a/src/main/java/client/command/commands/gm5/IpListCommand.java b/src/main/java/client/command/commands/gm5/IpListCommand.java index 1a832916b7..c7ae291fbd 100644 --- a/src/main/java/client/command/commands/gm5/IpListCommand.java +++ b/src/main/java/client/command/commands/gm5/IpListCommand.java @@ -50,7 +50,7 @@ public class IpListCommand extends Command { str += "\r\n" + GameConstants.WORLD_NAMES[w.getId()] + "\r\n"; for (MapleCharacter chr : chars) { - str += " " + chr.getName() + " - " + chr.getClient().getSession().getRemoteAddress() + "\r\n"; + str += " " + chr.getName() + " - " + chr.getClient().getRemoteAddress() + "\r\n"; } } } diff --git a/src/main/java/client/command/commands/gm6/WarpWorldCommand.java b/src/main/java/client/command/commands/gm6/WarpWorldCommand.java index 9670f16a87..0512f1f4fc 100644 --- a/src/main/java/client/command/commands/gm6/WarpWorldCommand.java +++ b/src/main/java/client/command/commands/gm6/WarpWorldCommand.java @@ -49,7 +49,7 @@ public class WarpWorldCommand extends Command { byte worldb = Byte.parseByte(params[0]); if (worldb <= (server.getWorldsSize() - 1)) { try { - String[] socket = server.getInetSocket(c.getSession(), worldb, c.getChannel()); + String[] socket = server.getInetSocket(c, worldb, c.getChannel()); c.getWorldServer().removePlayer(player); player.getMap().removePlayer(player);//LOL FORGOT THIS >< player.setSessionTransitionState(); diff --git a/src/main/java/net/MapleServerHandler.java b/src/main/java/net/MapleServerHandler.java index 78ee117c28..bb9cec2692 100644 --- a/src/main/java/net/MapleServerHandler.java +++ b/src/main/java/net/MapleServerHandler.java @@ -115,28 +115,29 @@ public class MapleServerHandler extends IoHandlerAdapter { session.setAttribute(MapleClient.CLIENT_REMOTE_ADDRESS, remoteHost); if (!Server.getInstance().isOnline()) { - MapleSessionCoordinator.getInstance().closeSession(session, true); + MapleSessionCoordinator.getInstance().closeSession(MapleClient.getPlaceholder(), true); return; } - - if (!isLoginServerHandler()) { - if (Server.getInstance().getChannel(world, channel) == null) { - MapleSessionCoordinator.getInstance().closeSession(session, true); - return; - } - } else { - if (!MapleSessionCoordinator.getInstance().canStartLoginSession(session)) { - return; - } - - FilePrinter.print(FilePrinter.SESSION, "IoSession with " + session.getRemoteAddress() + " opened on " + sdf.format(Calendar.getInstance().getTime()), false); - } final InitializationVector ivSend = InitializationVector.generateSend(); final InitializationVector ivRecv = InitializationVector.generateReceive(); MapleAESOFB sendCypher = new MapleAESOFB(ivSend, (short) (0xFFFF - ServerConstants.VERSION)); MapleAESOFB recvCypher = new MapleAESOFB(ivRecv, ServerConstants.VERSION); MapleClient client = new MapleClient(sendCypher, recvCypher, session); + + if (!isLoginServerHandler()) { + if (Server.getInstance().getChannel(world, channel) == null) { + MapleSessionCoordinator.getInstance().closeSession(MapleClient.getPlaceholder(), true); + return; + } + } else { + if (!MapleSessionCoordinator.getInstance().canStartLoginSession(client)) { + return; + } + + FilePrinter.print(FilePrinter.SESSION, "IoSession with " + session.getRemoteAddress() + " opened on " + sdf.format(Calendar.getInstance().getTime()), false); + } + client.setWorld(world); client.setChannel(channel); client.setSessionId(sessionId.getAndIncrement()); // Generates a reasonable session id. @@ -146,9 +147,9 @@ public class MapleServerHandler extends IoHandlerAdapter { private void closeMapleSession(IoSession session) { if (isLoginServerHandler()) { - MapleSessionCoordinator.getInstance().closeLoginSession(session); + MapleSessionCoordinator.getInstance().closeLoginSession(MapleClient.getPlaceholder()); } else { - MapleSessionCoordinator.getInstance().closeSession(session, null); + MapleSessionCoordinator.getInstance().closeSession(MapleClient.getPlaceholder(), null); } MapleClient client = (MapleClient) session.getAttribute(MapleClient.CLIENT_KEY); diff --git a/src/main/java/net/mina/MaplePacketDecoder.java b/src/main/java/net/mina/MaplePacketDecoder.java index c66a595b33..735160b797 100644 --- a/src/main/java/net/mina/MaplePacketDecoder.java +++ b/src/main/java/net/mina/MaplePacketDecoder.java @@ -46,7 +46,7 @@ public class MaplePacketDecoder extends CumulativeProtocolDecoder { protected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception { final MapleClient client = (MapleClient) session.getAttribute(MapleClient.CLIENT_KEY); if(client == null) { - MapleSessionCoordinator.getInstance().closeSession(session, true); + MapleSessionCoordinator.getInstance().closeSession(MapleClient.getPlaceholder(), true); return false; } @@ -60,7 +60,7 @@ public class MaplePacketDecoder extends CumulativeProtocolDecoder { if (in.remaining() >= 4 && decoderState.packetlength == -1) { int packetHeader = in.getInt(); if (!rcvdCrypto.isValidHeader(packetHeader)) { - MapleSessionCoordinator.getInstance().closeSession(session, true); + MapleSessionCoordinator.getInstance().closeSession(MapleClient.getPlaceholder(), true); return false; } decoderState.packetlength = MapleAESOFB.getPacketLength(packetHeader); @@ -87,7 +87,7 @@ public class MaplePacketDecoder extends CumulativeProtocolDecoder { System.out.println("UnknownPacket:" + SendTo); } } else { - FilePrinter.print(FilePrinter.PACKET_STREAM + MapleSessionCoordinator.getSessionRemoteAddress(session) + ".txt", HexTool.toString(new byte[]{decryptedPacket[0], decryptedPacket[1]}) + "..."); + FilePrinter.print(FilePrinter.PACKET_STREAM + ".txt", HexTool.toString(new byte[]{decryptedPacket[0], decryptedPacket[1]}) + "..."); } } return true; diff --git a/src/main/java/net/mina/MaplePacketEncoder.java b/src/main/java/net/mina/MaplePacketEncoder.java index 2bd7fda9b4..e1a542deca 100644 --- a/src/main/java/net/mina/MaplePacketEncoder.java +++ b/src/main/java/net/mina/MaplePacketEncoder.java @@ -21,19 +21,18 @@ along with this program. If not, see . */ package net.mina; -import config.YamlConfig; import client.MapleClient; +import config.YamlConfig; import constants.net.OpcodeConstants; -import net.server.coordinator.session.MapleSessionCoordinator; import org.apache.mina.core.buffer.IoBuffer; import org.apache.mina.core.session.IoSession; import org.apache.mina.filter.codec.ProtocolEncoder; import org.apache.mina.filter.codec.ProtocolEncoderOutput; -import tools.MapleAESOFB; +import tools.FilePrinter; import tools.HexTool; +import tools.MapleAESOFB; import tools.data.input.ByteArrayByteStream; import tools.data.input.GenericLittleEndianAccessor; -import tools.FilePrinter; public class MaplePacketEncoder implements ProtocolEncoder { @@ -59,7 +58,7 @@ public class MaplePacketEncoder implements ProtocolEncoder { System.out.println("UnknownPacket:" + RecvTo); } } else { - FilePrinter.print(FilePrinter.PACKET_STREAM + MapleSessionCoordinator.getSessionRemoteAddress(session) + ".txt", HexTool.toString(new byte[]{input[0], input[1]}) + " ..."); + FilePrinter.print(FilePrinter.PACKET_STREAM + ".txt", HexTool.toString(new byte[]{input[0], input[1]}) + " ..."); } } diff --git a/src/main/java/net/server/Server.java b/src/main/java/net/server/Server.java index 87732a2843..4eac0d0c50 100644 --- a/src/main/java/net/server/Server.java +++ b/src/main/java/net/server/Server.java @@ -58,7 +58,6 @@ import org.apache.mina.core.buffer.IoBuffer; import org.apache.mina.core.buffer.SimpleBufferAllocator; import org.apache.mina.core.service.IoAcceptor; import org.apache.mina.core.session.IdleStatus; -import org.apache.mina.core.session.IoSession; import org.apache.mina.filter.codec.ProtocolCodecFilter; import org.apache.mina.transport.socket.nio.NioSocketAcceptor; import org.slf4j.Logger; @@ -294,8 +293,8 @@ public class Server { } } - public String[] getInetSocket(IoSession session, int world, int channel) { - String remoteIp = MapleSessionCoordinator.getSessionRemoteAddress(session); + public String[] getInetSocket(MapleClient client, int world, int channel) { + String remoteIp = client.getRemoteAddress(); String[] hostAddress = getIP(world, channel).split(":"); if (IpAddresses.isLocalAddress(remoteIp)) { @@ -1792,7 +1791,7 @@ public class Server { } private static String getRemoteHost(MapleClient client) { - return MapleSessionCoordinator.getSessionRemoteHost(client.getSession()); + return MapleSessionCoordinator.getSessionRemoteHost(client); } public void setCharacteridInTransition(MapleClient client, int charId) { @@ -1894,7 +1893,7 @@ public class Server { if (c.isLoggedIn()) { c.disconnect(false, false); } else { - MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true); + MapleSessionCoordinator.getInstance().closeSession(c, true); } } } diff --git a/src/main/java/net/server/channel/handlers/AdminCommandHandler.java b/src/main/java/net/server/channel/handlers/AdminCommandHandler.java index 7cc357ffea..9c8b80a88a 100644 --- a/src/main/java/net/server/channel/handlers/AdminCommandHandler.java +++ b/src/main/java/net/server/channel/handlers/AdminCommandHandler.java @@ -86,7 +86,7 @@ public final class AdminCommandHandler extends AbstractMaplePacketHandler { target = c.getChannelServer().getPlayerStorage().getCharacterByName(victim); if (target != null) { String readableTargetName = MapleCharacter.makeMapleReadable(target.getName()); - String ip = target.getClient().getSession().getRemoteAddress().toString().split(":")[0]; + String ip = target.getClient().getRemoteAddress(); reason += readableTargetName + " (IP: " + ip + ")"; if (duration == -1) { target.ban(description + " " + reason); diff --git a/src/main/java/net/server/channel/handlers/PlayerLoggedinHandler.java b/src/main/java/net/server/channel/handlers/PlayerLoggedinHandler.java index 3e718bda79..49b6892e01 100644 --- a/src/main/java/net/server/channel/handlers/PlayerLoggedinHandler.java +++ b/src/main/java/net/server/channel/handlers/PlayerLoggedinHandler.java @@ -39,7 +39,6 @@ import net.server.guild.MapleGuild; import net.server.world.MaplePartyCharacter; import net.server.world.PartyOperation; import net.server.world.World; -import org.apache.mina.core.session.IoSession; import scripting.event.EventInstanceManager; import server.life.MobSkill; import tools.DatabaseConnection; @@ -112,11 +111,10 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler { } MapleCharacter player = wserv.getPlayerStorage().getCharacterById(cid); - IoSession session = c.getSession(); String remoteHwid; if (player == null) { - remoteHwid = MapleSessionCoordinator.getInstance().pickLoginSessionHwid(session); + remoteHwid = MapleSessionCoordinator.getInstance().pickLoginSessionHwid(c); if (remoteHwid == null) { c.disconnect(true, false); return; @@ -126,10 +124,7 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler { remoteHwid = clientHwid == null ? null : clientHwid.hwid(); } - int hwidLen = remoteHwid.length(); - session.setAttribute(MapleClient.CLIENT_HWID, remoteHwid); - String nibbleHwid = remoteHwid.substring(hwidLen - 8, hwidLen); - session.setAttribute(MapleClient.CLIENT_NIBBLEHWID, nibbleHwid); + c.setRemoteHwid(remoteHwid); c.setHwid(new Hwid(remoteHwid)); if (!server.validateCharacteridInTransition(c, cid)) { @@ -393,7 +388,10 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler { if (player.getMap().getHPDec() > 0) player.resetHpDecreaseTask(); player.resetPlayerRates(); - if (YamlConfig.config.server.USE_ADD_RATES_BY_LEVEL == true) player.setPlayerRates(); + if (YamlConfig.config.server.USE_ADD_RATES_BY_LEVEL) { + player.setPlayerRates(); + } + player.setWorldRates(); player.updateCouponRates(); diff --git a/src/main/java/net/server/coordinator/session/Hwid.java b/src/main/java/net/server/coordinator/session/Hwid.java index fdaed64b04..8b143c0c40 100644 --- a/src/main/java/net/server/coordinator/session/Hwid.java +++ b/src/main/java/net/server/coordinator/session/Hwid.java @@ -12,6 +12,10 @@ public record Hwid (String hwid) { } public static Hwid fromClientString(String clientString) throws IllegalArgumentException { + if (clientString == null) { + throw new IllegalArgumentException("clientString must not be null"); + } + String[] split = clientString.split("_"); if (split.length != 2 || split[1].length() != HWID_LENGTH) { throw new IllegalArgumentException("Hwid validation failed for hwid: " + clientString); diff --git a/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java b/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java index 4e9512816a..314b212da2 100644 --- a/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java +++ b/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java @@ -24,7 +24,6 @@ import client.MapleClient; import config.YamlConfig; import net.server.Server; import net.server.coordinator.login.LoginStorage; -import org.apache.mina.core.session.IoSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import tools.DatabaseConnection; @@ -62,7 +61,7 @@ public class MapleSessionCoordinator { private final LoginStorage loginStorage = new LoginStorage(); private final Map onlineClients = new HashMap<>(); // Key: account id private final Set onlineRemoteHwids = new HashSet<>(); // Hwid/nibblehwid - private final Map> loginRemoteHosts = new HashMap<>(); // Key: Ip (+ nibblehwid) + private final Map> loginRemoteHosts = new HashMap<>(); // Key: Ip (+ nibblehwid) private final HostHwidCache hostHwidCache = new HostHwidCache(); private MapleSessionCoordinator() { @@ -93,30 +92,20 @@ public class MapleSessionCoordinator { return false; } - public static String getSessionRemoteAddress(IoSession session) { - return (String) session.getAttribute(MapleClient.CLIENT_REMOTE_ADDRESS); - } - - public static String getSessionRemoteHost(IoSession session) { - String nibbleHwid = (String) session.getAttribute(MapleClient.CLIENT_NIBBLEHWID); + public static String getSessionRemoteHost(MapleClient client) { + String nibbleHwid = client.getNibbleHWID(); if (nibbleHwid != null) { - return getSessionRemoteAddress(session) + "-" + nibbleHwid; + return client.getRemoteAddress() + "-" + nibbleHwid; } else { - return getSessionRemoteAddress(session); + return client.getRemoteAddress(); } } - private static MapleClient getSessionClient(IoSession session) { - return (MapleClient) session.getAttribute(MapleClient.CLIENT_KEY); - } - /** * 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); - + public void updateOnlineClient(MapleClient client) { if (client != null) { int accountId = client.getAccID(); disconnectClientIfOnline(accountId); @@ -131,12 +120,12 @@ public class MapleSessionCoordinator { } } - public boolean canStartLoginSession(IoSession session) { + public boolean canStartLoginSession(MapleClient client) { if (!YamlConfig.config.server.DETERRED_MULTICLIENT) { return true; } - String remoteHost = getSessionRemoteHost(session); + String remoteHost = getSessionRemoteHost(client); final InitializationResult initResult = sessionInit.initialize(remoteHost); switch (initResult.getAntiMulticlientResult()) { case REMOTE_PROCESSING -> { @@ -155,28 +144,28 @@ public class MapleSessionCoordinator { return false; } - addLoginRemoteHostSession(remoteHost, session); + addLoginRemoteHostClient(remoteHost, client); return true; } finally { sessionInit.finalize(remoteHost); } } - private void addLoginRemoteHostSession(String remoteHost, IoSession session) { - Set sessions = new HashSet<>(2); - sessions.add(session); - loginRemoteHosts.put(remoteHost, sessions); + private void addLoginRemoteHostClient(String remoteHost, MapleClient client) { + Set clients = new HashSet<>(2); + clients.add(client); + loginRemoteHosts.put(remoteHost, clients); } - public void closeLoginSession(IoSession session) { - String remoteHost = getSessionRemoteHost(session); - removeRemoteHostSession(remoteHost, session); + public void closeLoginSession(MapleClient client) { + String remoteHost = getSessionRemoteHost(client); + removeRemoteHostClient(remoteHost, client); - String nibbleHwid = (String) session.removeAttribute(MapleClient.CLIENT_NIBBLEHWID); + Hwid nibbleHwid = client.getHwid(); + client.setHwid(null); if (nibbleHwid != null) { - onlineRemoteHwids.remove(nibbleHwid); + onlineRemoteHwids.remove(nibbleHwid.hwid()); - MapleClient client = getSessionClient(session); if (client != null) { MapleClient loggedClient = onlineClients.get(client.getAccID()); @@ -188,24 +177,24 @@ public class MapleSessionCoordinator { } } - private void removeRemoteHostSession(String remoteHost, IoSession session) { - Set sessions = loginRemoteHosts.get(remoteHost); - if (sessions != null) { - sessions.remove(session); + private void removeRemoteHostClient(String remoteHost, MapleClient client) { + Set clients = loginRemoteHosts.get(remoteHost); + if (clients != null) { + clients.remove(client); - if (sessions.isEmpty()) { + if (clients.isEmpty()) { loginRemoteHosts.remove(remoteHost); } } } - public AntiMulticlientResult attemptLoginSession(IoSession session, String nibbleHwid, int accountId, boolean routineCheck) { + public AntiMulticlientResult attemptLoginSession(MapleClient client, String nibbleHwid, int accountId, boolean routineCheck) { if (!YamlConfig.config.server.DETERRED_MULTICLIENT) { - session.setAttribute(MapleClient.CLIENT_NIBBLEHWID, nibbleHwid); + client.setHwid(new Hwid(nibbleHwid)); return AntiMulticlientResult.SUCCESS; } - String remoteHost = getSessionRemoteHost(session); + String remoteHost = getSessionRemoteHost(client); InitializationResult initResult = sessionInit.initialize(remoteHost); if (initResult != InitializationResult.SUCCESS) { return initResult.getAntiMulticlientResult(); @@ -222,7 +211,7 @@ public class MapleSessionCoordinator { return AntiMulticlientResult.REMOTE_REACHED_LIMIT; } - session.setAttribute(MapleClient.CLIENT_NIBBLEHWID, nibbleHwid); + client.setHwid(new Hwid(nibbleHwid)); onlineRemoteHwids.add(nibbleHwid); return AntiMulticlientResult.SUCCESS; @@ -231,11 +220,11 @@ public class MapleSessionCoordinator { } } - public AntiMulticlientResult attemptGameSession(IoSession session, int accountId, String remoteHwid) { - final String remoteHost = getSessionRemoteHost(session); + public AntiMulticlientResult attemptGameSession(MapleClient client, int accountId, String remoteHwid) { + final String remoteHost = getSessionRemoteHost(client); if (!YamlConfig.config.server.DETERRED_MULTICLIENT) { hostHwidCache.addEntry(remoteHost, remoteHwid); - hostHwidCache.addEntry(getSessionRemoteAddress(session), remoteHwid); // no HWID information on the loggedin newcomer session... + hostHwidCache.addEntry(client.getRemoteAddress(), remoteHwid); // no HWID information on the loggedin newcomer session... return AntiMulticlientResult.SUCCESS; } @@ -245,14 +234,14 @@ public class MapleSessionCoordinator { } try { - String nibbleHwid = (String) session.getAttribute(MapleClient.CLIENT_NIBBLEHWID); // thanks Paxum for noticing account stuck after PIC failure + Hwid nibbleHwid = client.getHwid(); // thanks Paxum for noticing account stuck after PIC failure if (nibbleHwid == null) { return AntiMulticlientResult.REMOTE_NO_MATCH; } onlineRemoteHwids.remove(nibbleHwid); - if (!remoteHwid.endsWith(nibbleHwid)) { + if (!remoteHwid.endsWith(nibbleHwid.hwid())) { return AntiMulticlientResult.REMOTE_NO_MATCH; } else if (onlineRemoteHwids.contains(remoteHwid)) { return AntiMulticlientResult.REMOTE_LOGGEDIN; @@ -263,7 +252,7 @@ public class MapleSessionCoordinator { // updated session CLIENT_HWID attribute will be set when the player log in the game onlineRemoteHwids.add(remoteHwid); hostHwidCache.addEntry(remoteHost, remoteHwid); - hostHwidCache.addEntry(getSessionRemoteAddress(session), remoteHwid); + hostHwidCache.addEntry(client.getRemoteAddress(), remoteHwid); associateHwidAccountIfAbsent(remoteHwid, accountId); return AntiMulticlientResult.SUCCESS; @@ -290,9 +279,8 @@ public class MapleSessionCoordinator { } } - private static MapleClient fetchInTransitionSessionClient(IoSession session) { - String remoteHwid = MapleSessionCoordinator.getInstance().getGameSessionHwid(session); - + private static MapleClient fetchInTransitionSessionClient(MapleClient client) { + String remoteHwid = MapleSessionCoordinator.getInstance().getGameSessionHwid(client); if (remoteHwid == null) { // maybe this session was currently in-transition? return null; } @@ -300,42 +288,46 @@ public class MapleSessionCoordinator { final int hwidLen = remoteHwid.length(); final boolean isOnlyNibbleHwid = hwidLen <= 8; if (isOnlyNibbleHwid) { - session.setAttribute(MapleClient.CLIENT_NIBBLEHWID, remoteHwid); + client.setHwid(new Hwid(remoteHwid)); + } else { - session.setAttribute(MapleClient.CLIENT_HWID, remoteHwid); - session.setAttribute(MapleClient.CLIENT_NIBBLEHWID, remoteHwid.substring(hwidLen - 8, hwidLen)); + client.setRemoteHwid(remoteHwid); + client.setHwid(Hwid.fromClientString(remoteHwid)); } - MapleClient client = new MapleClient(null, null, session); + MapleClient fakeClient = new MapleClient(null, null, null); Integer chrId = Server.getInstance().freeCharacteridInTransition(client); if (chrId != null) { try { - client.setAccID(MapleCharacter.loadCharFromDB(chrId, client, false).getAccountID()); + fakeClient.setAccID(MapleCharacter.loadCharFromDB(chrId, client, false).getAccountID()); } catch (SQLException sqle) { sqle.printStackTrace(); } } - session.setAttribute(MapleClient.CLIENT_KEY, client); - return client; + return fakeClient; } - public void closeSession(IoSession session, Boolean immediately) { - MapleClient client = getSessionClient(session); + public void closeSession(MapleClient client, Boolean immediately) { if (client == null) { - client = fetchInTransitionSessionClient(session); + client = fetchInTransitionSessionClient(client); } - String hwid = (String) session.removeAttribute(MapleClient.CLIENT_NIBBLEHWID); // making sure to clean up calls to this function on login phase - // TODO: client.setNibbleHwid(null); - onlineRemoteHwids.remove(hwid); + Hwid hwid = client.getHwid(); + client.setHwid(null); // making sure to clean up calls to this function on login phase + if (hwid != null) { + onlineRemoteHwids.remove(hwid.hwid()); + } - hwid = (String) session.removeAttribute(MapleClient.CLIENT_HWID); + String remoteHwid = client.getRemoteHwid(); + client.setRemoteHwid(null); // TODO: client.setHwid(null); - onlineRemoteHwids.remove(hwid); + if (remoteHwid != null) { + onlineRemoteHwids.remove(remoteHwid); + } if (client != null) { - final boolean isGameSession = hwid != null; + final boolean isGameSession = hwid != null || remoteHwid != null; if (isGameSession) { onlineClients.remove(client.getAccID()); } else { @@ -347,20 +339,20 @@ public class MapleSessionCoordinator { } } } - - if (immediately != null) { - session.close(immediately); + + if (immediately != null && immediately) { + client.closeSession(); } } - public String pickLoginSessionHwid(IoSession session) { - String remoteHost = getSessionRemoteAddress(session); + public String pickLoginSessionHwid(MapleClient client) { + String remoteHost = client.getRemoteAddress(); // 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); + public String getGameSessionHwid(MapleClient client) { + String remoteHost = getSessionRemoteHost(client); return hostHwidCache.getEntryHwid(remoteHost); } @@ -395,11 +387,11 @@ public class MapleSessionCoordinator { } if (!loginRemoteHosts.isEmpty()) { - List>> elist = new ArrayList<>(loginRemoteHosts.entrySet()); + List>> elist = new ArrayList<>(loginRemoteHosts.entrySet()); elist.sort(Entry.comparingByKey()); System.out.println("Current login sessions: "); - for (Entry> e : elist) { + for (Entry> e : elist) { System.out.println(" " + e.getKey() + ", size: " + e.getValue().size()); } } @@ -429,12 +421,12 @@ public class MapleSessionCoordinator { } if (!loginRemoteHosts.isEmpty()) { - List>> elist = new ArrayList<>(loginRemoteHosts.entrySet()); + List>> elist = new ArrayList<>(loginRemoteHosts.entrySet()); elist.sort((e1, e2) -> e1.getKey().compareTo(e2.getKey())); str += ("Current login sessions:\r\n"); - for (Entry> e : elist) { + for (Entry> e : elist) { str += (" " + e.getKey() + ", IP: " + e.getValue() + "\r\n"); } } diff --git a/src/main/java/net/server/handlers/login/AfterLoginHandler.java b/src/main/java/net/server/handlers/login/AfterLoginHandler.java index f3acb93bf0..1364367a3d 100644 --- a/src/main/java/net/server/handlers/login/AfterLoginHandler.java +++ b/src/main/java/net/server/handlers/login/AfterLoginHandler.java @@ -57,7 +57,7 @@ public final class AfterLoginHandler extends AbstractMaplePacketHandler { c.announce(MaplePacketCreator.requestPinAfterFailure()); } } else if (c2 == 0 && c3 == 5) { - MapleSessionCoordinator.getInstance().closeSession(c.getSession(), null); + MapleSessionCoordinator.getInstance().closeSession(c, null); c.updateLoginState(MapleClient.LOGIN_NOTLOGGEDIN); } } diff --git a/src/main/java/net/server/handlers/login/CharSelectedHandler.java b/src/main/java/net/server/handlers/login/CharSelectedHandler.java index 9c927c3d1a..83d38a19dc 100644 --- a/src/main/java/net/server/handlers/login/CharSelectedHandler.java +++ b/src/main/java/net/server/handlers/login/CharSelectedHandler.java @@ -22,37 +22,28 @@ package net.server.handlers.login; import client.MapleClient; -import java.net.InetAddress; -import java.net.UnknownHostException; import net.AbstractMaplePacketHandler; import net.server.Server; import net.server.coordinator.session.Hwid; import net.server.coordinator.session.MapleSessionCoordinator; import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientResult; import net.server.world.World; -import org.apache.mina.core.session.IoSession; import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; +import java.net.InetAddress; +import java.net.UnknownHostException; + public final class CharSelectedHandler extends AbstractMaplePacketHandler { private static int parseAntiMulticlientError(AntiMulticlientResult res) { - switch (res) { - case REMOTE_PROCESSING: - return 10; - - case REMOTE_LOGGEDIN: - return 7; - - case REMOTE_NO_MATCH: - return 17; - - case COORDINATOR_ERROR: - return 8; - - default: - return 9; - } + return switch (res) { + case REMOTE_PROCESSING -> 10; + case REMOTE_LOGGEDIN -> 7; + case REMOTE_NO_MATCH -> 17; + case COORDINATOR_ERROR -> 8; + default -> 9; + }; } @Override @@ -69,22 +60,21 @@ public final class CharSelectedHandler extends AbstractMaplePacketHandler { c.updateMacs(macs); c.updateHwid(hwid); - - IoSession session = c.getSession(); - AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptGameSession(session, c.getAccID(), hwid); + + AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptGameSession(c, c.getAccID(), hwid); if (res != AntiMulticlientResult.SUCCESS) { c.announce(MaplePacketCreator.getAfterLoginError(parseAntiMulticlientError(res))); return; } if (c.hasBannedMac() || c.hasBannedHWID()) { - MapleSessionCoordinator.getInstance().closeSession(session, true); + MapleSessionCoordinator.getInstance().closeSession(c, true); return; } Server server = Server.getInstance(); if(!server.haveCharacterEntry(c.getAccID(), charId)) { - MapleSessionCoordinator.getInstance().closeSession(session, true); + MapleSessionCoordinator.getInstance().closeSession(c, true); return; } @@ -95,7 +85,7 @@ public final class CharSelectedHandler extends AbstractMaplePacketHandler { return; } - String[] socket = server.getInetSocket(session, c.getWorld(), c.getChannel()); + String[] socket = server.getInetSocket(c, c.getWorld(), c.getChannel()); if(socket == null) { c.announce(MaplePacketCreator.getAfterLoginError(10)); return; diff --git a/src/main/java/net/server/handlers/login/CharSelectedWithPicHandler.java b/src/main/java/net/server/handlers/login/CharSelectedWithPicHandler.java index ec7d9cbff3..e1fa8ec499 100644 --- a/src/main/java/net/server/handlers/login/CharSelectedWithPicHandler.java +++ b/src/main/java/net/server/handlers/login/CharSelectedWithPicHandler.java @@ -1,18 +1,17 @@ package net.server.handlers.login; -import java.net.InetAddress; -import java.net.UnknownHostException; - +import client.MapleClient; import net.AbstractMaplePacketHandler; import net.server.Server; import net.server.coordinator.session.Hwid; import net.server.coordinator.session.MapleSessionCoordinator; import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientResult; import net.server.world.World; -import org.apache.mina.core.session.IoSession; import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; -import client.MapleClient; + +import java.net.InetAddress; +import java.net.UnknownHostException; public class CharSelectedWithPicHandler extends AbstractMaplePacketHandler { @@ -50,17 +49,15 @@ public class CharSelectedWithPicHandler extends AbstractMaplePacketHandler { c.updateMacs(macs); c.updateHwid(hwid); - - IoSession session = c.getSession(); - + if (c.hasBannedMac() || c.hasBannedHWID()) { - MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true); + MapleSessionCoordinator.getInstance().closeSession(c, true); return; } Server server = Server.getInstance(); if(!server.haveCharacterEntry(c.getAccID(), charId)) { - MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true); + MapleSessionCoordinator.getInstance().closeSession(c, true); return; } @@ -72,13 +69,13 @@ public class CharSelectedWithPicHandler extends AbstractMaplePacketHandler { return; } - String[] socket = server.getInetSocket(session, c.getWorld(), c.getChannel()); + String[] socket = server.getInetSocket(c, c.getWorld(), c.getChannel()); if(socket == null) { c.announce(MaplePacketCreator.getAfterLoginError(10)); return; } - AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptGameSession(session, c.getAccID(), hwid); + AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptGameSession(c, c.getAccID(), hwid); if (res != AntiMulticlientResult.SUCCESS) { c.announce(MaplePacketCreator.getAfterLoginError(parseAntiMulticlientError(res))); return; diff --git a/src/main/java/net/server/handlers/login/RegisterPicHandler.java b/src/main/java/net/server/handlers/login/RegisterPicHandler.java index 161837f32c..e6aad9a99a 100644 --- a/src/main/java/net/server/handlers/login/RegisterPicHandler.java +++ b/src/main/java/net/server/handlers/login/RegisterPicHandler.java @@ -1,18 +1,17 @@ package net.server.handlers.login; -import java.net.InetAddress; -import java.net.UnknownHostException; - +import client.MapleClient; import net.AbstractMaplePacketHandler; import net.server.Server; import net.server.coordinator.session.Hwid; +import net.server.coordinator.session.MapleSessionCoordinator; +import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientResult; import net.server.world.World; import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; -import client.MapleClient; -import net.server.coordinator.session.MapleSessionCoordinator; -import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientResult; -import org.apache.mina.core.session.IoSession; + +import java.net.InetAddress; +import java.net.UnknownHostException; public final class RegisterPicHandler extends AbstractMaplePacketHandler { @@ -51,21 +50,20 @@ public final class RegisterPicHandler extends AbstractMaplePacketHandler { c.updateMacs(macs); c.updateHwid(hwid); - IoSession session = c.getSession(); - AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptGameSession(session, c.getAccID(), hwid); + AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptGameSession(c, c.getAccID(), hwid); if (res != AntiMulticlientResult.SUCCESS) { c.announce(MaplePacketCreator.getAfterLoginError(parseAntiMulticlientError(res))); return; } if (c.hasBannedMac() || c.hasBannedHWID()) { - MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true); + MapleSessionCoordinator.getInstance().closeSession(c, true); return; } Server server = Server.getInstance(); if(!server.haveCharacterEntry(c.getAccID(), charId)) { - MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true); + MapleSessionCoordinator.getInstance().closeSession(c, true); return; } @@ -80,7 +78,7 @@ public final class RegisterPicHandler extends AbstractMaplePacketHandler { return; } - String[] socket = server.getInetSocket(session, c.getWorld(), c.getChannel()); + String[] socket = server.getInetSocket(c, c.getWorld(), c.getChannel()); if(socket == null) { c.announce(MaplePacketCreator.getAfterLoginError(10)); return; @@ -95,7 +93,7 @@ public final class RegisterPicHandler extends AbstractMaplePacketHandler { e.printStackTrace(); } } else { - MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true); + MapleSessionCoordinator.getInstance().closeSession(c, true); } } } \ No newline at end of file diff --git a/src/main/java/net/server/handlers/login/RegisterPinHandler.java b/src/main/java/net/server/handlers/login/RegisterPinHandler.java index 65a3e667a9..82b8d9cb9d 100644 --- a/src/main/java/net/server/handlers/login/RegisterPinHandler.java +++ b/src/main/java/net/server/handlers/login/RegisterPinHandler.java @@ -35,7 +35,7 @@ public final class RegisterPinHandler extends AbstractMaplePacketHandler { public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { byte c2 = slea.readByte(); if (c2 == 0) { - MapleSessionCoordinator.getInstance().closeSession(c.getSession(), null); + MapleSessionCoordinator.getInstance().closeSession(c, null); c.updateLoginState(MapleClient.LOGIN_NOTLOGGEDIN); } else { String pin = slea.readMapleAsciiString(); @@ -43,7 +43,7 @@ public final class RegisterPinHandler extends AbstractMaplePacketHandler { c.setPin(pin); c.announce(MaplePacketCreator.pinRegistered()); - MapleSessionCoordinator.getInstance().closeSession(c.getSession(), null); + MapleSessionCoordinator.getInstance().closeSession(c, null); c.updateLoginState(MapleClient.LOGIN_NOTLOGGEDIN); } } diff --git a/src/main/java/net/server/handlers/login/SetGenderHandler.java b/src/main/java/net/server/handlers/login/SetGenderHandler.java index 3cf57342f8..c208b78946 100644 --- a/src/main/java/net/server/handlers/login/SetGenderHandler.java +++ b/src/main/java/net/server/handlers/login/SetGenderHandler.java @@ -44,7 +44,7 @@ public class SetGenderHandler extends AbstractMaplePacketHandler { Server.getInstance().registerLoginState(c); } else { - MapleSessionCoordinator.getInstance().closeSession(c.getSession(), null); + MapleSessionCoordinator.getInstance().closeSession(c, null); c.updateLoginState(MapleClient.LOGIN_NOTLOGGEDIN); } } diff --git a/src/main/java/net/server/handlers/login/ViewAllCharRegisterPicHandler.java b/src/main/java/net/server/handlers/login/ViewAllCharRegisterPicHandler.java index 88b4a76698..d0602672cd 100644 --- a/src/main/java/net/server/handlers/login/ViewAllCharRegisterPicHandler.java +++ b/src/main/java/net/server/handlers/login/ViewAllCharRegisterPicHandler.java @@ -1,19 +1,19 @@ package net.server.handlers.login; import client.MapleClient; -import java.net.InetAddress; -import java.net.UnknownHostException; import net.AbstractMaplePacketHandler; import net.server.Server; import net.server.coordinator.session.Hwid; import net.server.coordinator.session.MapleSessionCoordinator; import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientResult; import net.server.world.World; -import org.apache.mina.core.session.IoSession; import tools.MaplePacketCreator; import tools.Randomizer; import tools.data.input.SeekableLittleEndianAccessor; +import java.net.InetAddress; +import java.net.UnknownHostException; + public final class ViewAllCharRegisterPicHandler extends AbstractMaplePacketHandler { private static int parseAntiMulticlientError(AntiMulticlientResult res) { @@ -53,12 +53,11 @@ public final class ViewAllCharRegisterPicHandler extends AbstractMaplePacketHand c.updateHwid(hwid); if (c.hasBannedMac() || c.hasBannedHWID()) { - MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true); + MapleSessionCoordinator.getInstance().closeSession(c, true); return; } - - IoSession session = c.getSession(); - AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptGameSession(session, c.getAccID(), hwid); + + AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptGameSession(c, c.getAccID(), hwid); if (res != AntiMulticlientResult.SUCCESS) { c.announce(MaplePacketCreator.getAfterLoginError(parseAntiMulticlientError(res))); return; @@ -66,7 +65,7 @@ public final class ViewAllCharRegisterPicHandler extends AbstractMaplePacketHand Server server = Server.getInstance(); if(!server.haveCharacterEntry(c.getAccID(), charId)) { - MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true); + MapleSessionCoordinator.getInstance().closeSession(c, true); return; } @@ -83,7 +82,7 @@ public final class ViewAllCharRegisterPicHandler extends AbstractMaplePacketHand String pic = slea.readMapleAsciiString(); c.setPic(pic); - String[] socket = server.getInetSocket(session, c.getWorld(), channel); + String[] socket = server.getInetSocket(c, c.getWorld(), channel); if (socket == null) { c.announce(MaplePacketCreator.getAfterLoginError(10)); return; diff --git a/src/main/java/net/server/handlers/login/ViewAllCharSelectedHandler.java b/src/main/java/net/server/handlers/login/ViewAllCharSelectedHandler.java index 655db30415..a4c197ba88 100644 --- a/src/main/java/net/server/handlers/login/ViewAllCharSelectedHandler.java +++ b/src/main/java/net/server/handlers/login/ViewAllCharSelectedHandler.java @@ -22,19 +22,19 @@ package net.server.handlers.login; import client.MapleClient; -import java.net.InetAddress; -import java.net.UnknownHostException; import net.AbstractMaplePacketHandler; import net.server.Server; import net.server.coordinator.session.Hwid; import net.server.coordinator.session.MapleSessionCoordinator; import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientResult; import net.server.world.World; -import org.apache.mina.core.session.IoSession; import tools.MaplePacketCreator; import tools.Randomizer; import tools.data.input.SeekableLittleEndianAccessor; +import java.net.InetAddress; +import java.net.UnknownHostException; + public final class ViewAllCharSelectedHandler extends AbstractMaplePacketHandler { private static int parseAntiMulticlientError(AntiMulticlientResult res) { @@ -73,12 +73,11 @@ public final class ViewAllCharSelectedHandler extends AbstractMaplePacketHandler c.updateHwid(hwid); if (c.hasBannedMac() || c.hasBannedHWID()) { - MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true); + MapleSessionCoordinator.getInstance().closeSession(c, true); return; } - - IoSession session = c.getSession(); - AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptGameSession(session, c.getAccID(), hwid); + + AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptGameSession(c, c.getAccID(), hwid); if (res != AntiMulticlientResult.SUCCESS) { c.announce(MaplePacketCreator.getAfterLoginError(parseAntiMulticlientError(res))); return; @@ -86,7 +85,7 @@ public final class ViewAllCharSelectedHandler extends AbstractMaplePacketHandler Server server = Server.getInstance(); if(!server.haveCharacterEntry(c.getAccID(), charId)) { - MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true); + MapleSessionCoordinator.getInstance().closeSession(c, true); return; } @@ -106,7 +105,7 @@ public final class ViewAllCharSelectedHandler extends AbstractMaplePacketHandler c.setChannel(1); } - String[] socket = server.getInetSocket(session, c.getWorld(), c.getChannel()); + String[] socket = server.getInetSocket(c, c.getWorld(), c.getChannel()); if(socket == null) { c.announce(MaplePacketCreator.getAfterLoginError(10)); return; diff --git a/src/main/java/net/server/handlers/login/ViewAllCharSelectedWithPicHandler.java b/src/main/java/net/server/handlers/login/ViewAllCharSelectedWithPicHandler.java index 95e878b811..5476f2dbf3 100644 --- a/src/main/java/net/server/handlers/login/ViewAllCharSelectedWithPicHandler.java +++ b/src/main/java/net/server/handlers/login/ViewAllCharSelectedWithPicHandler.java @@ -1,19 +1,18 @@ package net.server.handlers.login; -import java.net.InetAddress; -import java.net.UnknownHostException; - +import client.MapleClient; import net.AbstractMaplePacketHandler; import net.server.Server; import net.server.coordinator.session.Hwid; +import net.server.coordinator.session.MapleSessionCoordinator; +import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientResult; import net.server.world.World; import tools.MaplePacketCreator; import tools.Randomizer; import tools.data.input.SeekableLittleEndianAccessor; -import client.MapleClient; -import net.server.coordinator.session.MapleSessionCoordinator; -import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientResult; -import org.apache.mina.core.session.IoSession; + +import java.net.InetAddress; +import java.net.UnknownHostException; public class ViewAllCharSelectedWithPicHandler extends AbstractMaplePacketHandler { @@ -55,15 +54,13 @@ public class ViewAllCharSelectedWithPicHandler extends AbstractMaplePacketHandle c.updateHwid(hwid); if (c.hasBannedMac() || c.hasBannedHWID()) { - MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true); + MapleSessionCoordinator.getInstance().closeSession(c, true); return; } - - IoSession session = c.getSession(); - + Server server = Server.getInstance(); if(!server.haveCharacterEntry(c.getAccID(), charId)) { - MapleSessionCoordinator.getInstance().closeSession(c.getSession(), true); + MapleSessionCoordinator.getInstance().closeSession(c, true); return; } @@ -78,13 +75,13 @@ public class ViewAllCharSelectedWithPicHandler extends AbstractMaplePacketHandle c.setChannel(channel); if (c.checkPic(pic)) { - String[] socket = server.getInetSocket(session, c.getWorld(), c.getChannel()); + String[] socket = server.getInetSocket(c, c.getWorld(), c.getChannel()); if(socket == null) { c.announce(MaplePacketCreator.getAfterLoginError(10)); return; } - AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptGameSession(session, c.getAccID(), hwid); + AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptGameSession(c, c.getAccID(), hwid); if (res != AntiMulticlientResult.SUCCESS) { c.announce(MaplePacketCreator.getAfterLoginError(parseAntiMulticlientError(res))); return; diff --git a/src/main/java/server/maps/MapleMiniGame.java b/src/main/java/server/maps/MapleMiniGame.java index bd4b00a1bb..4f383da923 100644 --- a/src/main/java/server/maps/MapleMiniGame.java +++ b/src/main/java/server/maps/MapleMiniGame.java @@ -156,7 +156,7 @@ public class MapleMiniGame extends AbstractMapleMapObject { public void broadcastToOwner(final byte[] packet) { MapleClient c = owner.getClient(); - if (c != null && c.getSession() != null) { + if (c != null) { c.announce(packet); } } diff --git a/src/main/java/server/maps/MaplePlayerShop.java b/src/main/java/server/maps/MaplePlayerShop.java index 2550bdf114..b2648d9854 100644 --- a/src/main/java/server/maps/MaplePlayerShop.java +++ b/src/main/java/server/maps/MaplePlayerShop.java @@ -386,8 +386,9 @@ public class MaplePlayerShop extends AbstractMapleMapObject { } public void broadcast(final byte[] packet) { - if (owner.getClient() != null && owner.getClient().getSession() != null) { - owner.getClient().announce(packet); + MapleClient client = owner.getClient(); + if (client != null) { + client.announce(packet); } broadcastToVisitors(packet); } From 3de827553ea6a88664e744f7ef1aa2127a87dec5 Mon Sep 17 00:00:00 2001 From: P0nk Date: Mon, 12 Jul 2021 19:19:12 +0200 Subject: [PATCH 23/37] Fix idle state disconnecting regardless of received pong --- src/main/java/client/MapleClient.java | 2 +- src/main/java/net/netty/ServerChannelInitializer.java | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/client/MapleClient.java b/src/main/java/client/MapleClient.java index 1eb5f0f71a..69bd115b09 100644 --- a/src/main/java/client/MapleClient.java +++ b/src/main/java/client/MapleClient.java @@ -1149,7 +1149,7 @@ public class MapleClient extends ChannelInboundHandlerAdapter { } public void pongReceived() { - lastPong = Server.getInstance().getCurrentTime(); + lastPong = System.currentTimeMillis(); } @Deprecated(forRemoval = true) diff --git a/src/main/java/net/netty/ServerChannelInitializer.java b/src/main/java/net/netty/ServerChannelInitializer.java index 493d47a61e..93dfde1751 100644 --- a/src/main/java/net/netty/ServerChannelInitializer.java +++ b/src/main/java/net/netty/ServerChannelInitializer.java @@ -12,6 +12,7 @@ import tools.MaplePacketCreator; import java.util.concurrent.atomic.AtomicLong; public abstract class ServerChannelInitializer extends ChannelInitializer { + private static final int IDLE_TIME_SECONDS = 30; static final AtomicLong sessionId = new AtomicLong(7777); void initPipeline(SocketChannel socketChannel, MapleClient client) { @@ -27,7 +28,7 @@ public abstract class ServerChannelInitializer extends ChannelInitializer Date: Tue, 13 Jul 2021 21:50:19 +0200 Subject: [PATCH 24/37] Able to log incoming and outgoing packets --- src/main/java/net/netty/PacketDecoder.java | 37 ++++++++++++++++++++-- src/main/java/net/netty/PacketEncoder.java | 37 ++++++++++++++++++++++ 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/netty/PacketDecoder.java b/src/main/java/net/netty/PacketDecoder.java index 79cc0fafb5..0c150f2527 100644 --- a/src/main/java/net/netty/PacketDecoder.java +++ b/src/main/java/net/netty/PacketDecoder.java @@ -1,16 +1,23 @@ package net.netty; +import config.YamlConfig; +import constants.net.OpcodeConstants; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ReplayingDecoder; import net.mina.MapleCustomEncryption; import net.packet.ByteBufInPacket; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import tools.HexTool; import tools.MapleAESOFB; import java.util.List; public class PacketDecoder extends ReplayingDecoder { + private static final Logger log = LoggerFactory.getLogger(PacketDecoder.class); + private static final boolean LOG_PACKETS = YamlConfig.config.server.USE_DEBUG_SHOW_PACKET; private final MapleAESOFB receiveCypher; public PacketDecoder(MapleAESOFB receiveCypher) { @@ -25,13 +32,16 @@ public class PacketDecoder extends ReplayingDecoder { throw new InvalidPacketHeaderException("Attempted to decode a packet with an invalid header", header); } - int packetLength = decodePacketLength(header); + final int packetLength = decodePacketLength(header); byte[] packet = new byte[packetLength]; in.readBytes(packet); receiveCypher.crypt(packet); MapleCustomEncryption.decryptData(packet); out.add(new ByteBufInPacket(Unpooled.wrappedBuffer(packet))); - // TODO conditionally log the packet + + if (LOG_PACKETS){ + logPacket(packet); + } } /** @@ -47,4 +57,27 @@ public class PacketDecoder extends ReplayingDecoder { length = ((length << 8) & 0xFF00) | ((length >>> 8) & 0xFF); return length; } + + private void logPacket(byte[] packet) { + final int packetLength = packet.length; + + if (packetLength <= 3000) { + final int opcode = readFirstShort(packet); + final String opcodeHex = Integer.toHexString(opcode).toUpperCase(); + final String opcodeName = getRecvOpcodeName(opcode); + final String prefix = opcodeName == null ? " " : ""; + log.info("{}ClientSend:{} [{}] ({}) - hex:{} - text:{}", prefix, opcodeName, opcodeHex, packetLength, + HexTool.toString(packet), HexTool.toStringFromAscii(packet)); + } else { + log.debug(HexTool.toString(new byte[]{packet[0], packet[1]}) + "..."); + } + } + + private static int readFirstShort(byte[] bytes) { + return new ByteBufInPacket(Unpooled.wrappedBuffer(bytes)).readShort(); + } + + private String getRecvOpcodeName(int opcode) { + return OpcodeConstants.recvOpcodeNames.get(opcode); + } } diff --git a/src/main/java/net/netty/PacketEncoder.java b/src/main/java/net/netty/PacketEncoder.java index 3d424822c9..2e6de8e4d7 100644 --- a/src/main/java/net/netty/PacketEncoder.java +++ b/src/main/java/net/netty/PacketEncoder.java @@ -1,13 +1,22 @@ package net.netty; +import config.YamlConfig; +import constants.net.OpcodeConstants; import io.netty.buffer.ByteBuf; +import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; import net.mina.MapleCustomEncryption; +import net.packet.ByteBufInPacket; import net.packet.OutPacket; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import tools.HexTool; import tools.MapleAESOFB; public class PacketEncoder extends MessageToByteEncoder { + private static final Logger log = LoggerFactory.getLogger(PacketEncoder.class); + private static final boolean LOG_PACKETS = YamlConfig.config.server.USE_DEBUG_SHOW_PACKET; private final MapleAESOFB sendCypher; public PacketEncoder(MapleAESOFB sendCypher) { @@ -18,6 +27,11 @@ public class PacketEncoder extends MessageToByteEncoder { protected void encode(ChannelHandlerContext ctx, OutPacket in, ByteBuf out) { byte[] packet = in.getBytes(); out.writeBytes(getEncodedHeader(packet.length)); + + if (LOG_PACKETS) { + logPacket(packet); + } + MapleCustomEncryption.encryptData(packet); sendCypher.crypt(packet); out.writeBytes(packet); @@ -26,4 +40,27 @@ public class PacketEncoder extends MessageToByteEncoder { private byte[] getEncodedHeader(int length) { return sendCypher.getPacketHeader(length); } + + private void logPacket(byte[] packet) { + final int packetLength = packet.length; + + if (packetLength <= 50000) { + final int opcode = readFirstShort(packet); + String opcodeHex = Integer.toHexString(opcode).toUpperCase(); + String opcodeName = getSendOpcodeName(opcode); + String prefix = opcodeName == null ? " " : ""; + log.info("{}ServerSend:{} [{}] ({}) - hex:{} - text:{}", prefix, opcodeName, opcodeHex, packetLength, + HexTool.toString(packet), HexTool.toStringFromAscii(packet)); + } else { + log.info(HexTool.toString(new byte[]{packet[0], packet[1]}) + " ..."); + } + } + + private String getSendOpcodeName(int opcode) { + return OpcodeConstants.sendOpcodeNames.get(opcode); + } + + private static int readFirstShort(byte[] bytes) { + return new ByteBufInPacket(Unpooled.wrappedBuffer(bytes)).readShort(); + } } From 94e1125ecaaed16285f6d4fc803b89f028b24cb0 Mon Sep 17 00:00:00 2001 From: P0nk Date: Tue, 13 Jul 2021 21:54:07 +0200 Subject: [PATCH 25/37] Move some remaining bits and pieces to netty implementation, start cleaning up --- src/main/java/client/MapleClient.java | 35 +- src/main/java/net/MapleServerHandler.java | 306 ------------------ .../net/netty/ChannelServerInitializer.java | 12 +- .../net/netty/LoginServerInitializer.java | 8 +- src/main/java/net/server/Server.java | 30 +- src/main/java/net/server/channel/Channel.java | 53 +-- .../session/MapleSessionCoordinator.java | 7 +- src/main/java/server/life/MaplePlayerNPC.java | 2 +- 8 files changed, 64 insertions(+), 389 deletions(-) delete mode 100644 src/main/java/net/MapleServerHandler.java diff --git a/src/main/java/client/MapleClient.java b/src/main/java/client/MapleClient.java index 69bd115b09..bd3f65956f 100644 --- a/src/main/java/client/MapleClient.java +++ b/src/main/java/client/MapleClient.java @@ -91,6 +91,8 @@ public class MapleClient extends ChannelInboundHandlerAdapter { public static final String CLIENT_NIBBLEHWID = "HWID2"; public static final String CLIENT_REMOTE_ADDRESS = "REMOTE_IP"; + private final Type type; + private Hwid hwid; private String remoteHwid; // Mac address + hwid in one. Retrieved from client when attempting to enter game. private String remoteAddress; @@ -136,6 +138,11 @@ public class MapleClient extends ChannelInboundHandlerAdapter { private long lastPacket = System.currentTimeMillis(); private int lang = 0; + public enum Type { + LOGIN, + CHANNEL + } + public void updateLastPacket() { lastPacket = System.currentTimeMillis(); } @@ -144,17 +151,23 @@ public class MapleClient extends ChannelInboundHandlerAdapter { return lastPacket; } - public MapleClient(PacketProcessor packetProcessor, int world, int channel) { + public MapleClient(Type type, PacketProcessor packetProcessor, int world, int channel) { + this.type = type; this.packetProcessor = packetProcessor; this.world = world; this.channel = channel; } public MapleClient(MapleAESOFB send, MapleAESOFB receive, IoSession session) { + this.type = null; this.send = send; this.receive = receive; } + public static MapleClient createMock() { + return new MapleClient(null, null, -123, -123); + } + @Override public void channelActive(ChannelHandlerContext ctx) { final io.netty.channel.Channel channel = ctx.channel(); @@ -209,7 +222,6 @@ public class MapleClient extends ChannelInboundHandlerAdapter { } updateLastPacket(); - super.channelRead(ctx, msg); } @Override @@ -226,18 +238,23 @@ public class MapleClient extends ChannelInboundHandlerAdapter { } if (cause instanceof InvalidPacketHeaderException) { - // TODO close session through MapleSessionCoordinator + MapleSessionCoordinator.getInstance().closeSession(this, true); } else if (cause instanceof IOException) { - closeSession(); - } else { - + closeMapleSession(); } - - super.exceptionCaught(ctx, cause); } @Override public void channelInactive(ChannelHandlerContext ctx) { + closeMapleSession(); + } + + private void closeMapleSession() { + switch (type) { + case LOGIN -> MapleSessionCoordinator.getInstance().closeLoginSession(this); + case CHANNEL -> MapleSessionCoordinator.getInstance().closeSession(this, null); + } + try { // client freeze issues on session transition states found thanks to yolinlin, Omo Oppa, Nozphex if (!inTransition) { @@ -245,6 +262,8 @@ public class MapleClient extends ChannelInboundHandlerAdapter { } } catch (Throwable t) { log.warn("Account stuck", t); + } finally { + closeSession(); } } diff --git a/src/main/java/net/MapleServerHandler.java b/src/main/java/net/MapleServerHandler.java deleted file mode 100644 index bb9cec2692..0000000000 --- a/src/main/java/net/MapleServerHandler.java +++ /dev/null @@ -1,306 +0,0 @@ -/* -This file is part of the OdinMS Maple Story Server -Copyright (C) 2008 Patrick Huy -Matthias Butz -Jan Christian Meyer - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as -published by the Free Software Foundation version 3 as published by -the Free Software Foundation. You may not use, modify or distribute -this program under any other version of the GNU Affero General Public -License. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with this program. If not, see . - */ -package net; - -import client.MapleClient; -import config.YamlConfig; -import constants.net.ServerConstants; -import net.netty.InitializationVector; -import net.server.Server; -import net.server.audit.LockCollector; -import net.server.audit.locks.MonitoredLockType; -import net.server.audit.locks.MonitoredReentrantLock; -import net.server.audit.locks.factory.MonitoredReentrantLockFactory; -import net.server.coordinator.session.IpAddresses; -import net.server.coordinator.session.MapleSessionCoordinator; -import org.apache.mina.core.service.IoHandlerAdapter; -import org.apache.mina.core.session.IdleStatus; -import org.apache.mina.core.session.IoSession; -import server.TimerManager; -import tools.FilePrinter; -import tools.MapleAESOFB; -import tools.MapleLogger; -import tools.MaplePacketCreator; -import tools.data.input.ByteArrayByteStream; -import tools.data.input.GenericSeekableLittleEndianAccessor; -import tools.data.input.SeekableLittleEndianAccessor; - -import java.io.IOException; -import java.net.InetSocketAddress; -import java.text.SimpleDateFormat; -import java.util.*; -import java.util.Map.Entry; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.atomic.AtomicLong; - -public class MapleServerHandler extends IoHandlerAdapter { - private final static Set ignoredDebugRecvPackets = new HashSet<>(Arrays.asList((short) 167, (short) 197, (short) 89, (short) 91, (short) 41, (short) 188, (short) 107)); - - private PacketProcessor processor; - private int world = -1, channel = -1; - private static final SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy HH:mm"); - private static AtomicLong sessionId = new AtomicLong(7777); - - private MonitoredReentrantLock idleLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.SRVHANDLER_IDLE, true); - private MonitoredReentrantLock tempLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.SRVHANDLER_TEMP, true); - private Map idleSessions = new HashMap<>(100); - private Map tempIdleSessions = new HashMap<>(); - private ScheduledFuture idleManager = null; - - public MapleServerHandler() { - this.processor = PacketProcessor.getProcessor(-1, -1); - - idleManagerTask(); - } - - public MapleServerHandler(int world, int channel) { - this.processor = PacketProcessor.getProcessor(world, channel); - this.world = world; - this.channel = channel; - - idleManagerTask(); - } - - @Override - public void exceptionCaught(IoSession session, Throwable cause) { - if (cause instanceof IOException) { - closeMapleSession(session); - } else { - MapleClient client = (MapleClient) session.getAttribute(MapleClient.CLIENT_KEY); - - if (client != null && client.getPlayer() != null) { - FilePrinter.printError(FilePrinter.EXCEPTION_CAUGHT, cause, "Exception caught by: " + client.getPlayer()); - } - } - } - - private boolean isLoginServerHandler() { - return channel == -1 && world == -1; - } - - @Override - public void sessionOpened(IoSession session) { - String remoteHost; - try { - remoteHost = ((InetSocketAddress) session.getRemoteAddress()).getAddress().getHostAddress(); - - if (remoteHost == null) { - remoteHost = "null"; - } else { - remoteHost = IpAddresses.evaluateRemoteAddress(remoteHost); // thanks dyz for noticing Local/LAN/WAN connections not interacting properly - } - } catch (NullPointerException npe) { // thanks Agassy, Alchemist for pointing out possibility of remoteHost = null. - remoteHost = "null"; - } - - session.setAttribute(MapleClient.CLIENT_REMOTE_ADDRESS, remoteHost); - - if (!Server.getInstance().isOnline()) { - MapleSessionCoordinator.getInstance().closeSession(MapleClient.getPlaceholder(), true); - return; - } - - final InitializationVector ivSend = InitializationVector.generateSend(); - final InitializationVector ivRecv = InitializationVector.generateReceive(); - MapleAESOFB sendCypher = new MapleAESOFB(ivSend, (short) (0xFFFF - ServerConstants.VERSION)); - MapleAESOFB recvCypher = new MapleAESOFB(ivRecv, ServerConstants.VERSION); - MapleClient client = new MapleClient(sendCypher, recvCypher, session); - - if (!isLoginServerHandler()) { - if (Server.getInstance().getChannel(world, channel) == null) { - MapleSessionCoordinator.getInstance().closeSession(MapleClient.getPlaceholder(), true); - return; - } - } else { - if (!MapleSessionCoordinator.getInstance().canStartLoginSession(client)) { - return; - } - - FilePrinter.print(FilePrinter.SESSION, "IoSession with " + session.getRemoteAddress() + " opened on " + sdf.format(Calendar.getInstance().getTime()), false); - } - - client.setWorld(world); - client.setChannel(channel); - client.setSessionId(sessionId.getAndIncrement()); // Generates a reasonable session id. - session.write(MaplePacketCreator.getHello(ServerConstants.VERSION, ivSend, ivRecv)); - session.setAttribute(MapleClient.CLIENT_KEY, client); - } - - private void closeMapleSession(IoSession session) { - if (isLoginServerHandler()) { - MapleSessionCoordinator.getInstance().closeLoginSession(MapleClient.getPlaceholder()); - } else { - MapleSessionCoordinator.getInstance().closeSession(MapleClient.getPlaceholder(), null); - } - - MapleClient client = (MapleClient) session.getAttribute(MapleClient.CLIENT_KEY); - if (client != null) { - try { - // client freeze issues on session transition states found thanks to yolinlin, Omo Oppa, Nozphex - if (!client.isInTransition()) { - client.disconnect(false, false); - } - } catch (Throwable t) { - FilePrinter.printError(FilePrinter.ACCOUNT_STUCK, t); - } finally { - session.close(); - session.removeAttribute(MapleClient.CLIENT_KEY); - //client.empty(); - } - } - } - - @Override - public void sessionClosed(IoSession session) throws Exception { - closeMapleSession(session); - super.sessionClosed(session); - } - - @Override - public void messageReceived(IoSession session, Object message) { - byte[] content = (byte[]) message; - SeekableLittleEndianAccessor slea = new GenericSeekableLittleEndianAccessor(new ByteArrayByteStream(content)); - short packetId = slea.readShort(); - MapleClient client = (MapleClient) session.getAttribute(MapleClient.CLIENT_KEY); - - if(YamlConfig.config.server.USE_DEBUG_SHOW_RCVD_PACKET && !ignoredDebugRecvPackets.contains(packetId)) System.out.println("Received packet id " + packetId); - final MaplePacketHandler packetHandler = processor.getHandler(packetId); - if (packetHandler != null && packetHandler.validateState(client)) { - try { - MapleLogger.logRecv(client, packetId, message); - packetHandler.handlePacket(slea, client); - } catch (final Throwable t) { - FilePrinter.printError(FilePrinter.PACKET_HANDLER + packetHandler.getClass().getName() + ".txt", t, "Error for " + (client.getPlayer() == null ? "" : "player ; " + client.getPlayer() + " on map ; " + client.getPlayer().getMapId() + " - ") + "account ; " + client.getAccountName() + "\r\n" + slea.toString()); - //client.announce(MaplePacketCreator.enableActions());//bugs sometimes - } - client.updateLastPacket(); - } - } - - @Override - public void messageSent(IoSession session, Object message) { - byte[] content = (byte[]) message; - SeekableLittleEndianAccessor slea = new GenericSeekableLittleEndianAccessor(new ByteArrayByteStream(content)); - slea.readShort(); //packetId - } - - @Override - public void sessionIdle(final IoSession session, final IdleStatus status) throws Exception { - MapleClient client = (MapleClient) session.getAttribute(MapleClient.CLIENT_KEY); - if (client != null) { - registerIdleSession(client); - } - super.sessionIdle(session, status); - } - - private void registerIdleSession(MapleClient c) { - if(idleLock.tryLock()) { - try { - idleSessions.put(c, Server.getInstance().getCurrentTime()); - c.announce(MaplePacketCreator.getPing()); - } finally { - idleLock.unlock(); - } - } else { - tempLock.lock(); - try { - tempIdleSessions.put(c, Server.getInstance().getCurrentTime()); - c.announce(MaplePacketCreator.getPing()); - } finally { - tempLock.unlock(); - } - } - } - - private void manageIdleSessions() { - long timeNow = Server.getInstance().getCurrentTime(); - long timeThen = timeNow - 15000; - - Set pingClients = new HashSet<>(); - idleLock.lock(); - try { - for(Entry mc : idleSessions.entrySet()) { - if(timeNow - mc.getValue() >= 15000) { - pingClients.add(mc.getKey()); - } - } - idleSessions.clear(); - - if(!tempIdleSessions.isEmpty()) { - tempLock.lock(); - try { - for(Entry mc : tempIdleSessions.entrySet()) { - idleSessions.put(mc.getKey(), mc.getValue()); - } - - tempIdleSessions.clear(); - } finally { - tempLock.unlock(); - } - } - } finally { - idleLock.unlock(); - } - - for(MapleClient c : pingClients) { - c.testPing(timeThen); - } - } - - private void idleManagerTask() { - this.idleManager = TimerManager.getInstance().register(() -> manageIdleSessions(), 10000); - } - - private void cancelIdleManagerTask() { - this.idleManager.cancel(false); - this.idleManager = null; - } - - private void disposeLocks() { - LockCollector.getInstance().registerDisposeAction(() -> emptyLocks()); - } - - private void emptyLocks() { - idleLock.dispose(); - tempLock.dispose(); - } - - public void dispose() { - cancelIdleManagerTask(); - - idleLock.lock(); - try { - idleSessions.clear(); - } finally { - idleLock.unlock(); - } - - tempLock.lock(); - try { - tempIdleSessions.clear(); - } finally { - tempLock.unlock(); - } - - disposeLocks(); - } -} diff --git a/src/main/java/net/netty/ChannelServerInitializer.java b/src/main/java/net/netty/ChannelServerInitializer.java index 49887268bc..5b56d92638 100644 --- a/src/main/java/net/netty/ChannelServerInitializer.java +++ b/src/main/java/net/netty/ChannelServerInitializer.java @@ -3,6 +3,8 @@ package net.netty; import client.MapleClient; import io.netty.channel.socket.SocketChannel; import net.PacketProcessor; +import net.server.Server; +import net.server.coordinator.session.MapleSessionCoordinator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -20,12 +22,18 @@ public class ChannelServerInitializer extends ServerChannelInitializer { @Override public void initChannel(SocketChannel socketChannel) { final String clientIp = socketChannel.remoteAddress().getHostName(); - log.debug("Client connected to world {}, channel {} from {}", world, channel, clientIp); + log.debug("Client connecting to world {}, channel {} from {}", world, channel, clientIp); PacketProcessor packetProcessor = PacketProcessor.getChannelServerProcessor(world, channel); - final MapleClient client = new MapleClient(packetProcessor, world, channel); + final MapleClient client = new MapleClient(MapleClient.Type.CHANNEL, packetProcessor, world, channel); client.setSessionId(sessionId.getAndIncrement()); + if (Server.getInstance().getChannel(world, channel) == null) { + MapleSessionCoordinator.getInstance().closeSession(client, true); + socketChannel.close(); + return; + } + initPipeline(socketChannel, client); } } diff --git a/src/main/java/net/netty/LoginServerInitializer.java b/src/main/java/net/netty/LoginServerInitializer.java index 592589861b..1d9795e011 100644 --- a/src/main/java/net/netty/LoginServerInitializer.java +++ b/src/main/java/net/netty/LoginServerInitializer.java @@ -3,6 +3,7 @@ package net.netty; import client.MapleClient; import io.netty.channel.socket.SocketChannel; import net.PacketProcessor; +import net.server.coordinator.session.MapleSessionCoordinator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -15,9 +16,14 @@ public class LoginServerInitializer extends ServerChannelInitializer { log.debug("Client connected to login server from {} ", clientIp); PacketProcessor packetProcessor = PacketProcessor.getLoginServerProcessor(); - final MapleClient client = new MapleClient(packetProcessor, LoginServer.WORLD_ID, LoginServer.CHANNEL_ID); + final MapleClient client = new MapleClient(MapleClient.Type.LOGIN, packetProcessor, LoginServer.WORLD_ID, LoginServer.CHANNEL_ID); client.setSessionId(sessionId.getAndIncrement()); + if (!MapleSessionCoordinator.getInstance().canStartLoginSession(client)) { + socketChannel.close(); + return; + } + initPipeline(socketChannel, client); } } diff --git a/src/main/java/net/server/Server.java b/src/main/java/net/server/Server.java index 4eac0d0c50..fe515cef9c 100644 --- a/src/main/java/net/server/Server.java +++ b/src/main/java/net/server/Server.java @@ -35,8 +35,6 @@ import constants.game.GameConstants; import constants.inventory.ItemConstants; import constants.net.OpcodeConstants; import constants.net.ServerConstants; -import net.MapleServerHandler; -import net.mina.MapleCodecFactory; import net.netty.LoginServer; import net.server.audit.ThreadTracker; import net.server.audit.locks.MonitoredLockType; @@ -54,12 +52,6 @@ import net.server.guild.MapleGuild; import net.server.guild.MapleGuildCharacter; import net.server.task.*; import net.server.world.World; -import org.apache.mina.core.buffer.IoBuffer; -import org.apache.mina.core.buffer.SimpleBufferAllocator; -import org.apache.mina.core.service.IoAcceptor; -import org.apache.mina.core.session.IdleStatus; -import org.apache.mina.filter.codec.ProtocolCodecFilter; -import org.apache.mina.transport.socket.nio.NioSocketAcceptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import server.CashShop.CashItemFactory; @@ -74,8 +66,6 @@ import tools.DatabaseConnection; import tools.FilePrinter; import tools.Pair; -import java.io.IOException; -import java.net.InetSocketAddress; import java.security.Security; import java.sql.Connection; import java.sql.PreparedStatement; @@ -106,7 +96,6 @@ public class Server { private static final Map couponRates = new HashMap<>(30); private static final List activeCoupons = new LinkedList<>(); - private IoAcceptor acceptor; private LoginServer loginServer; private List> channels = new LinkedList<>(); private List worlds = new ArrayList<>(); @@ -931,22 +920,6 @@ public class Server { return loginServer; } - private IoAcceptor initAcceptor(int port) { - IoBuffer.setUseDirectBuffer(false); // join IO operations performed by lxconan - IoBuffer.setAllocator(new SimpleBufferAllocator()); - acceptor = new NioSocketAcceptor(); - acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new MapleCodecFactory())); - acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 30); - acceptor.setHandler(new MapleServerHandler()); - try { - acceptor.bind(new InetSocketAddress(port)); - } catch (IOException ex) { - ex.printStackTrace(); - } - - return acceptor; - } - private static void setAllLoggedOut(Connection con) throws SQLException { try (PreparedStatement ps = con.prepareStatement("UPDATE accounts SET loggedin = 0")) { ps.executeUpdate(); @@ -1958,8 +1931,7 @@ public class Server { TimerManager.getInstance().stop(); System.out.println("Worlds + Channels are offline."); - acceptor.unbind(); - acceptor = null; + loginServer.stop(); if (!restart) { // shutdown hook deadlocks if System.exit() method is used within its body chores, thanks MIKE for pointing that out new Thread(() -> System.exit(0)).start(); } else { diff --git a/src/main/java/net/server/channel/Channel.java b/src/main/java/net/server/channel/Channel.java index 2201f0292d..d90e46c64a 100644 --- a/src/main/java/net/server/channel/Channel.java +++ b/src/main/java/net/server/channel/Channel.java @@ -24,8 +24,6 @@ package net.server.channel; import client.MapleCharacter; import config.YamlConfig; import constants.game.GameConstants; -import net.MapleServerHandler; -import net.mina.MapleCodecFactory; import net.netty.ChannelServer; import net.server.PlayerStorage; import net.server.Server; @@ -40,13 +38,6 @@ import net.server.services.type.ChannelServices; import net.server.world.MapleParty; import net.server.world.MaplePartyCharacter; import net.server.world.World; -import org.apache.mina.core.buffer.IoBuffer; -import org.apache.mina.core.buffer.SimpleBufferAllocator; -import org.apache.mina.core.service.IoAcceptor; -import org.apache.mina.core.session.IdleStatus; -import org.apache.mina.filter.codec.ProtocolCodecFilter; -import org.apache.mina.transport.socket.SocketSessionConfig; -import org.apache.mina.transport.socket.nio.NioSocketAcceptor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import scripting.event.EventScriptManager; @@ -59,21 +50,22 @@ import tools.MaplePacketCreator; import tools.Pair; import java.io.File; -import java.io.IOException; -import java.net.InetSocketAddress; import java.util.*; import java.util.Map.Entry; import java.util.concurrent.ScheduledFuture; public final class Channel { private static final Logger log = LoggerFactory.getLogger(Channel.class); + private static final int BASE_PORT = 7575; + + private final int port; + private final String ip; + private final int world; + private final int channel; - private int port = 7575; private PlayerStorage players = new PlayerStorage(); - private int world, channel; private ChannelServer channelServer; - private IoAcceptor acceptor; - private String ip, serverMessage; + private String serverMessage; private MapleMapManager mapManager; private EventScriptManager eventSM; private ServicesManager services; @@ -121,12 +113,11 @@ public final class Channel { this.ongoingStartTime = startTime + 10000; // rude approach to a world's last channel boot time, placeholder for the 1st wedding reservation ever this.mapManager = new MapleMapManager(null, world, channel); + this.port = BASE_PORT + (this.channel - 1) + (world * 100); + this.ip = YamlConfig.config.server.HOST + ":" + port; + try { - port = 7575 + this.channel - 1; - port += (world * 100); - ip = YamlConfig.config.server.HOST + ":" + port; - // acceptor = initAcceptor(); - channelServer = initServer(port, world, channel); + this.channelServer = initServer(port, world, channel); expedType.addAll(Arrays.asList(MapleExpeditionType.values())); if (Server.getInstance().isOnline()) { // postpone event loading to improve boot time... thanks Riizade, daronhudson for noticing slow startup times @@ -150,24 +141,12 @@ public final class Channel { log.info("Channel {}: Listening on port {}", getId(), port); } catch (Exception e) { - e.printStackTrace(); + log.warn("Error during channel initialization", e); } } - private IoAcceptor initAcceptor() throws IOException { - IoBuffer.setUseDirectBuffer(false); - IoBuffer.setAllocator(new SimpleBufferAllocator()); - IoAcceptor acceptor = new NioSocketAcceptor(); - acceptor.setHandler(new MapleServerHandler(world, channel)); - acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 30); - acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new MapleCodecFactory())); - acceptor.bind(new InetSocketAddress(port)); - ((SocketSessionConfig) acceptor.getSessionConfig()).setTcpNoDelay(true); - return acceptor; - } - private ChannelServer initServer(int port, int world, int channel) { - this.channelServer = new ChannelServer(port, world, channel); + ChannelServer channelServer = new ChannelServer(port, world, channel); channelServer.start(); return channelServer; } @@ -202,10 +181,8 @@ public final class Channel { closeChannelSchedules(); players = null; - - MapleServerHandler handler = (MapleServerHandler) acceptor.getHandler(); - handler.dispose(); - acceptor.unbind(); + + channelServer.stop(); finishedShutdown = true; System.out.println("Successfully shut down Channel " + channel + " on World " + world + "\r\n"); diff --git a/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java b/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java index 314b212da2..7af767f5b3 100644 --- a/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java +++ b/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java @@ -295,7 +295,7 @@ public class MapleSessionCoordinator { client.setHwid(Hwid.fromClientString(remoteHwid)); } - MapleClient fakeClient = new MapleClient(null, null, null); + MapleClient fakeClient = MapleClient.createMock(); Integer chrId = Server.getInstance().freeCharacteridInTransition(client); if (chrId != null) { try { @@ -313,15 +313,14 @@ public class MapleSessionCoordinator { client = fetchInTransitionSessionClient(client); } - Hwid hwid = client.getHwid(); + final Hwid hwid = client.getHwid(); client.setHwid(null); // making sure to clean up calls to this function on login phase if (hwid != null) { onlineRemoteHwids.remove(hwid.hwid()); } - String remoteHwid = client.getRemoteHwid(); + final String remoteHwid = client.getRemoteHwid(); client.setRemoteHwid(null); - // TODO: client.setHwid(null); if (remoteHwid != null) { onlineRemoteHwids.remove(remoteHwid); } diff --git a/src/main/java/server/life/MaplePlayerNPC.java b/src/main/java/server/life/MaplePlayerNPC.java index 5c801fccf7..8c5c8d27b5 100644 --- a/src/main/java/server/life/MaplePlayerNPC.java +++ b/src/main/java/server/life/MaplePlayerNPC.java @@ -588,7 +588,7 @@ public class MaplePlayerNPC extends AbstractMapleMapObject { World wserv = Server.getInstance().getWorld(world); if (wserv == null) return; - MapleClient c = new MapleClient(null, null, null); // mock client + MapleClient c = MapleClient.createMock(); c.setWorld(world); c.setChannel(1); From 81393392abb55c62a9d005bef9a54261ecbbb9ae Mon Sep 17 00:00:00 2001 From: P0nk Date: Tue, 13 Jul 2021 22:10:01 +0200 Subject: [PATCH 26/37] Clean up Apache Mina, goodbye! --- pom.xml | 5 - src/main/java/client/MapleCharacter.java | 4 +- src/main/java/client/MapleClient.java | 30 ----- .../net/{mina => }/MapleCustomEncryption.java | 2 +- src/main/java/net/mina/MapleCodecFactory.java | 47 -------- .../java/net/mina/MaplePacketDecoder.java | 105 ------------------ .../java/net/mina/MaplePacketEncoder.java | 97 ---------------- src/main/java/net/netty/PacketDecoder.java | 2 +- src/main/java/net/netty/PacketEncoder.java | 2 +- 9 files changed, 5 insertions(+), 289 deletions(-) rename src/main/java/net/{mina => }/MapleCustomEncryption.java (99%) delete mode 100644 src/main/java/net/mina/MapleCodecFactory.java delete mode 100644 src/main/java/net/mina/MaplePacketDecoder.java delete mode 100644 src/main/java/net/mina/MaplePacketEncoder.java diff --git a/pom.xml b/pom.xml index 8052282e74..ac8592c6ee 100644 --- a/pom.xml +++ b/pom.xml @@ -46,11 +46,6 @@ - - org.apache.mina - mina-core - 2.1.3 - io.netty netty-transport diff --git a/src/main/java/client/MapleCharacter.java b/src/main/java/client/MapleCharacter.java index f3fc390ed5..5434e00e2d 100644 --- a/src/main/java/client/MapleCharacter.java +++ b/src/main/java/client/MapleCharacter.java @@ -52,7 +52,6 @@ import net.server.services.task.world.CharacterSaveService; import net.server.services.type.ChannelServices; import net.server.services.type.WorldServices; import net.server.world.*; -import org.apache.mina.util.ConcurrentHashSet; import scripting.AbstractPlayerInteraction; import scripting.event.EventInstanceManager; import scripting.item.ItemScriptManager; @@ -85,6 +84,7 @@ import java.time.LocalDateTime; import java.util.List; import java.util.*; import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; @@ -177,7 +177,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject { private final Map quests; private Set controlled = new LinkedHashSet<>(); private Map entered = new LinkedHashMap<>(); - private Set visibleMapObjects = new ConcurrentHashSet<>(); + private Set visibleMapObjects = Collections.newSetFromMap(new ConcurrentHashMap<>()); private Map skills = new LinkedHashMap<>(); private Map activeCoupons = new LinkedHashMap<>(); private Map activeCouponRates = new LinkedHashMap<>(); diff --git a/src/main/java/client/MapleClient.java b/src/main/java/client/MapleClient.java index bd3f65956f..380dd9ce65 100644 --- a/src/main/java/client/MapleClient.java +++ b/src/main/java/client/MapleClient.java @@ -45,7 +45,6 @@ import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientRes import net.server.guild.MapleGuild; import net.server.guild.MapleGuildCharacter; import net.server.world.*; -import org.apache.mina.core.session.IoSession; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import scripting.AbstractPlayerInteraction; @@ -86,10 +85,6 @@ public class MapleClient extends ChannelInboundHandlerAdapter { public static final int LOGIN_NOTLOGGEDIN = 0; public static final int LOGIN_SERVER_TRANSITION = 1; public static final int LOGIN_LOGGEDIN = 2; - public static final String CLIENT_KEY = "CLIENT"; - public static final String CLIENT_HWID = "HWID"; - public static final String CLIENT_NIBBLEHWID = "HWID2"; - public static final String CLIENT_REMOTE_ADDRESS = "REMOTE_IP"; private final Type type; @@ -98,9 +93,6 @@ public class MapleClient extends ChannelInboundHandlerAdapter { private String remoteAddress; private volatile boolean inTransition; - private MapleAESOFB send; - private MapleAESOFB receive; - private io.netty.channel.Channel ioChannel; private PacketProcessor packetProcessor; private MapleCharacter player; @@ -158,12 +150,6 @@ public class MapleClient extends ChannelInboundHandlerAdapter { this.channel = channel; } - public MapleClient(MapleAESOFB send, MapleAESOFB receive, IoSession session) { - this.type = null; - this.send = send; - this.receive = receive; - } - public static MapleClient createMock() { return new MapleClient(null, null, -123, -123); } @@ -275,14 +261,6 @@ public class MapleClient extends ChannelInboundHandlerAdapter { ioChannel.disconnect(); } - public MapleAESOFB getReceiveCrypto() { - return receive; - } - - public MapleAESOFB getSendCrypto() { - return send; - } - public Hwid getHwid() { return hwid; } @@ -1097,9 +1075,6 @@ public class MapleClient extends ChannelInboundHandlerAdapter { this.birthday = null; this.engines = null; this.player = null; - this.receive = null; - this.send = null; - //this.session = null; } public void setCharacterOnSessionTransitionState(int cid) { @@ -1171,11 +1146,6 @@ public class MapleClient extends ChannelInboundHandlerAdapter { lastPong = System.currentTimeMillis(); } - @Deprecated(forRemoval = true) - public void testPing(long timeThen) { - throw new UnsupportedOperationException(); - } - public void checkIfIdle(final IdleStateEvent event) { final long pingedAt = System.currentTimeMillis(); announce(MaplePacketCreator.getPing()); diff --git a/src/main/java/net/mina/MapleCustomEncryption.java b/src/main/java/net/MapleCustomEncryption.java similarity index 99% rename from src/main/java/net/mina/MapleCustomEncryption.java rename to src/main/java/net/MapleCustomEncryption.java index 93f0a1b121..e6063279b9 100644 --- a/src/main/java/net/mina/MapleCustomEncryption.java +++ b/src/main/java/net/MapleCustomEncryption.java @@ -19,7 +19,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net.mina; +package net; public class MapleCustomEncryption { private static byte rollLeft(byte in, int count) { diff --git a/src/main/java/net/mina/MapleCodecFactory.java b/src/main/java/net/mina/MapleCodecFactory.java deleted file mode 100644 index 9ca3b76bd1..0000000000 --- a/src/main/java/net/mina/MapleCodecFactory.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - This file is part of the OdinMS Maple Story Server - Copyright (C) 2008 Patrick Huy - Matthias Butz - Jan Christian Meyer - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as - published by the Free Software Foundation version 3 as published by - the Free Software Foundation. You may not use, modify or distribute - this program under any other version of the GNU Affero General Public - License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ -package net.mina; - -import org.apache.mina.core.session.IoSession; -import org.apache.mina.filter.codec.ProtocolCodecFactory; -import org.apache.mina.filter.codec.ProtocolDecoder; -import org.apache.mina.filter.codec.ProtocolEncoder; - -public class MapleCodecFactory implements ProtocolCodecFactory { - private final ProtocolEncoder encoder; - private final ProtocolDecoder decoder; - - public MapleCodecFactory() { - encoder = new MaplePacketEncoder(); - decoder = new MaplePacketDecoder(); - } - - @Override - public ProtocolEncoder getEncoder(IoSession session) throws Exception { - return encoder; - } - - @Override - public ProtocolDecoder getDecoder(IoSession session) throws Exception { - return decoder; - } -} diff --git a/src/main/java/net/mina/MaplePacketDecoder.java b/src/main/java/net/mina/MaplePacketDecoder.java deleted file mode 100644 index 735160b797..0000000000 --- a/src/main/java/net/mina/MaplePacketDecoder.java +++ /dev/null @@ -1,105 +0,0 @@ -/* - This file is part of the OdinMS Maple Story Server - Copyright (C) 2008 Patrick Huy - Matthias Butz - Jan Christian Meyer - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License as - published by the Free Software Foundation version 3 as published by - the Free Software Foundation. You may not use, modify or distribute - this program under any other version of the GNU Affero General Public - License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . -*/ -package net.mina; - -import client.MapleClient; -import config.YamlConfig; -import constants.net.OpcodeConstants; -import net.server.coordinator.session.MapleSessionCoordinator; -import org.apache.mina.core.buffer.IoBuffer; -import org.apache.mina.core.session.IoSession; -import org.apache.mina.filter.codec.CumulativeProtocolDecoder; -import org.apache.mina.filter.codec.ProtocolDecoderOutput; -import tools.FilePrinter; -import tools.HexTool; -import tools.MapleAESOFB; -import tools.data.input.ByteArrayByteStream; -import tools.data.input.GenericLittleEndianAccessor; - -public class MaplePacketDecoder extends CumulativeProtocolDecoder { - private static final String DECODER_STATE_KEY = MaplePacketDecoder.class.getName() + ".STATE"; - - private static class DecoderState { - public int packetlength = -1; - } - - @Override - protected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception { - final MapleClient client = (MapleClient) session.getAttribute(MapleClient.CLIENT_KEY); - if(client == null) { - MapleSessionCoordinator.getInstance().closeSession(MapleClient.getPlaceholder(), true); - return false; - } - - DecoderState decoderState = (DecoderState) session.getAttribute(DECODER_STATE_KEY); - if (decoderState == null) { - decoderState = new DecoderState(); - session.setAttribute(DECODER_STATE_KEY, decoderState); - } - - MapleAESOFB rcvdCrypto = client.getReceiveCrypto(); - if (in.remaining() >= 4 && decoderState.packetlength == -1) { - int packetHeader = in.getInt(); - if (!rcvdCrypto.isValidHeader(packetHeader)) { - MapleSessionCoordinator.getInstance().closeSession(MapleClient.getPlaceholder(), true); - return false; - } - decoderState.packetlength = MapleAESOFB.getPacketLength(packetHeader); - } else if (in.remaining() < 4 && decoderState.packetlength == -1) { - return false; - } - if (in.remaining() >= decoderState.packetlength) { - byte[] decryptedPacket = new byte[decoderState.packetlength]; - in.get(decryptedPacket, 0, decoderState.packetlength); - decoderState.packetlength = -1; - rcvdCrypto.crypt(decryptedPacket); - MapleCustomEncryption.decryptData(decryptedPacket); - out.write(decryptedPacket); - if (YamlConfig.config.server.USE_DEBUG_SHOW_PACKET){ // Atoot's idea: packet traffic log, applied using auto-identation thanks to lrenex - int packetLen = decryptedPacket.length; - int pHeader = readFirstShort(decryptedPacket); - String pHeaderStr = Integer.toHexString(pHeader).toUpperCase(); - String op = lookupSend(pHeader); - String Send = "ClientSend:" + op + " [" + pHeaderStr + "] (" + packetLen + ")\r\n"; - if (packetLen <= 3000) { - String SendTo = Send + HexTool.toString(decryptedPacket) + "\r\n" + HexTool.toStringFromAscii(decryptedPacket); - System.out.println(SendTo); - if (op == null) { - System.out.println("UnknownPacket:" + SendTo); - } - } else { - FilePrinter.print(FilePrinter.PACKET_STREAM + ".txt", HexTool.toString(new byte[]{decryptedPacket[0], decryptedPacket[1]}) + "..."); - } - } - return true; - } - return false; - } - - private String lookupSend(int val) { - return OpcodeConstants.recvOpcodeNames.get(val); - } - - private int readFirstShort(byte[] arr) { - return new GenericLittleEndianAccessor(new ByteArrayByteStream(arr)).readShort(); - } -} diff --git a/src/main/java/net/mina/MaplePacketEncoder.java b/src/main/java/net/mina/MaplePacketEncoder.java deleted file mode 100644 index e1a542deca..0000000000 --- a/src/main/java/net/mina/MaplePacketEncoder.java +++ /dev/null @@ -1,97 +0,0 @@ -/* -This file is part of the OdinMS Maple Story Server -Copyright (C) 2008 Patrick Huy -Matthias Butz -Jan Christian Meyer - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Affero General Public License as -published by the Free Software Foundation version 3 as published by -the Free Software Foundation. You may not use, modify or distribute -this program under any other version of the GNU Affero General Public -License. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Affero General Public License for more details. - -You should have received a copy of the GNU Affero General Public License -along with this program. If not, see . - */ -package net.mina; - -import client.MapleClient; -import config.YamlConfig; -import constants.net.OpcodeConstants; -import org.apache.mina.core.buffer.IoBuffer; -import org.apache.mina.core.session.IoSession; -import org.apache.mina.filter.codec.ProtocolEncoder; -import org.apache.mina.filter.codec.ProtocolEncoderOutput; -import tools.FilePrinter; -import tools.HexTool; -import tools.MapleAESOFB; -import tools.data.input.ByteArrayByteStream; -import tools.data.input.GenericLittleEndianAccessor; - -public class MaplePacketEncoder implements ProtocolEncoder { - - @Override - public void encode(final IoSession session, final Object message, final ProtocolEncoderOutput out) throws Exception { - final MapleClient client = (MapleClient) session.getAttribute(MapleClient.CLIENT_KEY); - - try { - if (client.tryacquireEncoder()) { - try { - final MapleAESOFB send_crypto = client.getSendCrypto(); - final byte[] input = (byte[]) message; - if (YamlConfig.config.server.USE_DEBUG_SHOW_PACKET) { - int packetLen = input.length; - int pHeader = readFirstShort(input); - String pHeaderStr = Integer.toHexString(pHeader).toUpperCase(); - String op = lookupRecv(pHeader); - String Recv = "ServerSend:" + op + " [" + pHeaderStr + "] (" + packetLen + ")\r\n"; - if (packetLen <= 50000) { - String RecvTo = Recv + HexTool.toString(input) + "\r\n" + HexTool.toStringFromAscii(input); - System.out.println(RecvTo); - if (op == null) { - System.out.println("UnknownPacket:" + RecvTo); - } - } else { - FilePrinter.print(FilePrinter.PACKET_STREAM + ".txt", HexTool.toString(new byte[]{input[0], input[1]}) + " ..."); - } - } - - final byte[] unencrypted = new byte[input.length]; - System.arraycopy(input, 0, unencrypted, 0, input.length); - final byte[] ret = new byte[unencrypted.length + 4]; - final byte[] header = send_crypto.getPacketHeader(unencrypted.length); - MapleCustomEncryption.encryptData(unencrypted); - - send_crypto.crypt(unencrypted); - System.arraycopy(header, 0, ret, 0, 4); - System.arraycopy(unencrypted, 0, ret, 4, unencrypted.length); - - out.write(IoBuffer.wrap(ret)); - } finally { - client.unlockEncoder(); - } - } -// System.arraycopy(unencrypted, 0, ret, 4, unencrypted.length); -// out.write(ByteBuffer.wrap(ret)); - } catch (NullPointerException npe) { - out.write(IoBuffer.wrap(((byte[]) message))); - } - } - - private String lookupRecv(int val) { - return OpcodeConstants.sendOpcodeNames.get(val); - } - - private int readFirstShort(byte[] arr) { - return new GenericLittleEndianAccessor(new ByteArrayByteStream(arr)).readShort(); - } - - @Override - public void dispose(IoSession session) throws Exception {} -} \ No newline at end of file diff --git a/src/main/java/net/netty/PacketDecoder.java b/src/main/java/net/netty/PacketDecoder.java index 0c150f2527..253b16fe5f 100644 --- a/src/main/java/net/netty/PacketDecoder.java +++ b/src/main/java/net/netty/PacketDecoder.java @@ -6,7 +6,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ReplayingDecoder; -import net.mina.MapleCustomEncryption; +import net.MapleCustomEncryption; import net.packet.ByteBufInPacket; import org.slf4j.Logger; import org.slf4j.LoggerFactory; diff --git a/src/main/java/net/netty/PacketEncoder.java b/src/main/java/net/netty/PacketEncoder.java index 2e6de8e4d7..0fc2fb6979 100644 --- a/src/main/java/net/netty/PacketEncoder.java +++ b/src/main/java/net/netty/PacketEncoder.java @@ -6,7 +6,7 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; -import net.mina.MapleCustomEncryption; +import net.MapleCustomEncryption; import net.packet.ByteBufInPacket; import net.packet.OutPacket; import org.slf4j.Logger; From cce85ea663f5f860138dd43902f6c069df197300 Mon Sep 17 00:00:00 2001 From: P0nk Date: Thu, 15 Jul 2021 22:08:19 +0200 Subject: [PATCH 27/37] Use Hwid for login bypass --- src/main/java/client/MapleClient.java | 21 ++----- .../commands/gm0/EnableAuthCommand.java | 2 +- .../login/MapleLoginBypassCoordinator.java | 63 ++++++++++--------- .../session/MapleSessionCoordinator.java | 6 +- 4 files changed, 40 insertions(+), 52 deletions(-) diff --git a/src/main/java/client/MapleClient.java b/src/main/java/client/MapleClient.java index 380dd9ce65..ee332760b4 100644 --- a/src/main/java/client/MapleClient.java +++ b/src/main/java/client/MapleClient.java @@ -579,7 +579,7 @@ public class MapleClient extends ChannelInboundHandlerAdapter { } if (pin.equals(other)) { pinattempt = 0; - MapleLoginBypassCoordinator.getInstance().registerLoginBypassEntry(getNibbleHWID(), accId, false); + MapleLoginBypassCoordinator.getInstance().registerLoginBypassEntry(hwid, accId, false); return true; } return false; @@ -612,7 +612,7 @@ public class MapleClient extends ChannelInboundHandlerAdapter { } if (pic.equals(other)) { // thanks ryantpayton (HeavenClient) for noticing null pics being checked here picattempt = 0; - MapleLoginBypassCoordinator.getInstance().registerLoginBypassEntry(getNibbleHWID(), accId, true); + MapleLoginBypassCoordinator.getInstance().registerLoginBypassEntry(hwid, accId, true); return true; } return false; @@ -1597,21 +1597,12 @@ public class MapleClient extends ChannelInboundHandlerAdapter { announce(MaplePacketCreator.enableCSUse(player)); } - @Deprecated - public String getNibbleHWID() { - if (hwid != null) { - return hwid.hwid(); - } - - return null; - } - public boolean canBypassPin() { - return MapleLoginBypassCoordinator.getInstance().canLoginBypass(getNibbleHWID(), accId, false); + return MapleLoginBypassCoordinator.getInstance().canLoginBypass(hwid, accId, false); } public boolean canBypassPic() { - return MapleLoginBypassCoordinator.getInstance().canLoginBypass(getNibbleHWID(), accId, true); + return MapleLoginBypassCoordinator.getInstance().canLoginBypass(hwid, accId, true); } public int getLanguage() { @@ -1621,8 +1612,4 @@ public class MapleClient extends ChannelInboundHandlerAdapter { public void setLanguage(int lingua) { this.lang = lingua; } - - public static MapleClient getPlaceholder() { - return null; - } } \ No newline at end of file diff --git a/src/main/java/client/command/commands/gm0/EnableAuthCommand.java b/src/main/java/client/command/commands/gm0/EnableAuthCommand.java index 000c6fe54d..97f6ffd366 100644 --- a/src/main/java/client/command/commands/gm0/EnableAuthCommand.java +++ b/src/main/java/client/command/commands/gm0/EnableAuthCommand.java @@ -36,7 +36,7 @@ public class EnableAuthCommand extends Command { public void execute(MapleClient c, String[] params) { if (c.tryacquireClient()) { try { - MapleLoginBypassCoordinator.getInstance().unregisterLoginBypassEntry(c.getNibbleHWID(), c.getAccID()); + MapleLoginBypassCoordinator.getInstance().unregisterLoginBypassEntry(c.getHwid(), c.getAccID()); } finally { c.releaseClient(); } diff --git a/src/main/java/net/server/coordinator/login/MapleLoginBypassCoordinator.java b/src/main/java/net/server/coordinator/login/MapleLoginBypassCoordinator.java index 30c111ab3d..498a273165 100644 --- a/src/main/java/net/server/coordinator/login/MapleLoginBypassCoordinator.java +++ b/src/main/java/net/server/coordinator/login/MapleLoginBypassCoordinator.java @@ -19,7 +19,14 @@ */ package net.server.coordinator.login; +import client.MapleCharacter; +import client.MapleClient; import config.YamlConfig; +import net.server.Server; +import net.server.coordinator.session.Hwid; +import net.server.world.World; +import tools.Pair; + import java.util.HashSet; import java.util.LinkedList; import java.util.List; @@ -27,62 +34,56 @@ import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; -import client.MapleCharacter; -import client.MapleClient; -import net.server.world.World; -import net.server.Server; -import tools.Pair; - /** - * * @author Ronan */ public class MapleLoginBypassCoordinator { - private final static MapleLoginBypassCoordinator instance = new MapleLoginBypassCoordinator(); - + public static MapleLoginBypassCoordinator getInstance() { return instance; } - - private final ConcurrentHashMap, Pair> loginBypass = new ConcurrentHashMap<>(); // optimized PIN & PIC check - - public boolean canLoginBypass(String nibbleHwid, int accId, boolean pic) { + + private final ConcurrentHashMap, Pair> loginBypass = new ConcurrentHashMap<>(); // optimized PIN & PIC check + + public boolean canLoginBypass(Hwid hwid, int accId, boolean pic) { try { - Pair entry = new Pair<>(nibbleHwid, accId); + Pair entry = new Pair<>(hwid, accId); Boolean p = loginBypass.get(entry).getLeft(); - + return !pic || p; } catch (NullPointerException npe) { return false; } } - - public void registerLoginBypassEntry(String nibbleHwid, int accId, boolean pic) { + + public void registerLoginBypassEntry(Hwid hwid, int accId, boolean pic) { long expireTime = (pic ? YamlConfig.config.server.BYPASS_PIC_EXPIRATION : YamlConfig.config.server.BYPASS_PIN_EXPIRATION); if (expireTime > 0) { - Pair entry = new Pair<>(nibbleHwid, accId); + Pair entry = new Pair<>(hwid, accId); expireTime = Server.getInstance().getCurrentTime() + expireTime * 60 * 1000; try { pic |= loginBypass.get(entry).getLeft(); expireTime = Math.max(loginBypass.get(entry).getRight(), expireTime); - } catch (NullPointerException npe) {} - + } catch (NullPointerException npe) { + } + loginBypass.put(entry, new Pair<>(pic, expireTime)); } } - - public void unregisterLoginBypassEntry(String nibbleHwid, int accId) { - Pair entry = new Pair<>(nibbleHwid, accId); + + public void unregisterLoginBypassEntry(Hwid hwid, int accId) { + String hwidValue = hwid == null ? null : hwid.hwid(); + Pair entry = new Pair<>(hwidValue, accId); loginBypass.remove(entry); } - + public void runUpdateLoginBypass() { if (!loginBypass.isEmpty()) { - List> toRemove = new LinkedList<>(); + List> toRemove = new LinkedList<>(); Set onlineAccounts = new HashSet<>(); long timeNow = Server.getInstance().getCurrentTime(); - + for (World w : Server.getInstance().getWorlds()) { for (MapleCharacter chr : w.getPlayerStorage().getAllCharacters()) { MapleClient c = chr.getClient(); @@ -91,8 +92,8 @@ public class MapleLoginBypassCoordinator { } } } - - for (Entry, Pair> e : loginBypass.entrySet()) { + + for (Entry, Pair> e : loginBypass.entrySet()) { if (onlineAccounts.contains(e.getKey().getRight())) { long expireTime = timeNow + 2 * 60 * 1000; if (expireTime > e.getValue().getRight()) { @@ -102,13 +103,13 @@ public class MapleLoginBypassCoordinator { toRemove.add(e.getKey()); } } - + if (!toRemove.isEmpty()) { - for (Pair p : toRemove) { + for (Pair p : toRemove) { loginBypass.remove(p); } } } } - + } diff --git a/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java b/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java index 7af767f5b3..ae6ef08173 100644 --- a/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java +++ b/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java @@ -93,10 +93,10 @@ public class MapleSessionCoordinator { } public static String getSessionRemoteHost(MapleClient client) { - String nibbleHwid = client.getNibbleHWID(); + Hwid hwid = client.getHwid(); - if (nibbleHwid != null) { - return client.getRemoteAddress() + "-" + nibbleHwid; + if (hwid != null) { + return client.getRemoteAddress() + "-" + hwid.hwid(); } else { return client.getRemoteAddress(); } From 12a415e3d5cabae1d0bdfca64156c2abcebdc9d7 Mon Sep 17 00:00:00 2001 From: P0nk Date: Thu, 15 Jul 2021 23:48:47 +0200 Subject: [PATCH 28/37] Add simple hex utils that delegate to Netty The goal is to use libraries for this sort of common stuff --- src/main/java/tools/HexTool.java | 11 +++++++++++ src/test/java/tools/HexToolTest.java | 22 ++++++++++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 src/test/java/tools/HexToolTest.java diff --git a/src/main/java/tools/HexTool.java b/src/main/java/tools/HexTool.java index e610055e95..f23e2ac5fc 100644 --- a/src/main/java/tools/HexTool.java +++ b/src/main/java/tools/HexTool.java @@ -22,6 +22,7 @@ package tools; import constants.string.CharsetConstants; +import io.netty.buffer.ByteBufUtil; import java.io.ByteArrayOutputStream; @@ -105,4 +106,14 @@ public class HexTool { return ""; } + /** + * Get upper case hex dump + */ + public static String bytesToHex(byte[] bytes) { + return ByteBufUtil.hexDump(bytes).toUpperCase(); + } + + public static byte[] hexToBytes(String hex) { + return ByteBufUtil.decodeHexDump(hex); + } } diff --git a/src/test/java/tools/HexToolTest.java b/src/test/java/tools/HexToolTest.java new file mode 100644 index 0000000000..8ea59c403a --- /dev/null +++ b/src/test/java/tools/HexToolTest.java @@ -0,0 +1,22 @@ +package tools; + +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +class HexToolTest { + + @Test + void upperCaseHexToBytesAndBack() { + String hex = "A1B2C3"; + byte[] bytes = HexTool.hexToBytes(hex); + assertEquals(hex, HexTool.bytesToHex(bytes)); + } + + @Test + void mixedCaseHexToBytesAndBack() { + String hex = "aB5DaA"; + byte[] bytes = HexTool.hexToBytes(hex); + assertEquals(hex.toUpperCase(), HexTool.bytesToHex(bytes)); + } +} \ No newline at end of file From 6e6fbf3be70a667871b18107f2af02841a7f3bad Mon Sep 17 00:00:00 2001 From: P0nk Date: Thu, 15 Jul 2021 23:51:01 +0200 Subject: [PATCH 29/37] Consistently use Hwid domain object, fix login bypass Login bypass (skip pin/pic) was broken due to an inconsistency in hwid format. --- src/main/java/client/MapleClient.java | 24 +--- .../handlers/PlayerLoggedinHandler.java | 12 +- .../server/coordinator/session/HostHwid.java | 4 +- .../coordinator/session/HostHwidCache.java | 8 +- .../net/server/coordinator/session/Hwid.java | 27 ++-- .../session/MapleSessionCoordinator.java | 117 ++++++++---------- .../coordinator/session/SessionDAO.java | 18 +-- .../handlers/login/CharSelectedHandler.java | 13 +- .../login/CharSelectedWithPicHandler.java | 36 +++--- .../handlers/login/LoginPasswordHandler.java | 7 +- .../handlers/login/RegisterPicHandler.java | 36 +++--- .../login/ViewAllCharRegisterPicHandler.java | 36 +++--- .../login/ViewAllCharSelectedHandler.java | 36 +++--- .../ViewAllCharSelectedWithPicHandler.java | 36 +++--- 14 files changed, 183 insertions(+), 227 deletions(-) diff --git a/src/main/java/client/MapleClient.java b/src/main/java/client/MapleClient.java index ee332760b4..ace3670253 100644 --- a/src/main/java/client/MapleClient.java +++ b/src/main/java/client/MapleClient.java @@ -89,7 +89,6 @@ public class MapleClient extends ChannelInboundHandlerAdapter { private final Type type; private Hwid hwid; - private String remoteHwid; // Mac address + hwid in one. Retrieved from client when attempting to enter game. private String remoteAddress; private volatile boolean inTransition; @@ -269,14 +268,6 @@ public class MapleClient extends ChannelInboundHandlerAdapter { this.hwid = hwid; } - public String getRemoteHwid() { - return remoteHwid; - } - - public void setRemoteHwid(String remoteHwid) { - this.remoteHwid = remoteHwid; - } - public String getRemoteAddress() { return remoteAddress; } @@ -618,7 +609,7 @@ public class MapleClient extends ChannelInboundHandlerAdapter { return false; } - public int login(String login, String pwd, String nibbleHwid) { + public int login(String login, String pwd, Hwid hwid) { int loginok = 5; loginattempt++; @@ -676,7 +667,7 @@ public class MapleClient extends ChannelInboundHandlerAdapter { } if (loginok == 0 || loginok == 4) { - AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptLoginSession(this, nibbleHwid, accId, loginok == 4); + AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptLoginSession(this, hwid, accId, loginok == 4); switch (res) { case SUCCESS: @@ -756,16 +747,7 @@ public class MapleClient extends ChannelInboundHandlerAdapter { return ipAddress; } - public void updateHwid(String hwidClientString) { - final Hwid hwid; - try { - hwid = Hwid.fromClientString(hwidClientString); - } catch (IllegalArgumentException e) { - log.warn("Failed to create hwid from client string: {}", hwidClientString, e); - this.disconnect(false, false); - return; - } - + public void updateHwid(Hwid hwid) { this.hwid = hwid; try (Connection con = DatabaseConnection.getConnection(); diff --git a/src/main/java/net/server/channel/handlers/PlayerLoggedinHandler.java b/src/main/java/net/server/channel/handlers/PlayerLoggedinHandler.java index 49b6892e01..328e40d7ee 100644 --- a/src/main/java/net/server/channel/handlers/PlayerLoggedinHandler.java +++ b/src/main/java/net/server/channel/handlers/PlayerLoggedinHandler.java @@ -112,20 +112,18 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler { MapleCharacter player = wserv.getPlayerStorage().getCharacterById(cid); - String remoteHwid; + final Hwid hwid; if (player == null) { - remoteHwid = MapleSessionCoordinator.getInstance().pickLoginSessionHwid(c); - if (remoteHwid == null) { + hwid = MapleSessionCoordinator.getInstance().pickLoginSessionHwid(c); + if (hwid == null) { c.disconnect(true, false); return; } } else { - Hwid clientHwid = player.getClient().getHwid(); - remoteHwid = clientHwid == null ? null : clientHwid.hwid(); + hwid = player.getClient().getHwid(); } - c.setRemoteHwid(remoteHwid); - c.setHwid(new Hwid(remoteHwid)); + c.setHwid(hwid); if (!server.validateCharacteridInTransition(c, cid)) { c.disconnect(true, false); diff --git a/src/main/java/net/server/coordinator/session/HostHwid.java b/src/main/java/net/server/coordinator/session/HostHwid.java index b6c7a6590e..9516e2f2af 100644 --- a/src/main/java/net/server/coordinator/session/HostHwid.java +++ b/src/main/java/net/server/coordinator/session/HostHwid.java @@ -5,8 +5,8 @@ import net.server.Server; import java.time.Instant; import java.util.concurrent.TimeUnit; -record HostHwid(String hwid, Instant expiry) { - static HostHwid createWithDefaultExpiry(String hwid) { +record HostHwid(Hwid hwid, Instant expiry) { + static HostHwid createWithDefaultExpiry(Hwid hwid) { return new HostHwid(hwid, getDefaultExpiry()); } diff --git a/src/main/java/net/server/coordinator/session/HostHwidCache.java b/src/main/java/net/server/coordinator/session/HostHwidCache.java index 7f75ae854f..21a9cafca1 100644 --- a/src/main/java/net/server/coordinator/session/HostHwidCache.java +++ b/src/main/java/net/server/coordinator/session/HostHwidCache.java @@ -27,20 +27,20 @@ class HostHwidCache { } } - void addEntry(String remoteHost, String remoteHwid) { - hostHwidCache.put(remoteHost, HostHwid.createWithDefaultExpiry(remoteHwid)); + void addEntry(String remoteHost, Hwid hwid) { + hostHwidCache.put(remoteHost, HostHwid.createWithDefaultExpiry(hwid)); } HostHwid getEntry(String remoteHost) { return hostHwidCache.get(remoteHost); } - String removeEntryAndGetItsHwid(String remoteHost) { + Hwid removeEntryAndGetItsHwid(String remoteHost) { HostHwid hostHwid = hostHwidCache.remove(remoteHost); return hostHwid == null ? null : hostHwid.hwid(); } - String getEntryHwid(String remoteHost) { + Hwid getEntryHwid(String remoteHost) { HostHwid hostHwid = hostHwidCache.get(remoteHost); return hostHwid == null ? null : hostHwid.hwid(); } diff --git a/src/main/java/net/server/coordinator/session/Hwid.java b/src/main/java/net/server/coordinator/session/Hwid.java index 8b143c0c40..bd8ad7ce61 100644 --- a/src/main/java/net/server/coordinator/session/Hwid.java +++ b/src/main/java/net/server/coordinator/session/Hwid.java @@ -5,31 +5,22 @@ import java.util.regex.Pattern; public record Hwid (String hwid) { private static final int HWID_LENGTH = 8; // First part is a mac address (without dashes), second part is the hwid - private static final Pattern VALID_RAW_HWID_PATTERN = Pattern.compile("[0-9A-F]{12}_[0-9A-F]{8}"); + private static final Pattern VALID_HOST_STRING_PATTERN = Pattern.compile("[0-9A-F]{12}_[0-9A-F]{8}"); - public static boolean isValidRawHwid(String rawHwid) { - return VALID_RAW_HWID_PATTERN.matcher(rawHwid).matches(); + private static boolean isValidHostString(String hostString) { + return VALID_HOST_STRING_PATTERN.matcher(hostString).matches(); } - public static Hwid fromClientString(String clientString) throws IllegalArgumentException { - if (clientString == null) { - throw new IllegalArgumentException("clientString must not be null"); + public static Hwid fromHostString(String hostString) throws IllegalArgumentException { + if (hostString == null || !isValidHostString(hostString)) { + throw new IllegalArgumentException("hostString has invalid format"); } - String[] split = clientString.split("_"); + final String[] split = hostString.split("_"); if (split.length != 2 || split[1].length() != HWID_LENGTH) { - throw new IllegalArgumentException("Hwid validation failed for hwid: " + clientString); + throw new IllegalArgumentException("Hwid validation failed for hwid: " + hostString); } - StringBuilder newHwid = new StringBuilder(); - String convert = split[1]; - - int len = convert.length(); - for (int i = len - 2; i >= 0; i -= 2) { - newHwid.append(convert, i, i + 2); - } - newHwid.insert(4, "-"); - - return new Hwid(newHwid.toString()); + return new Hwid(split[1]); } } diff --git a/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java b/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java index ae6ef08173..dca6def3c1 100644 --- a/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java +++ b/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java @@ -60,22 +60,22 @@ public class MapleSessionCoordinator { private final SessionInitialization sessionInit = new SessionInitialization(); private final LoginStorage loginStorage = new LoginStorage(); private final Map onlineClients = new HashMap<>(); // Key: account id - private final Set onlineRemoteHwids = new HashSet<>(); // Hwid/nibblehwid + private final Set onlineRemoteHwids = new HashSet<>(); // Hwid/nibblehwid private final Map> loginRemoteHosts = new HashMap<>(); // Key: Ip (+ nibblehwid) private final HostHwidCache hostHwidCache = new HostHwidCache(); private MapleSessionCoordinator() { } - private static boolean attemptAccountAccess(int accountId, String nibbleHwid, boolean routineCheck) { + private static boolean attemptAccountAccess(int accountId, Hwid hwid, boolean routineCheck) { try (Connection con = DatabaseConnection.getConnection()) { List hwidRelevances = SessionDAO.getHwidRelevance(con, accountId); for (HwidRelevance hwidRelevance : hwidRelevances) { - if (hwidRelevance.hwid().endsWith(nibbleHwid)) { + if (hwidRelevance.hwid().endsWith(hwid.hwid())) { if (!routineCheck) { // better update HWID relevance as soon as the login is authenticated Instant expiry = HwidAssociationExpiry.getHwidAccountExpiry(hwidRelevance.relevance()); - SessionDAO.updateAccountAccess(con, nibbleHwid, accountId, expiry, hwidRelevance.getIncrementedRelevance()); + SessionDAO.updateAccountAccess(con, hwid, accountId, expiry, hwidRelevance.getIncrementedRelevance()); } return true; @@ -86,7 +86,7 @@ public class MapleSessionCoordinator { return true; } } catch (SQLException e) { - log.warn("Failed to update account access. Account id: {}, nibbleHwid: {}", accountId, nibbleHwid, e); + log.warn("Failed to update account access. Account id: {}, nibbleHwid: {}", accountId, hwid, e); } return false; @@ -188,9 +188,9 @@ public class MapleSessionCoordinator { } } - public AntiMulticlientResult attemptLoginSession(MapleClient client, String nibbleHwid, int accountId, boolean routineCheck) { + public AntiMulticlientResult attemptLoginSession(MapleClient client, Hwid hwid, int accountId, boolean routineCheck) { if (!YamlConfig.config.server.DETERRED_MULTICLIENT) { - client.setHwid(new Hwid(nibbleHwid)); + client.setHwid(hwid); return AntiMulticlientResult.SUCCESS; } @@ -203,16 +203,16 @@ public class MapleSessionCoordinator { try { if (!loginStorage.registerLogin(accountId)) { return AntiMulticlientResult.MANY_ACCOUNT_ATTEMPTS; - } else if (routineCheck && !attemptAccountAccess(accountId, nibbleHwid, routineCheck)) { + } else if (routineCheck && !attemptAccountAccess(accountId, hwid, routineCheck)) { return AntiMulticlientResult.REMOTE_REACHED_LIMIT; - } else if (onlineRemoteHwids.contains(nibbleHwid)) { + } else if (onlineRemoteHwids.contains(hwid)) { return AntiMulticlientResult.REMOTE_LOGGEDIN; - } else if (!attemptAccountAccess(accountId, nibbleHwid, routineCheck)) { + } else if (!attemptAccountAccess(accountId, hwid, routineCheck)) { return AntiMulticlientResult.REMOTE_REACHED_LIMIT; } - client.setHwid(new Hwid(nibbleHwid)); - onlineRemoteHwids.add(nibbleHwid); + client.setHwid(hwid); + onlineRemoteHwids.add(hwid); return AntiMulticlientResult.SUCCESS; } finally { @@ -220,11 +220,11 @@ public class MapleSessionCoordinator { } } - public AntiMulticlientResult attemptGameSession(MapleClient client, int accountId, String remoteHwid) { + public AntiMulticlientResult attemptGameSession(MapleClient client, int accountId, Hwid hwid) { final String remoteHost = getSessionRemoteHost(client); if (!YamlConfig.config.server.DETERRED_MULTICLIENT) { - hostHwidCache.addEntry(remoteHost, remoteHwid); - hostHwidCache.addEntry(client.getRemoteAddress(), remoteHwid); // no HWID information on the loggedin newcomer session... + hostHwidCache.addEntry(remoteHost, hwid); + hostHwidCache.addEntry(client.getRemoteAddress(), hwid); // no HWID information on the loggedin newcomer session... return AntiMulticlientResult.SUCCESS; } @@ -234,26 +234,26 @@ public class MapleSessionCoordinator { } try { - Hwid nibbleHwid = client.getHwid(); // thanks Paxum for noticing account stuck after PIC failure - if (nibbleHwid == null) { + Hwid clientHwid = client.getHwid(); // thanks Paxum for noticing account stuck after PIC failure + if (clientHwid == null) { return AntiMulticlientResult.REMOTE_NO_MATCH; } - onlineRemoteHwids.remove(nibbleHwid); + onlineRemoteHwids.remove(clientHwid); - if (!remoteHwid.endsWith(nibbleHwid.hwid())) { + if (!hwid.equals(clientHwid)) { return AntiMulticlientResult.REMOTE_NO_MATCH; - } else if (onlineRemoteHwids.contains(remoteHwid)) { + } else if (onlineRemoteHwids.contains(hwid)) { return AntiMulticlientResult.REMOTE_LOGGEDIN; } // assumption: after a SUCCESSFUL login attempt, the incoming client WILL receive a new IoSession from the game server // updated session CLIENT_HWID attribute will be set when the player log in the game - onlineRemoteHwids.add(remoteHwid); - hostHwidCache.addEntry(remoteHost, remoteHwid); - hostHwidCache.addEntry(client.getRemoteAddress(), remoteHwid); - associateHwidAccountIfAbsent(remoteHwid, accountId); + onlineRemoteHwids.add(hwid); + hostHwidCache.addEntry(remoteHost, hwid); + hostHwidCache.addEntry(client.getRemoteAddress(), hwid); + associateHwidAccountIfAbsent(hwid, accountId); return AntiMulticlientResult.SUCCESS; } finally { @@ -261,41 +261,32 @@ public class MapleSessionCoordinator { } } - private static void associateHwidAccountIfAbsent(String remoteHwid, int accountId) { + private static void associateHwidAccountIfAbsent(Hwid hwid, int accountId) { try (Connection con = DatabaseConnection.getConnection()) { - List hwids = SessionDAO.getHwidsForAccount(con, accountId); + List hwids = SessionDAO.getHwidsForAccount(con, accountId); - boolean containsRemoteHwid = hwids.stream().anyMatch(hwid -> hwid.contentEquals(remoteHwid)); + boolean containsRemoteHwid = hwids.stream().anyMatch(accountHwid -> accountHwid.equals(hwid)); if (containsRemoteHwid) { return; } if (hwids.size() < YamlConfig.config.server.MAX_ALLOWED_ACCOUNT_HWID) { Instant expiry = HwidAssociationExpiry.getHwidAccountExpiry(0); - SessionDAO.registerAccountAccess(con, accountId, remoteHwid, expiry); + SessionDAO.registerAccountAccess(con, accountId, hwid, expiry); } } catch (SQLException ex) { - log.warn("Failed to associate hwid {} with account id {}", remoteHwid, accountId, ex); + log.warn("Failed to associate hwid {} with account id {}", hwid, accountId, ex); } } private static MapleClient fetchInTransitionSessionClient(MapleClient client) { - String remoteHwid = MapleSessionCoordinator.getInstance().getGameSessionHwid(client); - if (remoteHwid == null) { // maybe this session was currently in-transition? + Hwid hwid = MapleSessionCoordinator.getInstance().getGameSessionHwid(client); + if (hwid == null) { // maybe this session was currently in-transition? return null; } - final int hwidLen = remoteHwid.length(); - final boolean isOnlyNibbleHwid = hwidLen <= 8; - if (isOnlyNibbleHwid) { - client.setHwid(new Hwid(remoteHwid)); - - } else { - client.setRemoteHwid(remoteHwid); - client.setHwid(Hwid.fromClientString(remoteHwid)); - } - MapleClient fakeClient = MapleClient.createMock(); + fakeClient.setHwid(hwid); Integer chrId = Server.getInstance().freeCharacteridInTransition(client); if (chrId != null) { try { @@ -316,26 +307,18 @@ public class MapleSessionCoordinator { final Hwid hwid = client.getHwid(); client.setHwid(null); // making sure to clean up calls to this function on login phase if (hwid != null) { - onlineRemoteHwids.remove(hwid.hwid()); - } - - final String remoteHwid = client.getRemoteHwid(); - client.setRemoteHwid(null); - if (remoteHwid != null) { - onlineRemoteHwids.remove(remoteHwid); + onlineRemoteHwids.remove(hwid); } - if (client != null) { - final boolean isGameSession = hwid != null || remoteHwid != null; - if (isGameSession) { + final boolean isGameSession = hwid != null; + if (isGameSession) { + onlineClients.remove(client.getAccID()); + } else { + MapleClient loggedClient = onlineClients.get(client.getAccID()); + + // do not remove an online game session here, only login session + if (loggedClient != null && loggedClient.getSessionId() == client.getSessionId()) { onlineClients.remove(client.getAccID()); - } else { - MapleClient loggedClient = onlineClients.get(client.getAccID()); - - // do not remove an online game session here, only login session - if (loggedClient != null && loggedClient.getSessionId() == client.getSessionId()) { - onlineClients.remove(client.getAccID()); - } } } @@ -343,14 +326,14 @@ public class MapleSessionCoordinator { client.closeSession(); } } - - public String pickLoginSessionHwid(MapleClient client) { + + public Hwid pickLoginSessionHwid(MapleClient client) { String remoteHost = client.getRemoteAddress(); // thanks BHB, resinate for noticing players from same network not being able to login return hostHwidCache.removeEntryAndGetItsHwid(remoteHost); } - public String getGameSessionHwid(MapleClient client) { + public Hwid getGameSessionHwid(MapleClient client) { String remoteHost = getSessionRemoteHost(client); return hostHwidCache.getEntryHwid(remoteHost); } @@ -376,11 +359,11 @@ public class MapleSessionCoordinator { } if (!onlineRemoteHwids.isEmpty()) { - List slist = new ArrayList<>(onlineRemoteHwids); - Collections.sort(slist); + List hwids = new ArrayList<>(onlineRemoteHwids); + hwids.sort(Comparator.comparing(Hwid::hwid)); System.out.println("Current online HWIDs: "); - for (String s : slist) { + for (Hwid s : hwids) { System.out.println(" " + s); } } @@ -410,11 +393,11 @@ public class MapleSessionCoordinator { } if (!onlineRemoteHwids.isEmpty()) { - List slist = new ArrayList<>(onlineRemoteHwids); - Collections.sort(slist); + List hwids = new ArrayList<>(onlineRemoteHwids); + hwids.sort(Comparator.comparing(Hwid::hwid)); str += ("Current online HWIDs:\r\n"); - for (String s : slist) { + for (Hwid s : hwids) { str += (" " + s + "\r\n"); } } diff --git a/src/main/java/net/server/coordinator/session/SessionDAO.java b/src/main/java/net/server/coordinator/session/SessionDAO.java index b4edafff87..ca862632bf 100644 --- a/src/main/java/net/server/coordinator/session/SessionDAO.java +++ b/src/main/java/net/server/coordinator/session/SessionDAO.java @@ -22,8 +22,8 @@ public class SessionDAO { } } - public static List getHwidsForAccount(Connection con, int accountId) throws SQLException { - final List hwids = new ArrayList<>(); + public static List getHwidsForAccount(Connection con, int accountId) throws SQLException { + final List hwids = new ArrayList<>(); final String query = "SELECT hwid FROM hwidaccounts WHERE accountid = ?"; try (PreparedStatement ps = con.prepareStatement(query)) { @@ -31,7 +31,7 @@ public class SessionDAO { try (ResultSet rs = ps.executeQuery()) { while (rs.next()) { - hwids.add(rs.getString("hwid")); + hwids.add(new Hwid(rs.getString("hwid"))); } } } @@ -39,12 +39,16 @@ public class SessionDAO { return hwids; } - public static void registerAccountAccess(Connection con, int accountId, String remoteHwid, Instant expiry) + public static void registerAccountAccess(Connection con, int accountId, Hwid hwid, Instant expiry) throws SQLException { + if (hwid == null) { + throw new IllegalArgumentException("Hwid must not be null"); + } + final String query = "INSERT INTO hwidaccounts (accountid, hwid, expiresat) VALUES (?, ?, ?)"; try (PreparedStatement ps = con.prepareStatement(query)) { ps.setInt(1, accountId); - ps.setString(2, remoteHwid); + ps.setString(2, hwid.hwid()); ps.setTimestamp(3, Timestamp.from(expiry)); ps.executeUpdate(); @@ -70,14 +74,14 @@ public class SessionDAO { return hwidRelevances; } - public static void updateAccountAccess(Connection con, String remoteHwid, int accountId, Instant expiry, int loginRelevance) + public static void updateAccountAccess(Connection con, Hwid hwid, int accountId, Instant expiry, int loginRelevance) throws SQLException { final String query = "UPDATE hwidaccounts SET relevance = ?, expiresat = ? WHERE accountid = ? AND hwid LIKE ?"; try (PreparedStatement ps = con.prepareStatement(query)) { ps.setInt(1, loginRelevance); ps.setTimestamp(2, Timestamp.from(expiry)); ps.setInt(3, accountId); - ps.setString(4, remoteHwid); + ps.setString(4, hwid.hwid()); ps.executeUpdate(); } diff --git a/src/main/java/net/server/handlers/login/CharSelectedHandler.java b/src/main/java/net/server/handlers/login/CharSelectedHandler.java index 83d38a19dc..4e032335d3 100644 --- a/src/main/java/net/server/handlers/login/CharSelectedHandler.java +++ b/src/main/java/net/server/handlers/login/CharSelectedHandler.java @@ -28,6 +28,8 @@ import net.server.coordinator.session.Hwid; import net.server.coordinator.session.MapleSessionCoordinator; import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientResult; import net.server.world.World; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; @@ -35,6 +37,7 @@ import java.net.InetAddress; import java.net.UnknownHostException; public final class CharSelectedHandler extends AbstractMaplePacketHandler { + private static final Logger log = LoggerFactory.getLogger(CharSelectedHandler.class); private static int parseAntiMulticlientError(AntiMulticlientResult res) { return switch (res) { @@ -51,9 +54,13 @@ public final class CharSelectedHandler extends AbstractMaplePacketHandler { int charId = slea.readInt(); String macs = slea.readMapleAsciiString(); - String hwid = slea.readMapleAsciiString(); - - if (!Hwid.isValidRawHwid(hwid)) { + String hostString = slea.readMapleAsciiString(); + + final Hwid hwid; + try { + hwid = Hwid.fromHostString(hostString); + } catch (IllegalArgumentException e) { + log.warn("Invalid host string: {}", hostString, e); c.announce(MaplePacketCreator.getAfterLoginError(17)); return; } diff --git a/src/main/java/net/server/handlers/login/CharSelectedWithPicHandler.java b/src/main/java/net/server/handlers/login/CharSelectedWithPicHandler.java index e1fa8ec499..6a2191db25 100644 --- a/src/main/java/net/server/handlers/login/CharSelectedWithPicHandler.java +++ b/src/main/java/net/server/handlers/login/CharSelectedWithPicHandler.java @@ -7,6 +7,8 @@ import net.server.coordinator.session.Hwid; import net.server.coordinator.session.MapleSessionCoordinator; import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientResult; import net.server.world.World; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; @@ -14,24 +16,16 @@ import java.net.InetAddress; import java.net.UnknownHostException; public class CharSelectedWithPicHandler extends AbstractMaplePacketHandler { + private static final Logger log = LoggerFactory.getLogger(CharSelectedWithPicHandler.class); private static int parseAntiMulticlientError(AntiMulticlientResult res) { - switch (res) { - case REMOTE_PROCESSING: - return 10; - - case REMOTE_LOGGEDIN: - return 7; - - case REMOTE_NO_MATCH: - return 17; - - case COORDINATOR_ERROR: - return 8; - - default: - return 9; - } + return switch (res) { + case REMOTE_PROCESSING -> 10; + case REMOTE_LOGGEDIN -> 7; + case REMOTE_NO_MATCH -> 17; + case COORDINATOR_ERROR -> 8; + default -> 9; + }; } @Override @@ -40,9 +34,13 @@ public class CharSelectedWithPicHandler extends AbstractMaplePacketHandler { int charId = slea.readInt(); String macs = slea.readMapleAsciiString(); - String hwid = slea.readMapleAsciiString(); - - if (!Hwid.isValidRawHwid(hwid)) { + String hostString = slea.readMapleAsciiString(); + + final Hwid hwid; + try { + hwid = Hwid.fromHostString(hostString); + } catch (IllegalArgumentException e) { + log.warn("Invalid host string: {}", hostString, e); c.announce(MaplePacketCreator.getAfterLoginError(17)); return; } diff --git a/src/main/java/net/server/handlers/login/LoginPasswordHandler.java b/src/main/java/net/server/handlers/login/LoginPasswordHandler.java index 1d7e7cd8f5..3928d9506c 100644 --- a/src/main/java/net/server/handlers/login/LoginPasswordHandler.java +++ b/src/main/java/net/server/handlers/login/LoginPasswordHandler.java @@ -26,6 +26,7 @@ import client.MapleClient; import config.YamlConfig; import net.MaplePacketHandler; import net.server.Server; +import net.server.coordinator.session.Hwid; import tools.BCrypt; import tools.DatabaseConnection; import tools.HexTool; @@ -66,8 +67,8 @@ public final class LoginPasswordHandler implements MaplePacketHandler { slea.skip(6); // localhost masked the initial part with zeroes... byte[] hwidNibbles = slea.read(4); - String nibbleHwid = HexTool.toCompressedString(hwidNibbles); - int loginok = c.login(login, pwd, nibbleHwid); + Hwid hwid = new Hwid(HexTool.bytesToHex(hwidNibbles)); + int loginok = c.login(login, pwd, hwid); if (YamlConfig.config.server.AUTOMATIC_REGISTER && loginok == 5) { @@ -87,7 +88,7 @@ public final class LoginPasswordHandler implements MaplePacketHandler { c.setAccID(-1); e.printStackTrace(); } finally { - loginok = c.login(login, pwd, nibbleHwid); + loginok = c.login(login, pwd, hwid); } } diff --git a/src/main/java/net/server/handlers/login/RegisterPicHandler.java b/src/main/java/net/server/handlers/login/RegisterPicHandler.java index e6aad9a99a..f1e7f7315f 100644 --- a/src/main/java/net/server/handlers/login/RegisterPicHandler.java +++ b/src/main/java/net/server/handlers/login/RegisterPicHandler.java @@ -7,6 +7,8 @@ import net.server.coordinator.session.Hwid; import net.server.coordinator.session.MapleSessionCoordinator; import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientResult; import net.server.world.World; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; @@ -14,24 +16,16 @@ import java.net.InetAddress; import java.net.UnknownHostException; public final class RegisterPicHandler extends AbstractMaplePacketHandler { + private static final Logger log = LoggerFactory.getLogger(RegisterPicHandler.class); private static int parseAntiMulticlientError(AntiMulticlientResult res) { - switch (res) { - case REMOTE_PROCESSING: - return 10; - - case REMOTE_LOGGEDIN: - return 7; - - case REMOTE_NO_MATCH: - return 17; - - case COORDINATOR_ERROR: - return 8; - - default: - return 9; - } + return switch (res) { + case REMOTE_PROCESSING -> 10; + case REMOTE_LOGGEDIN -> 7; + case REMOTE_NO_MATCH -> 17; + case COORDINATOR_ERROR -> 8; + default -> 9; + }; } @Override @@ -40,9 +34,13 @@ public final class RegisterPicHandler extends AbstractMaplePacketHandler { int charId = slea.readInt(); String macs = slea.readMapleAsciiString(); - String hwid = slea.readMapleAsciiString(); - - if (!Hwid.isValidRawHwid(hwid)) { + String hostString = slea.readMapleAsciiString(); + + final Hwid hwid; + try { + hwid = Hwid.fromHostString(hostString); + } catch (IllegalArgumentException e) { + log.warn("Invalid host string: {}", hostString, e); c.announce(MaplePacketCreator.getAfterLoginError(17)); return; } diff --git a/src/main/java/net/server/handlers/login/ViewAllCharRegisterPicHandler.java b/src/main/java/net/server/handlers/login/ViewAllCharRegisterPicHandler.java index d0602672cd..47e4cbbdef 100644 --- a/src/main/java/net/server/handlers/login/ViewAllCharRegisterPicHandler.java +++ b/src/main/java/net/server/handlers/login/ViewAllCharRegisterPicHandler.java @@ -7,6 +7,8 @@ import net.server.coordinator.session.Hwid; import net.server.coordinator.session.MapleSessionCoordinator; import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientResult; import net.server.world.World; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import tools.MaplePacketCreator; import tools.Randomizer; import tools.data.input.SeekableLittleEndianAccessor; @@ -15,24 +17,16 @@ import java.net.InetAddress; import java.net.UnknownHostException; public final class ViewAllCharRegisterPicHandler extends AbstractMaplePacketHandler { + private static final Logger log = LoggerFactory.getLogger(ViewAllCharRegisterPicHandler.class); private static int parseAntiMulticlientError(AntiMulticlientResult res) { - switch (res) { - case REMOTE_PROCESSING: - return 10; - - case REMOTE_LOGGEDIN: - return 7; - - case REMOTE_NO_MATCH: - return 17; - - case COORDINATOR_ERROR: - return 8; - - default: - return 9; - } + return switch (res) { + case REMOTE_PROCESSING -> 10; + case REMOTE_LOGGEDIN -> 7; + case REMOTE_NO_MATCH -> 17; + case COORDINATOR_ERROR -> 8; + default -> 9; + }; } @Override @@ -42,9 +36,13 @@ public final class ViewAllCharRegisterPicHandler extends AbstractMaplePacketHand slea.readInt(); // please don't let the client choose which world they should login String mac = slea.readMapleAsciiString(); - String hwid = slea.readMapleAsciiString(); - - if (!Hwid.isValidRawHwid(hwid)) { + String hostString = slea.readMapleAsciiString(); + + final Hwid hwid; + try { + hwid = Hwid.fromHostString(hostString); + } catch (IllegalArgumentException e) { + log.warn("Invalid host string: {}", hostString, e); c.announce(MaplePacketCreator.getAfterLoginError(17)); return; } diff --git a/src/main/java/net/server/handlers/login/ViewAllCharSelectedHandler.java b/src/main/java/net/server/handlers/login/ViewAllCharSelectedHandler.java index a4c197ba88..32e8cb86b4 100644 --- a/src/main/java/net/server/handlers/login/ViewAllCharSelectedHandler.java +++ b/src/main/java/net/server/handlers/login/ViewAllCharSelectedHandler.java @@ -28,6 +28,8 @@ import net.server.coordinator.session.Hwid; import net.server.coordinator.session.MapleSessionCoordinator; import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientResult; import net.server.world.World; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import tools.MaplePacketCreator; import tools.Randomizer; import tools.data.input.SeekableLittleEndianAccessor; @@ -36,24 +38,16 @@ import java.net.InetAddress; import java.net.UnknownHostException; public final class ViewAllCharSelectedHandler extends AbstractMaplePacketHandler { + private static final Logger log = LoggerFactory.getLogger(ViewAllCharSelectedHandler.class); private static int parseAntiMulticlientError(AntiMulticlientResult res) { - switch (res) { - case REMOTE_PROCESSING: - return 10; - - case REMOTE_LOGGEDIN: - return 7; - - case REMOTE_NO_MATCH: - return 17; - - case COORDINATOR_ERROR: - return 8; - - default: - return 9; - } + return switch (res) { + case REMOTE_PROCESSING -> 10; + case REMOTE_LOGGEDIN -> 7; + case REMOTE_NO_MATCH -> 17; + case COORDINATOR_ERROR -> 8; + default -> 9; + }; } @Override @@ -62,9 +56,13 @@ public final class ViewAllCharSelectedHandler extends AbstractMaplePacketHandler slea.readInt(); // please don't let the client choose which world they should login String macs = slea.readMapleAsciiString(); - String hwid = slea.readMapleAsciiString(); - - if (!Hwid.isValidRawHwid(hwid)) { + String hostString = slea.readMapleAsciiString(); + + final Hwid hwid; + try { + hwid = Hwid.fromHostString(hostString); + } catch (IllegalArgumentException e) { + log.warn("Invalid host string: {}", hostString, e); c.announce(MaplePacketCreator.getAfterLoginError(17)); return; } diff --git a/src/main/java/net/server/handlers/login/ViewAllCharSelectedWithPicHandler.java b/src/main/java/net/server/handlers/login/ViewAllCharSelectedWithPicHandler.java index 5476f2dbf3..b5fbc40b5d 100644 --- a/src/main/java/net/server/handlers/login/ViewAllCharSelectedWithPicHandler.java +++ b/src/main/java/net/server/handlers/login/ViewAllCharSelectedWithPicHandler.java @@ -7,6 +7,8 @@ import net.server.coordinator.session.Hwid; import net.server.coordinator.session.MapleSessionCoordinator; import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientResult; import net.server.world.World; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import tools.MaplePacketCreator; import tools.Randomizer; import tools.data.input.SeekableLittleEndianAccessor; @@ -15,24 +17,16 @@ import java.net.InetAddress; import java.net.UnknownHostException; public class ViewAllCharSelectedWithPicHandler extends AbstractMaplePacketHandler { + private static final Logger log = LoggerFactory.getLogger(ViewAllCharSelectedWithPicHandler.class); private static int parseAntiMulticlientError(AntiMulticlientResult res) { - switch (res) { - case REMOTE_PROCESSING: - return 10; - - case REMOTE_LOGGEDIN: - return 7; - - case REMOTE_NO_MATCH: - return 17; - - case COORDINATOR_ERROR: - return 8; - - default: - return 9; - } + return switch (res) { + case REMOTE_PROCESSING -> 10; + case REMOTE_LOGGEDIN -> 7; + case REMOTE_NO_MATCH -> 17; + case COORDINATOR_ERROR -> 8; + default -> 9; + }; } @Override @@ -43,9 +37,13 @@ public class ViewAllCharSelectedWithPicHandler extends AbstractMaplePacketHandle slea.readInt(); // please don't let the client choose which world they should login String macs = slea.readMapleAsciiString(); - String hwid = slea.readMapleAsciiString(); - - if (!Hwid.isValidRawHwid(hwid)) { + String hostString = slea.readMapleAsciiString(); + + final Hwid hwid; + try { + hwid = Hwid.fromHostString(hostString); + } catch (IllegalArgumentException e) { + log.warn("Invalid host string: {}", hostString, e); c.announce(MaplePacketCreator.getAfterLoginError(17)); return; } From ab03dd310981e6823a5b73f382871bf1554da622 Mon Sep 17 00:00:00 2001 From: P0nk Date: Sat, 17 Jul 2021 11:24:11 +0200 Subject: [PATCH 30/37] Minor cleanup --- src/main/java/client/MapleClient.java | 16 ++++++++-------- .../java/net/netty/ChannelServerInitializer.java | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/main/java/client/MapleClient.java b/src/main/java/client/MapleClient.java index ace3670253..01ce9ee5fc 100644 --- a/src/main/java/client/MapleClient.java +++ b/src/main/java/client/MapleClient.java @@ -134,14 +134,6 @@ public class MapleClient extends ChannelInboundHandlerAdapter { CHANNEL } - public void updateLastPacket() { - lastPacket = System.currentTimeMillis(); - } - - public long getLastPacket() { - return lastPacket; - } - public MapleClient(Type type, PacketProcessor packetProcessor, int world, int channel) { this.type = type; this.packetProcessor = packetProcessor; @@ -252,6 +244,14 @@ public class MapleClient extends ChannelInboundHandlerAdapter { } } + public void updateLastPacket() { + lastPacket = System.currentTimeMillis(); + } + + public long getLastPacket() { + return lastPacket; + } + public void closeSession() { ioChannel.close(); } diff --git a/src/main/java/net/netty/ChannelServerInitializer.java b/src/main/java/net/netty/ChannelServerInitializer.java index 5b56d92638..78be6e0ed9 100644 --- a/src/main/java/net/netty/ChannelServerInitializer.java +++ b/src/main/java/net/netty/ChannelServerInitializer.java @@ -21,7 +21,7 @@ public class ChannelServerInitializer extends ServerChannelInitializer { @Override public void initChannel(SocketChannel socketChannel) { - final String clientIp = socketChannel.remoteAddress().getHostName(); + final String clientIp = socketChannel.remoteAddress().getHostString(); log.debug("Client connecting to world {}, channel {} from {}", world, channel, clientIp); PacketProcessor packetProcessor = PacketProcessor.getChannelServerProcessor(world, channel); From 11e83522d6b3bd6fa945bf939eaab1d3f233b7b6 Mon Sep 17 00:00:00 2001 From: P0nk Date: Sun, 18 Jul 2021 14:09:39 +0200 Subject: [PATCH 31/37] Log packets in dedicated handlers instead of in encoder/decoder --- src/main/java/client/MapleClient.java | 5 +- src/main/java/net/netty/PacketDecoder.java | 34 ------------- src/main/java/net/netty/PacketEncoder.java | 36 -------------- .../net/netty/ServerChannelInitializer.java | 13 +++++ .../net/packet/logging/InPacketLogger.java | 47 ++++++++++++++++++ .../java/net/packet/logging/LoggingUtil.java | 17 +++++++ .../net/packet/logging/OutPacketLogger.java | 48 +++++++++++++++++++ .../java/net/packet/logging/PacketLogger.java | 7 +++ src/main/resources/log4j2.xml | 5 +- 9 files changed, 139 insertions(+), 73 deletions(-) create mode 100644 src/main/java/net/packet/logging/InPacketLogger.java create mode 100644 src/main/java/net/packet/logging/LoggingUtil.java create mode 100644 src/main/java/net/packet/logging/OutPacketLogger.java create mode 100644 src/main/java/net/packet/logging/PacketLogger.java diff --git a/src/main/java/client/MapleClient.java b/src/main/java/client/MapleClient.java index 01ce9ee5fc..10d9764b62 100644 --- a/src/main/java/client/MapleClient.java +++ b/src/main/java/client/MapleClient.java @@ -30,6 +30,8 @@ import io.netty.handler.timeout.IdleStateEvent; import net.MaplePacketHandler; import net.PacketProcessor; import net.netty.InvalidPacketHeaderException; +import net.packet.logging.LoggingUtil; +import net.packet.logging.PacketLogger; import net.packet.ByteBufOutPacket; import net.packet.InPacket; import net.packet.OutPacket; @@ -80,7 +82,6 @@ import java.util.concurrent.locks.Lock; public class MapleClient extends ChannelInboundHandlerAdapter { private static final Logger log = LoggerFactory.getLogger(MapleClient.class); - private static final Set ignoredDebugRecvPackets = Set.of((short) 167, (short) 197, (short) 89, (short) 91, (short) 41, (short) 188, (short) 107); public static final int LOGIN_NOTLOGGEDIN = 0; public static final int LOGIN_SERVER_TRANSITION = 1; @@ -181,7 +182,7 @@ public class MapleClient extends ChannelInboundHandlerAdapter { short opcode = packet.readShort(); final MaplePacketHandler handler = packetProcessor.getHandler(opcode); - if (YamlConfig.config.server.USE_DEBUG_SHOW_RCVD_PACKET && !ignoredDebugRecvPackets.contains(opcode)) { + if (YamlConfig.config.server.USE_DEBUG_SHOW_RCVD_PACKET && !LoggingUtil.isIgnoredRecvPacket(opcode)) { log.debug("Received packet id {}", opcode); } diff --git a/src/main/java/net/netty/PacketDecoder.java b/src/main/java/net/netty/PacketDecoder.java index 253b16fe5f..748df78de1 100644 --- a/src/main/java/net/netty/PacketDecoder.java +++ b/src/main/java/net/netty/PacketDecoder.java @@ -1,23 +1,16 @@ package net.netty; -import config.YamlConfig; -import constants.net.OpcodeConstants; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ReplayingDecoder; import net.MapleCustomEncryption; import net.packet.ByteBufInPacket; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import tools.HexTool; import tools.MapleAESOFB; import java.util.List; public class PacketDecoder extends ReplayingDecoder { - private static final Logger log = LoggerFactory.getLogger(PacketDecoder.class); - private static final boolean LOG_PACKETS = YamlConfig.config.server.USE_DEBUG_SHOW_PACKET; private final MapleAESOFB receiveCypher; public PacketDecoder(MapleAESOFB receiveCypher) { @@ -38,10 +31,6 @@ public class PacketDecoder extends ReplayingDecoder { receiveCypher.crypt(packet); MapleCustomEncryption.decryptData(packet); out.add(new ByteBufInPacket(Unpooled.wrappedBuffer(packet))); - - if (LOG_PACKETS){ - logPacket(packet); - } } /** @@ -57,27 +46,4 @@ public class PacketDecoder extends ReplayingDecoder { length = ((length << 8) & 0xFF00) | ((length >>> 8) & 0xFF); return length; } - - private void logPacket(byte[] packet) { - final int packetLength = packet.length; - - if (packetLength <= 3000) { - final int opcode = readFirstShort(packet); - final String opcodeHex = Integer.toHexString(opcode).toUpperCase(); - final String opcodeName = getRecvOpcodeName(opcode); - final String prefix = opcodeName == null ? " " : ""; - log.info("{}ClientSend:{} [{}] ({}) - hex:{} - text:{}", prefix, opcodeName, opcodeHex, packetLength, - HexTool.toString(packet), HexTool.toStringFromAscii(packet)); - } else { - log.debug(HexTool.toString(new byte[]{packet[0], packet[1]}) + "..."); - } - } - - private static int readFirstShort(byte[] bytes) { - return new ByteBufInPacket(Unpooled.wrappedBuffer(bytes)).readShort(); - } - - private String getRecvOpcodeName(int opcode) { - return OpcodeConstants.recvOpcodeNames.get(opcode); - } } diff --git a/src/main/java/net/netty/PacketEncoder.java b/src/main/java/net/netty/PacketEncoder.java index 0fc2fb6979..f3156d8591 100644 --- a/src/main/java/net/netty/PacketEncoder.java +++ b/src/main/java/net/netty/PacketEncoder.java @@ -1,22 +1,13 @@ package net.netty; -import config.YamlConfig; -import constants.net.OpcodeConstants; import io.netty.buffer.ByteBuf; -import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; import net.MapleCustomEncryption; -import net.packet.ByteBufInPacket; import net.packet.OutPacket; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import tools.HexTool; import tools.MapleAESOFB; public class PacketEncoder extends MessageToByteEncoder { - private static final Logger log = LoggerFactory.getLogger(PacketEncoder.class); - private static final boolean LOG_PACKETS = YamlConfig.config.server.USE_DEBUG_SHOW_PACKET; private final MapleAESOFB sendCypher; public PacketEncoder(MapleAESOFB sendCypher) { @@ -28,10 +19,6 @@ public class PacketEncoder extends MessageToByteEncoder { byte[] packet = in.getBytes(); out.writeBytes(getEncodedHeader(packet.length)); - if (LOG_PACKETS) { - logPacket(packet); - } - MapleCustomEncryption.encryptData(packet); sendCypher.crypt(packet); out.writeBytes(packet); @@ -40,27 +27,4 @@ public class PacketEncoder extends MessageToByteEncoder { private byte[] getEncodedHeader(int length) { return sendCypher.getPacketHeader(length); } - - private void logPacket(byte[] packet) { - final int packetLength = packet.length; - - if (packetLength <= 50000) { - final int opcode = readFirstShort(packet); - String opcodeHex = Integer.toHexString(opcode).toUpperCase(); - String opcodeName = getSendOpcodeName(opcode); - String prefix = opcodeName == null ? " " : ""; - log.info("{}ServerSend:{} [{}] ({}) - hex:{} - text:{}", prefix, opcodeName, opcodeHex, packetLength, - HexTool.toString(packet), HexTool.toStringFromAscii(packet)); - } else { - log.info(HexTool.toString(new byte[]{packet[0], packet[1]}) + " ..."); - } - } - - private String getSendOpcodeName(int opcode) { - return OpcodeConstants.sendOpcodeNames.get(opcode); - } - - private static int readFirstShort(byte[] bytes) { - return new ByteBufInPacket(Unpooled.wrappedBuffer(bytes)).readShort(); - } } diff --git a/src/main/java/net/netty/ServerChannelInitializer.java b/src/main/java/net/netty/ServerChannelInitializer.java index 93dfde1751..e4705922b0 100644 --- a/src/main/java/net/netty/ServerChannelInitializer.java +++ b/src/main/java/net/netty/ServerChannelInitializer.java @@ -1,18 +1,26 @@ package net.netty; import client.MapleClient; +import config.YamlConfig; import constants.net.ServerConstants; import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import io.netty.handler.timeout.IdleStateHandler; +import net.packet.logging.InPacketLogger; +import net.packet.logging.OutPacketLogger; import tools.MaplePacketCreator; import java.util.concurrent.atomic.AtomicLong; public abstract class ServerChannelInitializer extends ChannelInitializer { private static final int IDLE_TIME_SECONDS = 30; + private static final boolean LOG_PACKETS = YamlConfig.config.server.USE_DEBUG_SHOW_PACKET; + private static final ChannelHandler sendPacketLogger = new OutPacketLogger(); + private static final ChannelHandler receivePacketLogger = new InPacketLogger(); + static final AtomicLong sessionId = new AtomicLong(7777); void initPipeline(SocketChannel socketChannel, MapleClient client) { @@ -31,5 +39,10 @@ public abstract class ServerChannelInitializer extends ChannelInitializer " : ""; + log.info("{}ClientSend:{} [{}] ({}) - hex:{} - text:{}", prefix, opcodeName, opcodeHex, packetLength, + HexTool.toString(content), HexTool.toStringFromAscii(content)); + } else { + log.debug(HexTool.toString(new byte[]{content[0], content[1]}) + "..."); + } + } + + private String getRecvOpcodeName(short opcode) { + return OpcodeConstants.recvOpcodeNames.get((int) opcode); + } +} diff --git a/src/main/java/net/packet/logging/LoggingUtil.java b/src/main/java/net/packet/logging/LoggingUtil.java new file mode 100644 index 0000000000..29af8bfc34 --- /dev/null +++ b/src/main/java/net/packet/logging/LoggingUtil.java @@ -0,0 +1,17 @@ +package net.packet.logging; + +import io.netty.buffer.Unpooled; + +import java.util.Set; + +public class LoggingUtil { + private static final Set ignoredDebugRecvPackets = Set.of((short) 167, (short) 197, (short) 89, (short) 91, (short) 41, (short) 188, (short) 107); + + public static short readFirstShort(byte[] bytes) { + return Unpooled.wrappedBuffer(bytes).readShortLE(); + } + + public static boolean isIgnoredRecvPacket(short opcode) { + return ignoredDebugRecvPackets.contains(opcode); + } +} diff --git a/src/main/java/net/packet/logging/OutPacketLogger.java b/src/main/java/net/packet/logging/OutPacketLogger.java new file mode 100644 index 0000000000..8c8e1fd3a9 --- /dev/null +++ b/src/main/java/net/packet/logging/OutPacketLogger.java @@ -0,0 +1,48 @@ +package net.packet.logging; + +import constants.net.OpcodeConstants; +import io.netty.channel.ChannelHandler.Sharable; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelOutboundHandlerAdapter; +import io.netty.channel.ChannelPromise; +import net.packet.OutPacket; +import net.packet.Packet; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import tools.HexTool; + +@Sharable +public class OutPacketLogger extends ChannelOutboundHandlerAdapter implements PacketLogger { + private static final Logger log = LoggerFactory.getLogger(OutPacketLogger.class); + private static final int LOG_CONTENT_THRESHOLD = 50_000; + + @Override + public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) { + if (msg instanceof OutPacket packet) { + log(packet); + } + + ctx.write(msg); + } + + @Override + public void log(Packet packet) { + final byte[] content = packet.getBytes(); + final int packetLength = content.length; + + if (packetLength <= LOG_CONTENT_THRESHOLD) { + final short opcode = LoggingUtil.readFirstShort(content); + String opcodeHex = Integer.toHexString(opcode).toUpperCase(); + String opcodeName = getSendOpcodeName(opcode); + String prefix = opcodeName == null ? " " : ""; + log.info("{}ServerSend:{} [{}] ({}) - hex:{} - text:{}", prefix, opcodeName, opcodeHex, packetLength, + HexTool.toString(content), HexTool.toStringFromAscii(content)); + } else { + log.info(HexTool.toString(new byte[]{content[0], content[1]}) + " ..."); + } + } + + private String getSendOpcodeName(short opcode) { + return OpcodeConstants.sendOpcodeNames.get((int) opcode); + } +} diff --git a/src/main/java/net/packet/logging/PacketLogger.java b/src/main/java/net/packet/logging/PacketLogger.java new file mode 100644 index 0000000000..db5ed00222 --- /dev/null +++ b/src/main/java/net/packet/logging/PacketLogger.java @@ -0,0 +1,7 @@ +package net.packet.logging; + +import net.packet.Packet; + +public interface PacketLogger { + void log(Packet packet); +} diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml index cf3d8fda41..ba1769d487 100644 --- a/src/main/resources/log4j2.xml +++ b/src/main/resources/log4j2.xml @@ -5,8 +5,11 @@ - + + %d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n + + From 9e3f3cc0368d532421c8ff2bc59773c99d7c19a7 Mon Sep 17 00:00:00 2001 From: P0nk Date: Sun, 18 Jul 2021 14:26:23 +0200 Subject: [PATCH 32/37] Restructure "net" packages --- src/main/java/client/MapleClient.java | 7 +- .../java/client/autoban/AutobanFactory.java | 2 +- .../command/commands/gm3/IgnoreCommand.java | 2 +- .../command/commands/gm3/IgnoredCommand.java | 2 +- .../command/commands/gm3/MonitorCommand.java | 2 +- .../command/commands/gm3/MonitorsCommand.java | 2 +- .../{netty => encryption}/ClientCyphers.java | 3 +- .../InitializationVector.java | 2 +- .../encryption}/MapleAESOFB.java | 4 +- .../{netty => encryption}/PacketCodec.java | 2 +- .../{netty => encryption}/PacketDecoder.java | 4 +- .../{netty => encryption}/PacketEncoder.java | 3 +- .../net/netty/ServerChannelInitializer.java | 3 + .../java/net/packet/logging/MapleLogger.java | 72 +++++++++++++++++ src/main/java/tools/MapleLogger.java | 80 ------------------- src/main/java/tools/MaplePacketCreator.java | 2 +- 16 files changed, 93 insertions(+), 99 deletions(-) rename src/main/java/net/{netty => encryption}/ClientCyphers.java (94%) rename src/main/java/net/{netty => encryption}/InitializationVector.java (96%) rename src/main/java/{tools => net/encryption}/MapleAESOFB.java (99%) rename src/main/java/net/{netty => encryption}/PacketCodec.java (93%) rename src/main/java/net/{netty => encryption}/PacketDecoder.java (95%) rename src/main/java/net/{netty => encryption}/PacketEncoder.java (94%) create mode 100644 src/main/java/net/packet/logging/MapleLogger.java delete mode 100644 src/main/java/tools/MapleLogger.java diff --git a/src/main/java/client/MapleClient.java b/src/main/java/client/MapleClient.java index 10d9764b62..ec8643cd04 100644 --- a/src/main/java/client/MapleClient.java +++ b/src/main/java/client/MapleClient.java @@ -31,7 +31,7 @@ import net.MaplePacketHandler; import net.PacketProcessor; import net.netty.InvalidPacketHeaderException; import net.packet.logging.LoggingUtil; -import net.packet.logging.PacketLogger; +import net.packet.logging.MapleLogger; import net.packet.ByteBufOutPacket; import net.packet.InPacket; import net.packet.OutPacket; @@ -189,9 +189,10 @@ public class MapleClient extends ChannelInboundHandlerAdapter { if (handler != null && handler.validateState(this)) { // TODO: pass InPacket directly to handler once all handlers have been ported, // this is just a temporary workaround - GenericSeekableLittleEndianAccessor accessor = new GenericSeekableLittleEndianAccessor(new ByteArrayByteStream(packet.getBytes())); + final byte[] content = packet.getBytes(); + GenericSeekableLittleEndianAccessor accessor = new GenericSeekableLittleEndianAccessor(new ByteArrayByteStream(content)); try { - MapleLogger.logRecv(this, opcode, msg); + MapleLogger.logRecv(this, opcode, content); handler.handlePacket(accessor, this); } catch (final Throwable t) { FilePrinter.printError(FilePrinter.PACKET_HANDLER + handler.getClass().getName() + ".txt", t, "Error for " + (getPlayer() == null ? "" : "player ; " + getPlayer() + " on map ; " + getPlayer().getMapId() + " - ") + "account ; " + getAccountName() + "\r\n" + accessor); diff --git a/src/main/java/client/autoban/AutobanFactory.java b/src/main/java/client/autoban/AutobanFactory.java index dbca257ea4..8f65f5463e 100644 --- a/src/main/java/client/autoban/AutobanFactory.java +++ b/src/main/java/client/autoban/AutobanFactory.java @@ -26,7 +26,7 @@ import client.MapleCharacter; import config.YamlConfig; import net.server.Server; import tools.FilePrinter; -import tools.MapleLogger; +import net.packet.logging.MapleLogger; import tools.MaplePacketCreator; /** diff --git a/src/main/java/client/command/commands/gm3/IgnoreCommand.java b/src/main/java/client/command/commands/gm3/IgnoreCommand.java index 4fc0ceb168..226e63c323 100644 --- a/src/main/java/client/command/commands/gm3/IgnoreCommand.java +++ b/src/main/java/client/command/commands/gm3/IgnoreCommand.java @@ -27,7 +27,7 @@ import client.MapleCharacter; import client.MapleClient; import client.command.Command; import net.server.Server; -import tools.MapleLogger; +import net.packet.logging.MapleLogger; import tools.MaplePacketCreator; public class IgnoreCommand extends Command { diff --git a/src/main/java/client/command/commands/gm3/IgnoredCommand.java b/src/main/java/client/command/commands/gm3/IgnoredCommand.java index f15617e6dc..daf704699d 100644 --- a/src/main/java/client/command/commands/gm3/IgnoredCommand.java +++ b/src/main/java/client/command/commands/gm3/IgnoredCommand.java @@ -26,7 +26,7 @@ package client.command.commands.gm3; import client.MapleCharacter; import client.MapleClient; import client.command.Command; -import tools.MapleLogger; +import net.packet.logging.MapleLogger; public class IgnoredCommand extends Command { { diff --git a/src/main/java/client/command/commands/gm3/MonitorCommand.java b/src/main/java/client/command/commands/gm3/MonitorCommand.java index 89aa250421..0a82ce454b 100644 --- a/src/main/java/client/command/commands/gm3/MonitorCommand.java +++ b/src/main/java/client/command/commands/gm3/MonitorCommand.java @@ -27,7 +27,7 @@ import client.MapleCharacter; import client.MapleClient; import client.command.Command; import net.server.Server; -import tools.MapleLogger; +import net.packet.logging.MapleLogger; import tools.MaplePacketCreator; public class MonitorCommand extends Command { diff --git a/src/main/java/client/command/commands/gm3/MonitorsCommand.java b/src/main/java/client/command/commands/gm3/MonitorsCommand.java index 2a72da5ed5..97fd37a9d4 100644 --- a/src/main/java/client/command/commands/gm3/MonitorsCommand.java +++ b/src/main/java/client/command/commands/gm3/MonitorsCommand.java @@ -26,7 +26,7 @@ package client.command.commands.gm3; import client.MapleCharacter; import client.MapleClient; import client.command.Command; -import tools.MapleLogger; +import net.packet.logging.MapleLogger; public class MonitorsCommand extends Command { { diff --git a/src/main/java/net/netty/ClientCyphers.java b/src/main/java/net/encryption/ClientCyphers.java similarity index 94% rename from src/main/java/net/netty/ClientCyphers.java rename to src/main/java/net/encryption/ClientCyphers.java index 6e46fb80ca..13c09292dd 100644 --- a/src/main/java/net/netty/ClientCyphers.java +++ b/src/main/java/net/encryption/ClientCyphers.java @@ -1,7 +1,6 @@ -package net.netty; +package net.encryption; import constants.net.ServerConstants; -import tools.MapleAESOFB; public class ClientCyphers { private final MapleAESOFB send; diff --git a/src/main/java/net/netty/InitializationVector.java b/src/main/java/net/encryption/InitializationVector.java similarity index 96% rename from src/main/java/net/netty/InitializationVector.java rename to src/main/java/net/encryption/InitializationVector.java index 22cd624db1..fb5edaa7b5 100644 --- a/src/main/java/net/netty/InitializationVector.java +++ b/src/main/java/net/encryption/InitializationVector.java @@ -1,4 +1,4 @@ -package net.netty; +package net.encryption; public class InitializationVector { private final byte[] bytes; diff --git a/src/main/java/tools/MapleAESOFB.java b/src/main/java/net/encryption/MapleAESOFB.java similarity index 99% rename from src/main/java/tools/MapleAESOFB.java rename to src/main/java/net/encryption/MapleAESOFB.java index 3b1bd1e809..96ab7d52bb 100644 --- a/src/main/java/tools/MapleAESOFB.java +++ b/src/main/java/net/encryption/MapleAESOFB.java @@ -19,11 +19,11 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package tools; +package net.encryption; -import net.netty.InitializationVector; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import tools.HexTool; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; diff --git a/src/main/java/net/netty/PacketCodec.java b/src/main/java/net/encryption/PacketCodec.java similarity index 93% rename from src/main/java/net/netty/PacketCodec.java rename to src/main/java/net/encryption/PacketCodec.java index 6292096711..e3541cc734 100644 --- a/src/main/java/net/netty/PacketCodec.java +++ b/src/main/java/net/encryption/PacketCodec.java @@ -1,4 +1,4 @@ -package net.netty; +package net.encryption; import io.netty.channel.CombinedChannelDuplexHandler; diff --git a/src/main/java/net/netty/PacketDecoder.java b/src/main/java/net/encryption/PacketDecoder.java similarity index 95% rename from src/main/java/net/netty/PacketDecoder.java rename to src/main/java/net/encryption/PacketDecoder.java index 748df78de1..cec48f5bcb 100644 --- a/src/main/java/net/netty/PacketDecoder.java +++ b/src/main/java/net/encryption/PacketDecoder.java @@ -1,12 +1,12 @@ -package net.netty; +package net.encryption; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ReplayingDecoder; import net.MapleCustomEncryption; +import net.netty.InvalidPacketHeaderException; import net.packet.ByteBufInPacket; -import tools.MapleAESOFB; import java.util.List; diff --git a/src/main/java/net/netty/PacketEncoder.java b/src/main/java/net/encryption/PacketEncoder.java similarity index 94% rename from src/main/java/net/netty/PacketEncoder.java rename to src/main/java/net/encryption/PacketEncoder.java index f3156d8591..7a297116c6 100644 --- a/src/main/java/net/netty/PacketEncoder.java +++ b/src/main/java/net/encryption/PacketEncoder.java @@ -1,11 +1,10 @@ -package net.netty; +package net.encryption; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; import net.MapleCustomEncryption; import net.packet.OutPacket; -import tools.MapleAESOFB; public class PacketEncoder extends MessageToByteEncoder { private final MapleAESOFB sendCypher; diff --git a/src/main/java/net/netty/ServerChannelInitializer.java b/src/main/java/net/netty/ServerChannelInitializer.java index e4705922b0..9d9f0c6e78 100644 --- a/src/main/java/net/netty/ServerChannelInitializer.java +++ b/src/main/java/net/netty/ServerChannelInitializer.java @@ -9,6 +9,9 @@ import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; import io.netty.channel.socket.SocketChannel; import io.netty.handler.timeout.IdleStateHandler; +import net.encryption.ClientCyphers; +import net.encryption.InitializationVector; +import net.encryption.PacketCodec; import net.packet.logging.InPacketLogger; import net.packet.logging.OutPacketLogger; import tools.MaplePacketCreator; diff --git a/src/main/java/net/packet/logging/MapleLogger.java b/src/main/java/net/packet/logging/MapleLogger.java new file mode 100644 index 0000000000..68ccc002b4 --- /dev/null +++ b/src/main/java/net/packet/logging/MapleLogger.java @@ -0,0 +1,72 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 ~ 2010 Patrick Huy + Matthias Butz + Jan Christian Meyer + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License version 3 + as published by the Free Software Foundation. You may not use, modify + or distribute this program under any other version of the + GNU Affero General Public License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + */ +package net.packet.logging; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +import net.opcodes.RecvOpcode; +import client.MapleCharacter; +import client.MapleClient; +import tools.FilePrinter; +import tools.HexTool; + +/** + * Logs packets to console and file. + * + * @author Alan (SharpAceX) + */ + +public class MapleLogger { + public static final Set monitored = new HashSet<>(); + public static final Set ignored = new HashSet<>(); + + public static void logRecv(MapleClient c, short packetId, byte[] packetContent) { + MapleCharacter chr = c.getPlayer(); + if (chr == null) { + return; + } + if (!monitored.contains(chr.getId())) { + return; + } + RecvOpcode op = getOpcodeFromValue(packetId); + if (isRecvBlocked(op)) { + return; + } + String packet = op + "\r\n" + HexTool.toString(packetContent); + FilePrinter.printError(FilePrinter.PACKET_LOGS + c.getAccountName() + "-" + chr.getName() + ".txt", packet); + } + + private static boolean isRecvBlocked(RecvOpcode op) { + return switch (op) { + case MOVE_PLAYER, GENERAL_CHAT, TAKE_DAMAGE, MOVE_PET, MOVE_LIFE, NPC_ACTION, FACE_EXPRESSION -> true; + default -> false; + }; + } + + private static RecvOpcode getOpcodeFromValue(int value) { + return Arrays.stream(RecvOpcode.values()) + .filter(opcode -> value == opcode.getValue()) + .findAny() + .orElse(null); + } +} diff --git a/src/main/java/tools/MapleLogger.java b/src/main/java/tools/MapleLogger.java deleted file mode 100644 index 7f2f0b8a6e..0000000000 --- a/src/main/java/tools/MapleLogger.java +++ /dev/null @@ -1,80 +0,0 @@ -/* - This file is part of the OdinMS Maple Story Server - Copyright (C) 2008 ~ 2010 Patrick Huy - Matthias Butz - Jan Christian Meyer - - This program is free software: you can redistribute it and/or modify - it under the terms of the GNU Affero General Public License version 3 - as published by the Free Software Foundation. You may not use, modify - or distribute this program under any other version of the - GNU Affero General Public License. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License - along with this program. If not, see . - */ -package tools; - -import java.util.HashSet; -import java.util.Set; - -import net.opcodes.RecvOpcode; -import client.MapleCharacter; -import client.MapleClient; - -/** - * Logs packets to console and file. - * - * @author Alan (SharpAceX) - */ - -public class MapleLogger { - - public static Set monitored = new HashSet<>(); - public static Set ignored = new HashSet<>(); - - public static void logRecv(MapleClient c, short packetId, Object message) { - MapleCharacter chr = c.getPlayer(); - if (chr == null){ - return; - } - if (!monitored.contains(chr.getId())){ - return; - } - RecvOpcode op = getOpcodeFromValue(packetId); - if (isRecvBlocked(op)){ - return; - } - String packet = op.toString() + "\r\n" + HexTool.toString((byte[]) message); - FilePrinter.printError(FilePrinter.PACKET_LOGS + c.getAccountName() + "-" + chr.getName() + ".txt", packet); - } - - private static final boolean isRecvBlocked(RecvOpcode op){ - switch(op){ - case MOVE_PLAYER: - case GENERAL_CHAT: - case TAKE_DAMAGE: - case MOVE_PET: - case MOVE_LIFE: - case NPC_ACTION: - case FACE_EXPRESSION: - return true; - default: - return false; - } - } - - private static final RecvOpcode getOpcodeFromValue(int value){ - for (RecvOpcode op : RecvOpcode.values()){ - if (op.getValue() == value){ - return op; - } - } - return null; - } -} diff --git a/src/main/java/tools/MaplePacketCreator.java b/src/main/java/tools/MaplePacketCreator.java index 1f163c2efd..c84994030b 100644 --- a/src/main/java/tools/MaplePacketCreator.java +++ b/src/main/java/tools/MaplePacketCreator.java @@ -36,7 +36,7 @@ import constants.inventory.ItemConstants; import constants.skills.Buccaneer; import constants.skills.Corsair; import constants.skills.ThunderBreaker; -import net.netty.InitializationVector; +import net.encryption.InitializationVector; import net.opcodes.SendOpcode; import net.server.PlayerCoolDownValueHolder; import net.server.Server; From 5f149f0a2789611ee5036c1905ec0f9c28a9ea37 Mon Sep 17 00:00:00 2001 From: P0nk Date: Sun, 18 Jul 2021 15:38:00 +0200 Subject: [PATCH 33/37] Configure logging to be less verbose Packet logging to the console is especially concise --- .../net/packet/logging/InPacketLogger.java | 2 +- .../net/packet/logging/OutPacketLogger.java | 4 ++-- src/main/resources/log4j2.xml | 18 +++++++++++++++--- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/main/java/net/packet/logging/InPacketLogger.java b/src/main/java/net/packet/logging/InPacketLogger.java index 604f76eb80..da9e8aa16d 100644 --- a/src/main/java/net/packet/logging/InPacketLogger.java +++ b/src/main/java/net/packet/logging/InPacketLogger.java @@ -34,7 +34,7 @@ public class InPacketLogger extends ChannelInboundHandlerAdapter implements Pack final String opcodeHex = Integer.toHexString(opcode).toUpperCase(); final String opcodeName = getRecvOpcodeName(opcode); final String prefix = opcodeName == null ? " " : ""; - log.info("{}ClientSend:{} [{}] ({}) - hex:{} - text:{}", prefix, opcodeName, opcodeHex, packetLength, + log.debug("{}ClientSend:{} [{}] ({}) {} {}", prefix, opcodeName, opcodeHex, packetLength, HexTool.toString(content), HexTool.toStringFromAscii(content)); } else { log.debug(HexTool.toString(new byte[]{content[0], content[1]}) + "..."); diff --git a/src/main/java/net/packet/logging/OutPacketLogger.java b/src/main/java/net/packet/logging/OutPacketLogger.java index 8c8e1fd3a9..ea8a96356e 100644 --- a/src/main/java/net/packet/logging/OutPacketLogger.java +++ b/src/main/java/net/packet/logging/OutPacketLogger.java @@ -35,10 +35,10 @@ public class OutPacketLogger extends ChannelOutboundHandlerAdapter implements Pa String opcodeHex = Integer.toHexString(opcode).toUpperCase(); String opcodeName = getSendOpcodeName(opcode); String prefix = opcodeName == null ? " " : ""; - log.info("{}ServerSend:{} [{}] ({}) - hex:{} - text:{}", prefix, opcodeName, opcodeHex, packetLength, + log.debug("{}ServerSend:{} [{}] ({}) {} {}", prefix, opcodeName, opcodeHex, packetLength, HexTool.toString(content), HexTool.toStringFromAscii(content)); } else { - log.info(HexTool.toString(new byte[]{content[0], content[1]}) + " ..."); + log.debug(HexTool.toString(new byte[]{content[0], content[1]}) + " ..."); } } diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml index ba1769d487..3b0f9d5966 100644 --- a/src/main/resources/log4j2.xml +++ b/src/main/resources/log4j2.xml @@ -2,11 +2,13 @@ cosmic-log + %d{HH:mm:ss.SSS} [%t] %-5level %logger{2} - %msg%n + %d{HH:mm:ss.SSS} %-15logger{1} - %msg%n - %d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n + ${standard-pattern} @@ -14,20 +16,30 @@ fileName="logs/${filename}.log" filePattern="logs/$${date:yyyy-MM}/$${date:yyyy-MM-dd}/${filename}-%d{yyyy-MM-dd_HH-mm-ss}-%i.log"> - %d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n + ${standard-pattern} + + + + ${packet-pattern} + + - + + + + + From 850d44d5490ab3b15eea2b07f6f52dc4828e7629 Mon Sep 17 00:00:00 2001 From: P0nk Date: Sun, 18 Jul 2021 16:05:34 +0200 Subject: [PATCH 34/37] Minor cleanup - pom and encryption package --- pom.xml | 24 ++++++++++--------- .../MapleCustomEncryption.java | 2 +- .../java/net/encryption/PacketDecoder.java | 1 - .../java/net/encryption/PacketEncoder.java | 1 - 4 files changed, 14 insertions(+), 14 deletions(-) rename src/main/java/net/{ => encryption}/MapleCustomEncryption.java (99%) diff --git a/pom.xml b/pom.xml index ac8592c6ee..0334d6b9c4 100644 --- a/pom.xml +++ b/pom.xml @@ -19,6 +19,8 @@ 2.14.1 21.1.0 + 4.1.65.Final + 5.7.2 @@ -32,6 +34,11 @@ jcip-annotations 1.0 + + commons-io + commons-io + 2.10.0 + @@ -49,27 +56,22 @@ io.netty netty-transport - 4.1.65.Final + ${netty.version} io.netty netty-codec - 4.1.65.Final + ${netty.version} io.netty netty-buffer - 4.1.65.Final - - - commons-io - commons-io - 2.10.0 + ${netty.version} io.netty netty-handler - 4.1.65.Final + ${netty.version} @@ -110,12 +112,12 @@ org.junit.jupiter junit-jupiter-api - 5.7.2 + ${junit.version} org.junit.jupiter junit-jupiter-engine - 5.7.2 + ${junit.version} diff --git a/src/main/java/net/MapleCustomEncryption.java b/src/main/java/net/encryption/MapleCustomEncryption.java similarity index 99% rename from src/main/java/net/MapleCustomEncryption.java rename to src/main/java/net/encryption/MapleCustomEncryption.java index e6063279b9..67e94844c3 100644 --- a/src/main/java/net/MapleCustomEncryption.java +++ b/src/main/java/net/encryption/MapleCustomEncryption.java @@ -19,7 +19,7 @@ You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ -package net; +package net.encryption; public class MapleCustomEncryption { private static byte rollLeft(byte in, int count) { diff --git a/src/main/java/net/encryption/PacketDecoder.java b/src/main/java/net/encryption/PacketDecoder.java index cec48f5bcb..896d565d0f 100644 --- a/src/main/java/net/encryption/PacketDecoder.java +++ b/src/main/java/net/encryption/PacketDecoder.java @@ -4,7 +4,6 @@ import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.ReplayingDecoder; -import net.MapleCustomEncryption; import net.netty.InvalidPacketHeaderException; import net.packet.ByteBufInPacket; diff --git a/src/main/java/net/encryption/PacketEncoder.java b/src/main/java/net/encryption/PacketEncoder.java index 7a297116c6..d558e7b13b 100644 --- a/src/main/java/net/encryption/PacketEncoder.java +++ b/src/main/java/net/encryption/PacketEncoder.java @@ -3,7 +3,6 @@ package net.encryption; import io.netty.buffer.ByteBuf; import io.netty.channel.ChannelHandlerContext; import io.netty.handler.codec.MessageToByteEncoder; -import net.MapleCustomEncryption; import net.packet.OutPacket; public class PacketEncoder extends MessageToByteEncoder { From 435cafd381cbdc9d55dcdb9f09029cad6d6628c2 Mon Sep 17 00:00:00 2001 From: P0nk Date: Sun, 18 Jul 2021 16:27:41 +0200 Subject: [PATCH 35/37] Refactor MapleClient constructor, fix issue with multiclient check --- src/main/java/client/MapleClient.java | 24 ++++++++++++------- .../net/netty/ChannelServerInitializer.java | 5 ++-- .../net/netty/LoginServerInitializer.java | 5 ++-- .../net/netty/ServerChannelInitializer.java | 20 ++++++++++++++++ 4 files changed, 42 insertions(+), 12 deletions(-) diff --git a/src/main/java/client/MapleClient.java b/src/main/java/client/MapleClient.java index ec8643cd04..582dd99758 100644 --- a/src/main/java/client/MapleClient.java +++ b/src/main/java/client/MapleClient.java @@ -88,13 +88,14 @@ public class MapleClient extends ChannelInboundHandlerAdapter { public static final int LOGIN_LOGGEDIN = 2; private final Type type; + private final long sessionId; + private final PacketProcessor packetProcessor; private Hwid hwid; private String remoteAddress; private volatile boolean inTransition; private io.netty.channel.Channel ioChannel; - private PacketProcessor packetProcessor; private MapleCharacter player; private int channel = 1; private int accId = -4; @@ -126,7 +127,6 @@ public class MapleClient extends ChannelInboundHandlerAdapter { private int voteTime = -1; private int visibleWorlds; private long lastNpcClick; - private long sessionId; private long lastPacket = System.currentTimeMillis(); private int lang = 0; @@ -135,15 +135,27 @@ public class MapleClient extends ChannelInboundHandlerAdapter { CHANNEL } - public MapleClient(Type type, PacketProcessor packetProcessor, int world, int channel) { + public MapleClient(Type type, long sessionId, String remoteAddress, PacketProcessor packetProcessor, int world, int channel) { this.type = type; + this.sessionId = sessionId; + this.remoteAddress = remoteAddress; this.packetProcessor = packetProcessor; this.world = world; this.channel = channel; } + public static MapleClient createLoginClient(long sessionId, String remoteAddress, PacketProcessor packetProcessor, + int world, int channel) { + return new MapleClient(Type.LOGIN, sessionId, remoteAddress, packetProcessor, world, channel); + } + + public static MapleClient createChannelClient(long sessionId, String remoteAddress, PacketProcessor packetProcessor, + int world, int channel) { + return new MapleClient(Type.CHANNEL, sessionId, remoteAddress, packetProcessor, world, channel); + } + public static MapleClient createMock() { - return new MapleClient(null, null, -123, -123); + return new MapleClient(null, -1,null, null, -123, -123); } @Override @@ -1528,10 +1540,6 @@ public class MapleClient extends ChannelInboundHandlerAdapter { return this.sessionId; } - public void setSessionId(long sessionId) { - this.sessionId = sessionId; - } - public boolean canRequestCharlist() { return lastNpcClick + 877 < Server.getInstance().getCurrentTime(); } diff --git a/src/main/java/net/netty/ChannelServerInitializer.java b/src/main/java/net/netty/ChannelServerInitializer.java index 78be6e0ed9..13f3aff181 100644 --- a/src/main/java/net/netty/ChannelServerInitializer.java +++ b/src/main/java/net/netty/ChannelServerInitializer.java @@ -25,8 +25,9 @@ public class ChannelServerInitializer extends ServerChannelInitializer { log.debug("Client connecting to world {}, channel {} from {}", world, channel, clientIp); PacketProcessor packetProcessor = PacketProcessor.getChannelServerProcessor(world, channel); - final MapleClient client = new MapleClient(MapleClient.Type.CHANNEL, packetProcessor, world, channel); - client.setSessionId(sessionId.getAndIncrement()); + final long clientSessionId = sessionId.getAndIncrement(); + final String remoteAddress = getRemoteAddress(socketChannel); + final MapleClient client = MapleClient.createChannelClient(clientSessionId, remoteAddress, packetProcessor, world, channel); if (Server.getInstance().getChannel(world, channel) == null) { MapleSessionCoordinator.getInstance().closeSession(client, true); diff --git a/src/main/java/net/netty/LoginServerInitializer.java b/src/main/java/net/netty/LoginServerInitializer.java index 1d9795e011..5294e5ead1 100644 --- a/src/main/java/net/netty/LoginServerInitializer.java +++ b/src/main/java/net/netty/LoginServerInitializer.java @@ -16,8 +16,9 @@ public class LoginServerInitializer extends ServerChannelInitializer { log.debug("Client connected to login server from {} ", clientIp); PacketProcessor packetProcessor = PacketProcessor.getLoginServerProcessor(); - final MapleClient client = new MapleClient(MapleClient.Type.LOGIN, packetProcessor, LoginServer.WORLD_ID, LoginServer.CHANNEL_ID); - client.setSessionId(sessionId.getAndIncrement()); + final long clientSessionId = sessionId.getAndIncrement(); + final String remoteAddress = getRemoteAddress(socketChannel); + final MapleClient client = MapleClient.createLoginClient(clientSessionId, remoteAddress, packetProcessor, LoginServer.WORLD_ID, LoginServer.CHANNEL_ID); if (!MapleSessionCoordinator.getInstance().canStartLoginSession(client)) { socketChannel.close(); diff --git a/src/main/java/net/netty/ServerChannelInitializer.java b/src/main/java/net/netty/ServerChannelInitializer.java index 9d9f0c6e78..9e520f593d 100644 --- a/src/main/java/net/netty/ServerChannelInitializer.java +++ b/src/main/java/net/netty/ServerChannelInitializer.java @@ -4,6 +4,7 @@ import client.MapleClient; import config.YamlConfig; import constants.net.ServerConstants; import io.netty.buffer.Unpooled; +import io.netty.channel.Channel; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelInitializer; import io.netty.channel.ChannelPipeline; @@ -14,11 +15,16 @@ import net.encryption.InitializationVector; import net.encryption.PacketCodec; import net.packet.logging.InPacketLogger; import net.packet.logging.OutPacketLogger; +import net.server.coordinator.session.IpAddresses; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import tools.MaplePacketCreator; +import java.net.InetSocketAddress; import java.util.concurrent.atomic.AtomicLong; public abstract class ServerChannelInitializer extends ChannelInitializer { + private static final Logger log = LoggerFactory.getLogger(ServerChannelInitializer.class); private static final int IDLE_TIME_SECONDS = 30; private static final boolean LOG_PACKETS = YamlConfig.config.server.USE_DEBUG_SHOW_PACKET; private static final ChannelHandler sendPacketLogger = new OutPacketLogger(); @@ -26,6 +32,20 @@ public abstract class ServerChannelInitializer extends ChannelInitializer Date: Wed, 18 Aug 2021 11:58:52 +0200 Subject: [PATCH 36/37] Fix remote host not clearing after login This would cause the client to disconnect whenever exiting the game or returning to the login screen if the multiclient deterrent was enabled. --- .../session/MapleSessionCoordinator.java | 41 +++++++------------ 1 file changed, 15 insertions(+), 26 deletions(-) diff --git a/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java b/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java index dca6def3c1..39b7a67358 100644 --- a/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java +++ b/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java @@ -33,6 +33,7 @@ import java.sql.SQLException; import java.time.Instant; import java.util.*; import java.util.Map.Entry; +import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors; /** @@ -61,7 +62,7 @@ public class MapleSessionCoordinator { private final LoginStorage loginStorage = new LoginStorage(); private final Map onlineClients = new HashMap<>(); // Key: account id private final Set onlineRemoteHwids = new HashSet<>(); // Hwid/nibblehwid - private final Map> loginRemoteHosts = new HashMap<>(); // Key: Ip (+ nibblehwid) + private final Map loginRemoteHosts = new ConcurrentHashMap<>(); // Key: Ip (+ nibblehwid) private final HostHwidCache hostHwidCache = new HostHwidCache(); private MapleSessionCoordinator() { @@ -144,27 +145,20 @@ public class MapleSessionCoordinator { return false; } - addLoginRemoteHostClient(remoteHost, client); + loginRemoteHosts.put(remoteHost, client); return true; } finally { sessionInit.finalize(remoteHost); } } - private void addLoginRemoteHostClient(String remoteHost, MapleClient client) { - Set clients = new HashSet<>(2); - clients.add(client); - loginRemoteHosts.put(remoteHost, clients); - } - public void closeLoginSession(MapleClient client) { - String remoteHost = getSessionRemoteHost(client); - removeRemoteHostClient(remoteHost, client); + clearLoginRemoteHost(client); Hwid nibbleHwid = client.getHwid(); client.setHwid(null); if (nibbleHwid != null) { - onlineRemoteHwids.remove(nibbleHwid.hwid()); + onlineRemoteHwids.remove(nibbleHwid); if (client != null) { MapleClient loggedClient = onlineClients.get(client.getAccID()); @@ -177,15 +171,10 @@ public class MapleSessionCoordinator { } } - private void removeRemoteHostClient(String remoteHost, MapleClient client) { - Set clients = loginRemoteHosts.get(remoteHost); - if (clients != null) { - clients.remove(client); - - if (clients.isEmpty()) { - loginRemoteHosts.remove(remoteHost); - } - } + private void clearLoginRemoteHost(MapleClient client) { + String remoteHost = getSessionRemoteHost(client); + loginRemoteHosts.remove(client.getRemoteAddress()); + loginRemoteHosts.remove(remoteHost); } public AntiMulticlientResult attemptLoginSession(MapleClient client, Hwid hwid, int accountId, boolean routineCheck) { @@ -369,12 +358,12 @@ public class MapleSessionCoordinator { } if (!loginRemoteHosts.isEmpty()) { - List>> elist = new ArrayList<>(loginRemoteHosts.entrySet()); + List> elist = new ArrayList<>(loginRemoteHosts.entrySet()); elist.sort(Entry.comparingByKey()); System.out.println("Current login sessions: "); - for (Entry> e : elist) { - System.out.println(" " + e.getKey() + ", size: " + e.getValue().size()); + for (Entry e : elist) { + System.out.println(" " + e.getKey() + ", client: " + e.getValue()); } } } @@ -403,13 +392,13 @@ public class MapleSessionCoordinator { } if (!loginRemoteHosts.isEmpty()) { - List>> elist = new ArrayList<>(loginRemoteHosts.entrySet()); + List> elist = new ArrayList<>(loginRemoteHosts.entrySet()); elist.sort((e1, e2) -> e1.getKey().compareTo(e2.getKey())); str += ("Current login sessions:\r\n"); - for (Entry> e : elist) { - str += (" " + e.getKey() + ", IP: " + e.getValue() + "\r\n"); + for (Entry e : elist) { + str += (" " + e.getKey() + ", IP: " + e.getValue().getRemoteAddress() + "\r\n"); } } From 8c4d3092a7c1a24d6ce6384132ba78edf45fde7d Mon Sep 17 00:00:00 2001 From: P0nk Date: Wed, 18 Aug 2021 15:47:01 +0200 Subject: [PATCH 37/37] Remove "Maple" prefix from MapleSessionCoordinator name --- src/main/java/client/MapleClient.java | 22 +++++++++---------- .../commands/gm5/ShowSessionsCommand.java | 4 ++-- .../net/netty/ChannelServerInitializer.java | 4 ++-- .../net/netty/LoginServerInitializer.java | 4 ++-- src/main/java/net/server/Server.java | 6 ++--- .../handlers/PlayerLoggedinHandler.java | 4 ++-- .../session/InitializationResult.java | 2 +- ...ordinator.java => SessionCoordinator.java} | 12 +++++----- .../handlers/login/AfterLoginHandler.java | 4 ++-- .../handlers/login/CharSelectedHandler.java | 10 ++++----- .../login/CharSelectedWithPicHandler.java | 10 ++++----- .../handlers/login/RegisterPicHandler.java | 12 +++++----- .../handlers/login/RegisterPinHandler.java | 6 ++--- .../handlers/login/SetGenderHandler.java | 4 ++-- .../login/ViewAllCharRegisterPicHandler.java | 10 ++++----- .../login/ViewAllCharSelectedHandler.java | 10 ++++----- .../ViewAllCharSelectedWithPicHandler.java | 10 ++++----- .../net/server/task/LoginCoordinatorTask.java | 4 ++-- .../net/server/task/LoginStorageTask.java | 4 ++-- 19 files changed, 71 insertions(+), 71 deletions(-) rename src/main/java/net/server/coordinator/session/{MapleSessionCoordinator.java => SessionCoordinator.java} (97%) diff --git a/src/main/java/client/MapleClient.java b/src/main/java/client/MapleClient.java index 582dd99758..4d72ec87ba 100644 --- a/src/main/java/client/MapleClient.java +++ b/src/main/java/client/MapleClient.java @@ -42,8 +42,8 @@ import net.server.channel.Channel; import net.server.coordinator.login.MapleLoginBypassCoordinator; import net.server.coordinator.session.Hwid; import net.server.coordinator.session.IpAddresses; -import net.server.coordinator.session.MapleSessionCoordinator; -import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientResult; +import net.server.coordinator.session.SessionCoordinator; +import net.server.coordinator.session.SessionCoordinator.AntiMulticlientResult; import net.server.guild.MapleGuild; import net.server.guild.MapleGuildCharacter; import net.server.world.*; @@ -229,7 +229,7 @@ public class MapleClient extends ChannelInboundHandlerAdapter { } if (cause instanceof InvalidPacketHeaderException) { - MapleSessionCoordinator.getInstance().closeSession(this, true); + SessionCoordinator.getInstance().closeSession(this, true); } else if (cause instanceof IOException) { closeMapleSession(); } @@ -242,8 +242,8 @@ public class MapleClient extends ChannelInboundHandlerAdapter { private void closeMapleSession() { switch (type) { - case LOGIN -> MapleSessionCoordinator.getInstance().closeLoginSession(this); - case CHANNEL -> MapleSessionCoordinator.getInstance().closeSession(this, null); + case LOGIN -> SessionCoordinator.getInstance().closeLoginSession(this); + case CHANNEL -> SessionCoordinator.getInstance().closeSession(this, null); } try { @@ -580,7 +580,7 @@ public class MapleClient extends ChannelInboundHandlerAdapter { pinattempt++; if (pinattempt > 5) { - MapleSessionCoordinator.getInstance().closeSession(this, false); + SessionCoordinator.getInstance().closeSession(this, false); } if (pin.equals(other)) { pinattempt = 0; @@ -613,7 +613,7 @@ public class MapleClient extends ChannelInboundHandlerAdapter { picattempt++; if (picattempt > 5) { - MapleSessionCoordinator.getInstance().closeSession(this, false); + SessionCoordinator.getInstance().closeSession(this, false); } if (pic.equals(other)) { // thanks ryantpayton (HeavenClient) for noticing null pics being checked here picattempt = 0; @@ -629,7 +629,7 @@ public class MapleClient extends ChannelInboundHandlerAdapter { loginattempt++; if (loginattempt > 4) { loggedIn = false; - MapleSessionCoordinator.getInstance().closeSession(this, false); + SessionCoordinator.getInstance().closeSession(this, false); return 6; // thanks Survival_Project for finding out an issue with AUTOMATIC_REGISTER here } @@ -681,7 +681,7 @@ public class MapleClient extends ChannelInboundHandlerAdapter { } if (loginok == 0 || loginok == 4) { - AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptLoginSession(this, hwid, accId, loginok == 4); + AntiMulticlientResult res = SessionCoordinator.getInstance().attemptLoginSession(this, hwid, accId, loginok == 4); switch (res) { case SUCCESS: @@ -807,7 +807,7 @@ public class MapleClient extends ChannelInboundHandlerAdapter { public void updateLoginState(int newState) { // rules out possibility of multiple account entries if (newState == LOGIN_LOGGEDIN) { - MapleSessionCoordinator.getInstance().updateOnlineClient(this); + SessionCoordinator.getInstance().updateOnlineClient(this); } try (Connection con = DatabaseConnection.getConnection(); @@ -1042,7 +1042,7 @@ public class MapleClient extends ChannelInboundHandlerAdapter { } } - MapleSessionCoordinator.getInstance().closeSession(this, false); + SessionCoordinator.getInstance().closeSession(this, false); if (!serverTransition && isLoggedIn()) { updateLoginState(MapleClient.LOGIN_NOTLOGGEDIN); diff --git a/src/main/java/client/command/commands/gm5/ShowSessionsCommand.java b/src/main/java/client/command/commands/gm5/ShowSessionsCommand.java index 40ed9ed114..71d31c9982 100644 --- a/src/main/java/client/command/commands/gm5/ShowSessionsCommand.java +++ b/src/main/java/client/command/commands/gm5/ShowSessionsCommand.java @@ -21,7 +21,7 @@ package client.command.commands.gm5; import client.MapleClient; import client.command.Command; -import net.server.coordinator.session.MapleSessionCoordinator; +import net.server.coordinator.session.SessionCoordinator; /** * @@ -34,6 +34,6 @@ public class ShowSessionsCommand extends Command { @Override public void execute(MapleClient c, String[] params) { - MapleSessionCoordinator.getInstance().printSessionTrace(c); + SessionCoordinator.getInstance().printSessionTrace(c); } } diff --git a/src/main/java/net/netty/ChannelServerInitializer.java b/src/main/java/net/netty/ChannelServerInitializer.java index 13f3aff181..a4a64c15ce 100644 --- a/src/main/java/net/netty/ChannelServerInitializer.java +++ b/src/main/java/net/netty/ChannelServerInitializer.java @@ -4,7 +4,7 @@ import client.MapleClient; import io.netty.channel.socket.SocketChannel; import net.PacketProcessor; import net.server.Server; -import net.server.coordinator.session.MapleSessionCoordinator; +import net.server.coordinator.session.SessionCoordinator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -30,7 +30,7 @@ public class ChannelServerInitializer extends ServerChannelInitializer { final MapleClient client = MapleClient.createChannelClient(clientSessionId, remoteAddress, packetProcessor, world, channel); if (Server.getInstance().getChannel(world, channel) == null) { - MapleSessionCoordinator.getInstance().closeSession(client, true); + SessionCoordinator.getInstance().closeSession(client, true); socketChannel.close(); return; } diff --git a/src/main/java/net/netty/LoginServerInitializer.java b/src/main/java/net/netty/LoginServerInitializer.java index 5294e5ead1..f4ada797b1 100644 --- a/src/main/java/net/netty/LoginServerInitializer.java +++ b/src/main/java/net/netty/LoginServerInitializer.java @@ -3,7 +3,7 @@ package net.netty; import client.MapleClient; import io.netty.channel.socket.SocketChannel; import net.PacketProcessor; -import net.server.coordinator.session.MapleSessionCoordinator; +import net.server.coordinator.session.SessionCoordinator; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -20,7 +20,7 @@ public class LoginServerInitializer extends ServerChannelInitializer { final String remoteAddress = getRemoteAddress(socketChannel); final MapleClient client = MapleClient.createLoginClient(clientSessionId, remoteAddress, packetProcessor, LoginServer.WORLD_ID, LoginServer.CHANNEL_ID); - if (!MapleSessionCoordinator.getInstance().canStartLoginSession(client)) { + if (!SessionCoordinator.getInstance().canStartLoginSession(client)) { socketChannel.close(); return; } diff --git a/src/main/java/net/server/Server.java b/src/main/java/net/server/Server.java index fe515cef9c..b64eaa2daf 100644 --- a/src/main/java/net/server/Server.java +++ b/src/main/java/net/server/Server.java @@ -46,7 +46,7 @@ import net.server.audit.locks.factory.MonitoredReentrantLockFactory; import net.server.audit.locks.factory.MonitoredWriteLockFactory; import net.server.channel.Channel; import net.server.coordinator.session.IpAddresses; -import net.server.coordinator.session.MapleSessionCoordinator; +import net.server.coordinator.session.SessionCoordinator; import net.server.guild.MapleAlliance; import net.server.guild.MapleGuild; import net.server.guild.MapleGuildCharacter; @@ -1764,7 +1764,7 @@ public class Server { } private static String getRemoteHost(MapleClient client) { - return MapleSessionCoordinator.getSessionRemoteHost(client); + return SessionCoordinator.getSessionRemoteHost(client); } public void setCharacteridInTransition(MapleClient client, int charId) { @@ -1866,7 +1866,7 @@ public class Server { if (c.isLoggedIn()) { c.disconnect(false, false); } else { - MapleSessionCoordinator.getInstance().closeSession(c, true); + SessionCoordinator.getInstance().closeSession(c, true); } } } diff --git a/src/main/java/net/server/channel/handlers/PlayerLoggedinHandler.java b/src/main/java/net/server/channel/handlers/PlayerLoggedinHandler.java index 328e40d7ee..dfa22824e4 100644 --- a/src/main/java/net/server/channel/handlers/PlayerLoggedinHandler.java +++ b/src/main/java/net/server/channel/handlers/PlayerLoggedinHandler.java @@ -32,7 +32,7 @@ import net.server.Server; import net.server.channel.Channel; import net.server.channel.CharacterIdChannelPair; import net.server.coordinator.session.Hwid; -import net.server.coordinator.session.MapleSessionCoordinator; +import net.server.coordinator.session.SessionCoordinator; import net.server.coordinator.world.MapleEventRecallCoordinator; import net.server.guild.MapleAlliance; import net.server.guild.MapleGuild; @@ -114,7 +114,7 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler { final Hwid hwid; if (player == null) { - hwid = MapleSessionCoordinator.getInstance().pickLoginSessionHwid(c); + hwid = SessionCoordinator.getInstance().pickLoginSessionHwid(c); if (hwid == null) { c.disconnect(true, false); return; diff --git a/src/main/java/net/server/coordinator/session/InitializationResult.java b/src/main/java/net/server/coordinator/session/InitializationResult.java index dc18ef502c..3c6f596dfa 100644 --- a/src/main/java/net/server/coordinator/session/InitializationResult.java +++ b/src/main/java/net/server/coordinator/session/InitializationResult.java @@ -1,6 +1,6 @@ package net.server.coordinator.session; -import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientResult; +import net.server.coordinator.session.SessionCoordinator.AntiMulticlientResult; enum InitializationResult { SUCCESS(AntiMulticlientResult.SUCCESS), diff --git a/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java b/src/main/java/net/server/coordinator/session/SessionCoordinator.java similarity index 97% rename from src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java rename to src/main/java/net/server/coordinator/session/SessionCoordinator.java index 39b7a67358..5a266383e9 100644 --- a/src/main/java/net/server/coordinator/session/MapleSessionCoordinator.java +++ b/src/main/java/net/server/coordinator/session/SessionCoordinator.java @@ -40,11 +40,11 @@ import java.util.stream.Collectors; * * @author Ronan */ -public class MapleSessionCoordinator { - private static final Logger log = LoggerFactory.getLogger(MapleSessionCoordinator.class); - private static final MapleSessionCoordinator instance = new MapleSessionCoordinator(); +public class SessionCoordinator { + private static final Logger log = LoggerFactory.getLogger(SessionCoordinator.class); + private static final SessionCoordinator instance = new SessionCoordinator(); - public static MapleSessionCoordinator getInstance() { + public static SessionCoordinator getInstance() { return instance; } @@ -65,7 +65,7 @@ public class MapleSessionCoordinator { private final Map loginRemoteHosts = new ConcurrentHashMap<>(); // Key: Ip (+ nibblehwid) private final HostHwidCache hostHwidCache = new HostHwidCache(); - private MapleSessionCoordinator() { + private SessionCoordinator() { } private static boolean attemptAccountAccess(int accountId, Hwid hwid, boolean routineCheck) { @@ -269,7 +269,7 @@ public class MapleSessionCoordinator { } private static MapleClient fetchInTransitionSessionClient(MapleClient client) { - Hwid hwid = MapleSessionCoordinator.getInstance().getGameSessionHwid(client); + Hwid hwid = SessionCoordinator.getInstance().getGameSessionHwid(client); if (hwid == null) { // maybe this session was currently in-transition? return null; } diff --git a/src/main/java/net/server/handlers/login/AfterLoginHandler.java b/src/main/java/net/server/handlers/login/AfterLoginHandler.java index 1364367a3d..2a4b306665 100644 --- a/src/main/java/net/server/handlers/login/AfterLoginHandler.java +++ b/src/main/java/net/server/handlers/login/AfterLoginHandler.java @@ -23,7 +23,7 @@ package net.server.handlers.login; import client.MapleClient; import net.AbstractMaplePacketHandler; -import net.server.coordinator.session.MapleSessionCoordinator; +import net.server.coordinator.session.SessionCoordinator; import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; @@ -57,7 +57,7 @@ public final class AfterLoginHandler extends AbstractMaplePacketHandler { c.announce(MaplePacketCreator.requestPinAfterFailure()); } } else if (c2 == 0 && c3 == 5) { - MapleSessionCoordinator.getInstance().closeSession(c, null); + SessionCoordinator.getInstance().closeSession(c, null); c.updateLoginState(MapleClient.LOGIN_NOTLOGGEDIN); } } diff --git a/src/main/java/net/server/handlers/login/CharSelectedHandler.java b/src/main/java/net/server/handlers/login/CharSelectedHandler.java index 4e032335d3..b879b30c21 100644 --- a/src/main/java/net/server/handlers/login/CharSelectedHandler.java +++ b/src/main/java/net/server/handlers/login/CharSelectedHandler.java @@ -25,8 +25,8 @@ import client.MapleClient; import net.AbstractMaplePacketHandler; import net.server.Server; import net.server.coordinator.session.Hwid; -import net.server.coordinator.session.MapleSessionCoordinator; -import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientResult; +import net.server.coordinator.session.SessionCoordinator; +import net.server.coordinator.session.SessionCoordinator.AntiMulticlientResult; import net.server.world.World; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -68,20 +68,20 @@ public final class CharSelectedHandler extends AbstractMaplePacketHandler { c.updateMacs(macs); c.updateHwid(hwid); - AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptGameSession(c, c.getAccID(), hwid); + AntiMulticlientResult res = SessionCoordinator.getInstance().attemptGameSession(c, c.getAccID(), hwid); if (res != AntiMulticlientResult.SUCCESS) { c.announce(MaplePacketCreator.getAfterLoginError(parseAntiMulticlientError(res))); return; } if (c.hasBannedMac() || c.hasBannedHWID()) { - MapleSessionCoordinator.getInstance().closeSession(c, true); + SessionCoordinator.getInstance().closeSession(c, true); return; } Server server = Server.getInstance(); if(!server.haveCharacterEntry(c.getAccID(), charId)) { - MapleSessionCoordinator.getInstance().closeSession(c, true); + SessionCoordinator.getInstance().closeSession(c, true); return; } diff --git a/src/main/java/net/server/handlers/login/CharSelectedWithPicHandler.java b/src/main/java/net/server/handlers/login/CharSelectedWithPicHandler.java index 6a2191db25..7311b931df 100644 --- a/src/main/java/net/server/handlers/login/CharSelectedWithPicHandler.java +++ b/src/main/java/net/server/handlers/login/CharSelectedWithPicHandler.java @@ -4,8 +4,8 @@ import client.MapleClient; import net.AbstractMaplePacketHandler; import net.server.Server; import net.server.coordinator.session.Hwid; -import net.server.coordinator.session.MapleSessionCoordinator; -import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientResult; +import net.server.coordinator.session.SessionCoordinator; +import net.server.coordinator.session.SessionCoordinator.AntiMulticlientResult; import net.server.world.World; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,13 +49,13 @@ public class CharSelectedWithPicHandler extends AbstractMaplePacketHandler { c.updateHwid(hwid); if (c.hasBannedMac() || c.hasBannedHWID()) { - MapleSessionCoordinator.getInstance().closeSession(c, true); + SessionCoordinator.getInstance().closeSession(c, true); return; } Server server = Server.getInstance(); if(!server.haveCharacterEntry(c.getAccID(), charId)) { - MapleSessionCoordinator.getInstance().closeSession(c, true); + SessionCoordinator.getInstance().closeSession(c, true); return; } @@ -73,7 +73,7 @@ public class CharSelectedWithPicHandler extends AbstractMaplePacketHandler { return; } - AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptGameSession(c, c.getAccID(), hwid); + AntiMulticlientResult res = SessionCoordinator.getInstance().attemptGameSession(c, c.getAccID(), hwid); if (res != AntiMulticlientResult.SUCCESS) { c.announce(MaplePacketCreator.getAfterLoginError(parseAntiMulticlientError(res))); return; diff --git a/src/main/java/net/server/handlers/login/RegisterPicHandler.java b/src/main/java/net/server/handlers/login/RegisterPicHandler.java index f1e7f7315f..8d3f4a7ad8 100644 --- a/src/main/java/net/server/handlers/login/RegisterPicHandler.java +++ b/src/main/java/net/server/handlers/login/RegisterPicHandler.java @@ -4,8 +4,8 @@ import client.MapleClient; import net.AbstractMaplePacketHandler; import net.server.Server; import net.server.coordinator.session.Hwid; -import net.server.coordinator.session.MapleSessionCoordinator; -import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientResult; +import net.server.coordinator.session.SessionCoordinator; +import net.server.coordinator.session.SessionCoordinator.AntiMulticlientResult; import net.server.world.World; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -48,20 +48,20 @@ public final class RegisterPicHandler extends AbstractMaplePacketHandler { c.updateMacs(macs); c.updateHwid(hwid); - AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptGameSession(c, c.getAccID(), hwid); + AntiMulticlientResult res = SessionCoordinator.getInstance().attemptGameSession(c, c.getAccID(), hwid); if (res != AntiMulticlientResult.SUCCESS) { c.announce(MaplePacketCreator.getAfterLoginError(parseAntiMulticlientError(res))); return; } if (c.hasBannedMac() || c.hasBannedHWID()) { - MapleSessionCoordinator.getInstance().closeSession(c, true); + SessionCoordinator.getInstance().closeSession(c, true); return; } Server server = Server.getInstance(); if(!server.haveCharacterEntry(c.getAccID(), charId)) { - MapleSessionCoordinator.getInstance().closeSession(c, true); + SessionCoordinator.getInstance().closeSession(c, true); return; } @@ -91,7 +91,7 @@ public final class RegisterPicHandler extends AbstractMaplePacketHandler { e.printStackTrace(); } } else { - MapleSessionCoordinator.getInstance().closeSession(c, true); + SessionCoordinator.getInstance().closeSession(c, true); } } } \ No newline at end of file diff --git a/src/main/java/net/server/handlers/login/RegisterPinHandler.java b/src/main/java/net/server/handlers/login/RegisterPinHandler.java index 82b8d9cb9d..d35d6717e7 100644 --- a/src/main/java/net/server/handlers/login/RegisterPinHandler.java +++ b/src/main/java/net/server/handlers/login/RegisterPinHandler.java @@ -23,7 +23,7 @@ package net.server.handlers.login; import client.MapleClient; import net.AbstractMaplePacketHandler; -import net.server.coordinator.session.MapleSessionCoordinator; +import net.server.coordinator.session.SessionCoordinator; import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; @@ -35,7 +35,7 @@ public final class RegisterPinHandler extends AbstractMaplePacketHandler { public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { byte c2 = slea.readByte(); if (c2 == 0) { - MapleSessionCoordinator.getInstance().closeSession(c, null); + SessionCoordinator.getInstance().closeSession(c, null); c.updateLoginState(MapleClient.LOGIN_NOTLOGGEDIN); } else { String pin = slea.readMapleAsciiString(); @@ -43,7 +43,7 @@ public final class RegisterPinHandler extends AbstractMaplePacketHandler { c.setPin(pin); c.announce(MaplePacketCreator.pinRegistered()); - MapleSessionCoordinator.getInstance().closeSession(c, null); + SessionCoordinator.getInstance().closeSession(c, null); c.updateLoginState(MapleClient.LOGIN_NOTLOGGEDIN); } } diff --git a/src/main/java/net/server/handlers/login/SetGenderHandler.java b/src/main/java/net/server/handlers/login/SetGenderHandler.java index c208b78946..50c9292643 100644 --- a/src/main/java/net/server/handlers/login/SetGenderHandler.java +++ b/src/main/java/net/server/handlers/login/SetGenderHandler.java @@ -25,7 +25,7 @@ package net.server.handlers.login; import client.MapleClient; import net.AbstractMaplePacketHandler; import net.server.Server; -import net.server.coordinator.session.MapleSessionCoordinator; +import net.server.coordinator.session.SessionCoordinator; import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; @@ -44,7 +44,7 @@ public class SetGenderHandler extends AbstractMaplePacketHandler { Server.getInstance().registerLoginState(c); } else { - MapleSessionCoordinator.getInstance().closeSession(c, null); + SessionCoordinator.getInstance().closeSession(c, null); c.updateLoginState(MapleClient.LOGIN_NOTLOGGEDIN); } } diff --git a/src/main/java/net/server/handlers/login/ViewAllCharRegisterPicHandler.java b/src/main/java/net/server/handlers/login/ViewAllCharRegisterPicHandler.java index 47e4cbbdef..a2fc4842b5 100644 --- a/src/main/java/net/server/handlers/login/ViewAllCharRegisterPicHandler.java +++ b/src/main/java/net/server/handlers/login/ViewAllCharRegisterPicHandler.java @@ -4,8 +4,8 @@ import client.MapleClient; import net.AbstractMaplePacketHandler; import net.server.Server; import net.server.coordinator.session.Hwid; -import net.server.coordinator.session.MapleSessionCoordinator; -import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientResult; +import net.server.coordinator.session.SessionCoordinator; +import net.server.coordinator.session.SessionCoordinator.AntiMulticlientResult; import net.server.world.World; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -51,11 +51,11 @@ public final class ViewAllCharRegisterPicHandler extends AbstractMaplePacketHand c.updateHwid(hwid); if (c.hasBannedMac() || c.hasBannedHWID()) { - MapleSessionCoordinator.getInstance().closeSession(c, true); + SessionCoordinator.getInstance().closeSession(c, true); return; } - AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptGameSession(c, c.getAccID(), hwid); + AntiMulticlientResult res = SessionCoordinator.getInstance().attemptGameSession(c, c.getAccID(), hwid); if (res != AntiMulticlientResult.SUCCESS) { c.announce(MaplePacketCreator.getAfterLoginError(parseAntiMulticlientError(res))); return; @@ -63,7 +63,7 @@ public final class ViewAllCharRegisterPicHandler extends AbstractMaplePacketHand Server server = Server.getInstance(); if(!server.haveCharacterEntry(c.getAccID(), charId)) { - MapleSessionCoordinator.getInstance().closeSession(c, true); + SessionCoordinator.getInstance().closeSession(c, true); return; } diff --git a/src/main/java/net/server/handlers/login/ViewAllCharSelectedHandler.java b/src/main/java/net/server/handlers/login/ViewAllCharSelectedHandler.java index 32e8cb86b4..82157f8c89 100644 --- a/src/main/java/net/server/handlers/login/ViewAllCharSelectedHandler.java +++ b/src/main/java/net/server/handlers/login/ViewAllCharSelectedHandler.java @@ -25,8 +25,8 @@ import client.MapleClient; import net.AbstractMaplePacketHandler; import net.server.Server; import net.server.coordinator.session.Hwid; -import net.server.coordinator.session.MapleSessionCoordinator; -import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientResult; +import net.server.coordinator.session.SessionCoordinator; +import net.server.coordinator.session.SessionCoordinator.AntiMulticlientResult; import net.server.world.World; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -71,11 +71,11 @@ public final class ViewAllCharSelectedHandler extends AbstractMaplePacketHandler c.updateHwid(hwid); if (c.hasBannedMac() || c.hasBannedHWID()) { - MapleSessionCoordinator.getInstance().closeSession(c, true); + SessionCoordinator.getInstance().closeSession(c, true); return; } - AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptGameSession(c, c.getAccID(), hwid); + AntiMulticlientResult res = SessionCoordinator.getInstance().attemptGameSession(c, c.getAccID(), hwid); if (res != AntiMulticlientResult.SUCCESS) { c.announce(MaplePacketCreator.getAfterLoginError(parseAntiMulticlientError(res))); return; @@ -83,7 +83,7 @@ public final class ViewAllCharSelectedHandler extends AbstractMaplePacketHandler Server server = Server.getInstance(); if(!server.haveCharacterEntry(c.getAccID(), charId)) { - MapleSessionCoordinator.getInstance().closeSession(c, true); + SessionCoordinator.getInstance().closeSession(c, true); return; } diff --git a/src/main/java/net/server/handlers/login/ViewAllCharSelectedWithPicHandler.java b/src/main/java/net/server/handlers/login/ViewAllCharSelectedWithPicHandler.java index b5fbc40b5d..2b3a33e14c 100644 --- a/src/main/java/net/server/handlers/login/ViewAllCharSelectedWithPicHandler.java +++ b/src/main/java/net/server/handlers/login/ViewAllCharSelectedWithPicHandler.java @@ -4,8 +4,8 @@ import client.MapleClient; import net.AbstractMaplePacketHandler; import net.server.Server; import net.server.coordinator.session.Hwid; -import net.server.coordinator.session.MapleSessionCoordinator; -import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientResult; +import net.server.coordinator.session.SessionCoordinator; +import net.server.coordinator.session.SessionCoordinator.AntiMulticlientResult; import net.server.world.World; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -52,13 +52,13 @@ public class ViewAllCharSelectedWithPicHandler extends AbstractMaplePacketHandle c.updateHwid(hwid); if (c.hasBannedMac() || c.hasBannedHWID()) { - MapleSessionCoordinator.getInstance().closeSession(c, true); + SessionCoordinator.getInstance().closeSession(c, true); return; } Server server = Server.getInstance(); if(!server.haveCharacterEntry(c.getAccID(), charId)) { - MapleSessionCoordinator.getInstance().closeSession(c, true); + SessionCoordinator.getInstance().closeSession(c, true); return; } @@ -79,7 +79,7 @@ public class ViewAllCharSelectedWithPicHandler extends AbstractMaplePacketHandle return; } - AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptGameSession(c, c.getAccID(), hwid); + AntiMulticlientResult res = SessionCoordinator.getInstance().attemptGameSession(c, c.getAccID(), hwid); if (res != AntiMulticlientResult.SUCCESS) { c.announce(MaplePacketCreator.getAfterLoginError(parseAntiMulticlientError(res))); return; diff --git a/src/main/java/net/server/task/LoginCoordinatorTask.java b/src/main/java/net/server/task/LoginCoordinatorTask.java index 6efe23b2dd..7f37434d62 100644 --- a/src/main/java/net/server/task/LoginCoordinatorTask.java +++ b/src/main/java/net/server/task/LoginCoordinatorTask.java @@ -19,7 +19,7 @@ */ package net.server.task; -import net.server.coordinator.session.MapleSessionCoordinator; +import net.server.coordinator.session.SessionCoordinator; /** * @@ -29,6 +29,6 @@ public class LoginCoordinatorTask implements Runnable { @Override public void run() { - MapleSessionCoordinator.getInstance().clearExpiredHwidHistory(); + SessionCoordinator.getInstance().clearExpiredHwidHistory(); } } diff --git a/src/main/java/net/server/task/LoginStorageTask.java b/src/main/java/net/server/task/LoginStorageTask.java index a7b99eaa84..fdf60eeb82 100644 --- a/src/main/java/net/server/task/LoginStorageTask.java +++ b/src/main/java/net/server/task/LoginStorageTask.java @@ -19,7 +19,7 @@ */ package net.server.task; -import net.server.coordinator.session.MapleSessionCoordinator; +import net.server.coordinator.session.SessionCoordinator; import net.server.coordinator.login.MapleLoginBypassCoordinator; /** @@ -30,7 +30,7 @@ public class LoginStorageTask implements Runnable { @Override public void run() { - MapleSessionCoordinator.getInstance().runUpdateLoginHistory(); + SessionCoordinator.getInstance().runUpdateLoginHistory(); MapleLoginBypassCoordinator.getInstance().runUpdateLoginBypass(); } }