for (RecvPacketOpcode op : RecvPacketOpcode.values()) {
...
}
Iterator<RecvPacketOpcode> it = RecvPacketOpcode.values().iterator();
while (it.hasNext()) {
RecvPacketOpcode op = (RecvPacketOpcode) it.next();
...
it.remove();
}
if (map.containsKey(packetHeader)) handlePacket
for (RecvOpcode r : opcodes) {
if (r.getValue() == packetHeader) {
handlePacket(opcode)
break;
}
}
...
void handlePacket(packetHeader)
switch (packetHeader) {
case 1: handlePacket
...
case 200: handlePacket
}
Map iteration is a slower than a simple switch. Most of the times, switches cannot compare strings (Java 6- and C++ for example), so maps are a 'good' replacement
//Initialize this at server startup. All values which do not have a handler should be set to a default handler which throws some sort of error.
function<void(packet*)> handlers[0x10000];
//When handling a packet
handlers[opcode](somepacket);
The most efficient way to handle opcode handlers is as follows:
This provides a truly constant lookup time, rather than a merely constant amortized time provided by a hash. As well, it ends up compiling to only a couple asm instructions while any sort of hash or tree can easily involve hundreds of instructions. Just because hash-based containers provide constant lookup time does not mean that lookup time is fast.Code://Initialize this at server startup. All values which do not have a handler should be set to a default handler which throws some sort of error. function<void(packet*)> handlers[0x10000]; //When handling a packet handlers[opcode](somepacket);
In this case both Celino and OdinMS provide subpar implementations for opcode handling.
@Override
public void messageReceived(final IoSession session, final Object message) throws Exception {
final SeekableLittleEndianAccessor slea = new GenericSeekableLittleEndianAccessor(new ByteArrayByteStream((byte[]) message));
final RecvPacketOpcode code = Header.get(slea.readShort());
if (code != null) {
final Client c = (Client) session.getAttribute(StringPool.CLIENT_KEY);
if (ServerConstants.enableHandledPacketLogging) {// Debugging
if (!code.isIgnorePacketSpamOrLog()) {
System.out.println(String.format("[%s] %s\nString : %s", code.name(), HexTool.toString((byte[]) message), HexTool.toStringFromAscii((byte[]) message)));
}
}
if (code.NeedsChecking()) {
if (!c.isLoggedIn()) {
session.close(true);
return;
}
/*
* if ((code.isCSOperation() && type == ServerType.CHANNEL) ||
* (!code.isCSOperation() && type != ServerType.CHANNEL)) {
* return; }
*/
}
final long cTime = System.currentTimeMillis();
final long Differences = cTime - c.LastCapturedTimeMillis_500MSThreshold;
if (Differences < 500) { // within 500ms
if (!code.isIgnorePacketSpamOrLog() && !c.FlagPendingDisconnection) { // Not move life, mob, summon, dragon
c.PacketSpamCountWithinHalfSecond++;
// 70 should be the acceptable level, but we will test with 200 first to make sure it doesn't affect laggers.
if (c.PacketSpamCountWithinHalfSecond > 200) { // Spam > 70 packet within 500ms = dc.
c.FlagPendingDisconnection();
ServerLog.RegisterForLogging(ServerLogType.PacketSpam,
String.format("[%s 0x%s] CharOrAccId : %s, Field : %s, Count : %d\nData : %s\nString : %s",
code.name(),
Integer.toHexString(code.getValue()),
c.getPlayer() != null ? c.getPlayer().getName() : "Accid=" + String.valueOf(c.getAccID()),
c.getPlayer() != null ? MapleMapFactory.getFullMapName(c.getPlayer().getMapId()) : "-1",
c.PacketSpamCountWithinHalfSecond,
HexTool.toString((byte[]) message),
HexTool.toStringFromAscii((byte[]) message)));
}
}
} else {
c.LastCapturedTimeMillis_500MSThreshold = cTime;
c.PacketSpamCountWithinHalfSecond = 0;
}
// This is also used throughout the packet handler to determine the current time instead of calling System.currentTimeMillis() again
c.LastCapturedTimeMillis = cTime;
// System.out.println("Differences : "+(Differences)+", count : "+c.PacketSpamCountWithinHalfSecond+", cTime : " + cTime);
handlePacket(code, slea, c, type);
} else if (ServerConstants.enableUnhandledPacketLogging) { // Console output part for debugging
System.out.println(String.format("[Unhandled Packet] %s\nString : %s", HexTool.toString((byte[]) message), HexTool.toStringFromAscii((byte[]) message)));
}
}
Not a fan of the structure, since I still prefer the older methods, such as in old Odin/Moople, but thanks for taking the time to show us some of your progress as a coder. Good luck with your future projects!That seems to be one of my old build that was leaked out. Unfortunately due to Asiasoft, I can't reveal much of the Celino (v115) Source code as much as I wish to.
However for educational purpose, here's my latest build dated June 2012 (The last time that it was last changed)
Code:@Override public void messageReceived(final IoSession session, final Object message) throws Exception { final SeekableLittleEndianAccessor slea = new GenericSeekableLittleEndianAccessor(new ByteArrayByteStream((byte[]) message)); final RecvPacketOpcode code = Header.get(slea.readShort()); if (code != null) { final Client c = (Client) session.getAttribute(StringPool.CLIENT_KEY); if (ServerConstants.enableHandledPacketLogging) {// Debugging if (!code.isIgnorePacketSpamOrLog()) { System.out.println(String.format("[%s] %s\nString : %s", code.name(), HexTool.toString((byte[]) message), HexTool.toStringFromAscii((byte[]) message))); } } if (code.NeedsChecking()) { if (!c.isLoggedIn()) { session.close(true); return; } /* * if ((code.isCSOperation() && type == ServerType.CHANNEL) || * (!code.isCSOperation() && type != ServerType.CHANNEL)) { * return; } */ } final long cTime = System.currentTimeMillis(); final long Differences = cTime - c.LastCapturedTimeMillis_500MSThreshold; if (Differences < 500) { // within 500ms if (!code.isIgnorePacketSpamOrLog() && !c.FlagPendingDisconnection) { // Not move life, mob, summon, dragon c.PacketSpamCountWithinHalfSecond++; // 70 should be the acceptable level, but we will test with 200 first to make sure it doesn't affect laggers. if (c.PacketSpamCountWithinHalfSecond > 200) { // Spam > 70 packet within 500ms = dc. c.FlagPendingDisconnection(); ServerLog.RegisterForLogging(ServerLogType.PacketSpam, String.format("[%s 0x%s] CharOrAccId : %s, Field : %s, Count : %d\nData : %s\nString : %s", code.name(), Integer.toHexString(code.getValue()), c.getPlayer() != null ? c.getPlayer().getName() : "Accid=" + String.valueOf(c.getAccID()), c.getPlayer() != null ? MapleMapFactory.getFullMapName(c.getPlayer().getMapId()) : "-1", c.PacketSpamCountWithinHalfSecond, HexTool.toString((byte[]) message), HexTool.toStringFromAscii((byte[]) message))); } } } else { c.LastCapturedTimeMillis_500MSThreshold = cTime; c.PacketSpamCountWithinHalfSecond = 0; } // This is also used throughout the packet handler to determine the current time instead of calling System.currentTimeMillis() again c.LastCapturedTimeMillis = cTime; // System.out.println("Differences : "+(Differences)+", count : "+c.PacketSpamCountWithinHalfSecond+", cTime : " + cTime); handlePacket(code, slea, c, type); } else if (ServerConstants.enableUnhandledPacketLogging) { // Console output part for debugging System.out.println(String.format("[Unhandled Packet] %s\nString : %s", HexTool.toString((byte[]) message), HexTool.toStringFromAscii((byte[]) message))); } }
or pastebin for better formatting :You must be registered to see links
It may not be the best way of doing it, but as you can see, I'm learning as I code: I've always been experimenting with different ways of doing things.
--
DO not ask me anything about maplestory btw.. I'm no longer working on it.
Have you bench marked this or is this just an assumption? :O I'm curious because Odin is structured with a version of Java which seems to be rather obsolete with the new improvements to it over the years. Or rather additions / changes.The idea was that the old way of doing it is actually faster than this way. Array lookup is faster than linear search through if statements. It's not that I prefer the old way of doing it, but rather that it is actually faster.
Have you bench marked this or is this just an assumption? :O I'm curious because Odin is structured with a version of Java which seems to be rather obsolete with the new improvements to it over the years. Or rather additions / changes.
I agree with Sparks. That is an interesting question. And, here is another question to add on top of his:
First of all, keep in mind, my understanding of MapleStory code/Java knowledge is rather limited.
Now then, just to clarify that I understand you correctly, 'array lookup' is basically searching for an element in the array, correct? If so, could you clarify what search method you'd be using for searching an array?
From my understanding, you would only be using linear search on an array if the array is unsorted. And if it is sorted, binary search would be the way to go, since it would be a lot faster. This is due to the fact that with linear search, it's worst case would be running 'n' times, where 'n' is the number of elements in the array. And it's average would be half of that. Meanwhile, binary search would also be the same, but it's average would be log n (which is a whole lot better)