From 513c9b615d977ea67aafcf93ec082143a9c8a466 Mon Sep 17 00:00:00 2001 From: P0nk Date: Sat, 26 Mar 2022 15:51:16 +0100 Subject: [PATCH 1/2] Rework View all characters - fix packet and limit max chrs Thanks to Lehava#4668 for finding the packet structure --- src/main/java/net/server/Server.java | 26 ++--- .../handlers/login/ViewAllCharHandler.java | 105 +++++++++++++----- src/main/java/net/server/world/World.java | 4 +- src/main/java/tools/PacketCreator.java | 8 +- 4 files changed, 97 insertions(+), 46 deletions(-) diff --git a/src/main/java/net/server/Server.java b/src/main/java/net/server/Server.java index 0f8ee0b18c..5295eae428 100644 --- a/src/main/java/net/server/Server.java +++ b/src/main/java/net/server/Server.java @@ -1453,37 +1453,33 @@ public class Server { } */ - public Pair>, List>>> loadAccountCharlist(Integer accountId, int visibleWorlds) { - List wlist = this.getWorlds(); - if (wlist.size() > visibleWorlds) { - wlist = wlist.subList(0, visibleWorlds); + public SortedMap> loadAccountCharlist(int accountId, int visibleWorlds) { + List worlds = this.getWorlds(); + if (worlds.size() > visibleWorlds) { + worlds = worlds.subList(0, visibleWorlds); } - List>> accChars = new ArrayList<>(wlist.size() + 1); + SortedMap> worldChrs = new TreeMap<>(); int chrTotal = 0; - List lastwchars = null; lgnRLock.lock(); try { - for (World w : wlist) { - List wchars = w.getAccountCharactersView(accountId); - if (wchars == null) { + for (World world : worlds) { + List chrs = world.getAccountCharactersView(accountId); + if (chrs == null) { if (!accountChars.containsKey(accountId)) { accountCharacterCount.put(accountId, (short) 0); accountChars.put(accountId, new HashSet<>()); // not advisable at all to write on the map on a read-protected environment } // yet it's known there's no problem since no other point in the source does - } else if (!wchars.isEmpty()) { // this action. - lastwchars = wchars; - - accChars.add(new Pair<>(w.getId(), wchars)); - chrTotal += wchars.size(); + } else if (!chrs.isEmpty()) { // this action. + worldChrs.put(world.getId(), chrs); } } } finally { lgnRLock.unlock(); } - return new Pair<>(new Pair<>(chrTotal, lastwchars), accChars); + return worldChrs; } private static Pair>> loadAccountCharactersViewFromDb(int accId, int wlen) { diff --git a/src/main/java/net/server/handlers/login/ViewAllCharHandler.java b/src/main/java/net/server/handlers/login/ViewAllCharHandler.java index 19d79f5632..d48e7d6302 100644 --- a/src/main/java/net/server/handlers/login/ViewAllCharHandler.java +++ b/src/main/java/net/server/handlers/login/ViewAllCharHandler.java @@ -28,11 +28,12 @@ import net.AbstractPacketHandler; import net.packet.InPacket; import net.server.Server; import tools.PacketCreator; -import tools.Pair; -import java.util.List; +import java.util.*; public final class ViewAllCharHandler extends AbstractPacketHandler { + private static final int CHARACTER_LIMIT = 60; // Client will crash if sending 61 or more characters + @Override public final void handlePacket(InPacket p, Client c) { try { @@ -41,34 +42,88 @@ public final class ViewAllCharHandler extends AbstractPacketHandler { return; } - int accountId = c.getAccID(); - Pair>, List>>> loginBlob = Server.getInstance().loadAccountCharlist(accountId, c.getVisibleWorlds()); + SortedMap> worldChrs = Server.getInstance().loadAccountCharlist(c.getAccID(), c.getVisibleWorlds()); + worldChrs = limitTotalChrs(worldChrs, CHARACTER_LIMIT); - List>> worldChars = loginBlob.getRight(); - int chrTotal = loginBlob.getLeft().getLeft(); - List lastwchars = loginBlob.getLeft().getRight(); + padChrsIfNeeded(worldChrs); - if (chrTotal > 9) { - int padRight = chrTotal % 3; - if (padRight > 0 && lastwchars != null) { - Character chr = lastwchars.get(lastwchars.size() - 1); + int totalWorlds = worldChrs.size(); + int totalChrs = countTotalChrs(worldChrs); + c.sendPacket(PacketCreator.showAllCharacter(totalWorlds, totalChrs)); - for (int i = padRight; i < 3; i++) { // filling the remaining slots with the last character loaded - chrTotal++; - lastwchars.add(chr); - } - } - } - - int charsSize = chrTotal; - int unk = charsSize + (3 - charsSize % 3); //rowSize? - c.sendPacket(PacketCreator.showAllCharacter(charsSize, unk)); - - for (Pair> wchars : worldChars) { - c.sendPacket(PacketCreator.showAllCharacterInfo(wchars.getLeft(), wchars.getRight(), YamlConfig.config.server.ENABLE_PIC && !c.canBypassPic())); - } + final boolean usePic = YamlConfig.config.server.ENABLE_PIC && !c.canBypassPic(); + worldChrs.forEach((worldId, chrs) -> + c.sendPacket(PacketCreator.showAllCharacterInfo(worldId, chrs, usePic)) + ); } catch (Exception e) { e.printStackTrace(); } } + + private static SortedMap> limitTotalChrs(SortedMap> worldChrs, + int limit) { + if (countTotalChrs(worldChrs) <= limit) { + return worldChrs; + } else {; + return cutAfterChrLimit(worldChrs, limit); + } + } + + private static int countTotalChrs(Map> worldChrs) { + return worldChrs.values().stream() + .mapToInt(List::size) + .sum(); + } + + private static SortedMap> cutAfterChrLimit(SortedMap> worldChrs, + int limit) { + SortedMap> cappedCopy = new TreeMap<>(); + int runningChrTotal = 0; + for (Map.Entry> entry : worldChrs.entrySet()) { + int worldId = entry.getKey(); + List chrs = entry.getValue(); + if (runningChrTotal + chrs.size() <= limit) { // Limit not reached, move them all + runningChrTotal += chrs.size(); + cappedCopy.put(worldId, chrs); + } else { // Limit would be reached if all chrs were moved. Move just enough to fit within limit. + int remainingSlots = limit - runningChrTotal; + List lastChrs = chrs.subList(0, remainingSlots); + cappedCopy.put(worldId, lastChrs); + break; + } + } + + return cappedCopy; + } + + /** + * If there are more characters than fits the screen (9), and you start scrolling down, + * the characters on the last row will not appear unless the row is completely filled. + * Meaning, if there are 1 or 2 characters remaining on the last row, they will not appear. + * + * @param totalChrs total amount of characters to display on 'View all characters' screen + * @return if we need to pad the last row to include the characters that would otherwise not appear + */ + private static void padChrsIfNeeded(SortedMap> worldChrs) { + while (shouldPadLastRow(countTotalChrs(worldChrs))) { + final List lastWorldChrs = getLastWorldChrs(worldChrs); + final Character lastChrForPadding = getLastItem(lastWorldChrs); + lastWorldChrs.add(lastChrForPadding); + } + } + + private static boolean shouldPadLastRow(int totalChrs) { + boolean shouldScroll = totalChrs > 9; + boolean isLastRowFilled = totalChrs % 3 == 0; + return shouldScroll && !isLastRowFilled; + } + + private static List getLastWorldChrs(SortedMap> worldChrs) { + return worldChrs.get(worldChrs.lastKey()); + } + + private static T getLastItem(List list) { + Objects.requireNonNull(list); + return list.get(list.size() - 1); + } } diff --git a/src/main/java/net/server/world/World.java b/src/main/java/net/server/world/World.java index 162bbc58b0..5b41cdf826 100644 --- a/src/main/java/net/server/world/World.java +++ b/src/main/java/net/server/world/World.java @@ -515,8 +515,8 @@ public class World { return chrList; } - public List getAccountCharactersView(Integer accountId) { - List chrList; + public List getAccountCharactersView(int accountId) { + final List chrList; accountCharsLock.lock(); try { diff --git a/src/main/java/tools/PacketCreator.java b/src/main/java/tools/PacketCreator.java index 24af096e62..8da4dc3320 100644 --- a/src/main/java/tools/PacketCreator.java +++ b/src/main/java/tools/PacketCreator.java @@ -2473,11 +2473,11 @@ public class PacketCreator { return p; } - public static Packet showAllCharacter(int chars, int unk) { + public static Packet showAllCharacter(int totalWorlds, int totalChrs) { OutPacket p = OutPacket.create(SendOpcode.VIEW_ALL_CHAR); - p.writeByte(chars > 0 ? 1 : 5); // 2: already connected to server, 3 : unk error (view-all-characters), 5 : cannot find any - p.writeInt(chars); - p.writeInt(unk); + p.writeByte(totalChrs > 0 ? 1 : 5); // 2: already connected to server, 3 : unk error (view-all-characters), 5 : cannot find any + p.writeInt(totalWorlds); + p.writeInt(totalChrs); return p; } From 570499f6ab535c2302661958182d437db41f7957 Mon Sep 17 00:00:00 2001 From: P0nk Date: Sat, 26 Mar 2022 16:30:43 +0100 Subject: [PATCH 2/2] Fix mixed up world ids --- config.yaml | 52 ++++++++++++++++++++++++++-------------------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/config.yaml b/config.yaml index 7aacf24b03..c6d0e0d6a3 100644 --- a/config.yaml +++ b/config.yaml @@ -86,18 +86,18 @@ worlds: why_am_i_recommended: Welcome to Galicia! channels: 3 - #Properties for El Nido 11 + #Properties for Kastia 11 - flag: 0 - server_message: Welcome to El Nido! - event_message: El Nido! - why_am_i_recommended: Welcome to El Nido! + server_message: Welcome to Kastia! + event_message: Kastia! + why_am_i_recommended: Welcome to Kastia! channels: 3 - #Properties for Zenith 12 + #Properties for Judis 12 - flag: 0 - server_message: Welcome to Zenith! - event_message: Zenith! - why_am_i_recommended: Welcome to Zenith! + server_message: Welcome to Judis! + event_message: Judis! + why_am_i_recommended: Welcome to Judis! channels: 3 #Properties for Arcenia 13 @@ -107,48 +107,48 @@ worlds: why_am_i_recommended: Welcome to Arcenia! channels: 3 - #Properties for Kastia 14 - - flag: 0 - server_message: Welcome to Kastia! - event_message: Kastia! - why_am_i_recommended: Welcome to Kastia! - channels: 3 - - #Properties for Judis 15 - - flag: 0 - server_message: Welcome to Judis! - event_message: Judis! - why_am_i_recommended: Welcome to Judis! - channels: 3 - - #Properties for Plana 16 + #Properties for Plana 14 - flag: 0 server_message: Welcome to Plana! event_message: Plana! why_am_i_recommended: Welcome to Plana! channels: 3 - #Properties for Kalluna 17 + #Properties for El Nido 15 + - flag: 0 + server_message: Welcome to El Nido! + event_message: El Nido! + why_am_i_recommended: Welcome to El Nido! + channels: 3 + + #Properties for Kalluna 16 - flag: 0 server_message: Welcome to Kalluna! event_message: Kalluna! why_am_i_recommended: Welcome to Kalluna! channels: 3 - #Properties for Stius 18 + #Properties for Stius 17 - flag: 0 server_message: Welcome to Stius! event_message: Stius! why_am_i_recommended: Welcome to Stius! channels: 3 - #Properties for Croa 19 + #Properties for Croa 18 - flag: 0 server_message: Welcome to Croa! event_message: Croa! why_am_i_recommended: Welcome to Croa! channels: 3 + #Properties for Zenith 19 + - flag: 0 + server_message: Welcome to Zenith! + event_message: Zenith! + why_am_i_recommended: Welcome to Zenith! + channels: 3 + #Properties for Medere 20 - flag: 0 server_message: Welcome to Medere!