Most visitors online was 8830 , on 6 Feb 2024
Join our community of MMO enthusiasts and game developers! By registering, you'll gain access to discussions on the latest developments in MMO server files and collaborate with like-minded individuals. Join us today and unlock the potential of MMO server development!
Join Today!I have no idea. I never knew why they did that, it's so much slower than the strategy pattern.Why people are using switch statements in sources v140+ and not using array(strategy pattern like), with handlers behind a interface like OdinMS does in v62?
You must be registered to see links...
That's true.There's a lot of questionable code written in public Java Emulators, you need to keep in mind there are no qualifications required for developing and releasing a 'repack'/source, which is why you'll find a lot of non-Odin code doesn't keep up to coding conventions.
It's because Celino did it in v82 for MSEA and everyone thought it was good because Celino had it. There's a very hivemind thinking process here.
Also doesn't help that decompiled nexon binaries does this.
It's because Celino did it in v82 for MSEA and everyone thought it was good because Celino had it. There's a very hivemind thinking process here.
Also doesn't help that decompiled nexon binaries does this.
That is not true. It's because RMZero used Celino as base. And there's lots v83+ (excluding v83, more likely v111+) source/repack used RMZero's source as base.
It term of performance, you won't find much differences.
There's also lots bad practices in RMZero's source. I'm not saying he's not a good coder, but lot of his codes are really ugly.
It's because Celino did it in v82 for MSEA and everyone thought it was good because Celino had it. There's a very hivemind thinking process here.
Also doesn't help that decompiled nexon binaries does this.
@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)));
}
}
public static final void handlePacket(final RecvPacketOpcode header, final SeekableLittleEndianAccessor slea, final Client c, final ServerType type) {
switch (header) {
...
}
I think that it's important to notice that actually the way most handlers are implemented also violates SRP. This is because most handlers actually have two responsibilities:
- Extract/Parse data from the message
- Decide how to change the world state with that data
The problem with this is that the former has more to do with the app-layer protocol, whereas the latter has something to do with game logic. This is especially true for some of the more complicated handlers, such as the attack handlers. Now of course this still works out alright in practice but if you want to get things 100% right, these two things should be seperated. I'm currently working on this in my own code and I can already see how much cleaner the code becomes after this separation.