Welcome!

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!

Understanding the Source of MoopleDev v83

Initiate Mage
Joined
Oct 17, 2008
Messages
16
Reaction score
0
I have always wanted to understand the source files for a MS private server purely for educational purposes. I have been programming in Java in school for a few years so this source is a great one to understand. These questions are meant for kevintjuh93 about MoopleDev v83 Rev 119 (I think it’s Rev 119) but if anyone wants to help, feel free.

Firstly, some notes about this thread. When I ask to explain a class or method, I really want someone to fully explain every line of code within that class/method. The easiest way for this to be done is probably to use the spoiler message when posting in this thread. You can use spoiler to show the code and your comments. I will edit this first post marking off things I understand and things I don’t.

I won't ask any SkillFactory/MapleItemInformationProvider/CashItemFactory questions for now as I want to see how this goes :p

Legend: Green means I don't need help anymore, anything else means I still need help.

Here are the questions:

1.
In net.server.Server, for acceptor.getFilterChain().addLast("codec", (IoFilter) new ProtocolCodecFilter(new MapleCodecFactory())); Why are you calling specifically addLast to the filter chain? Why aren’t you saying addBefore/addAfter/addFirst?



2.
For MaplePacketEncoder and MaplePacketDecoder, when is the encode method called and when is the doDecode method called?


3.
a) In the creation of MapleClient.send and MapleClient.receive which occurs in MapleServerHandler.sessionOpened(), Is there any reason for the numbers specified in the creation for ivRecv and ivSend? I think they’re supposed to be completely random because that’s what it says in the definition of Initialization Vector.
b) When making use of ivSend and ivRecv to create sendCypher and recvCypher which are MapleClient.send and MapleClient.recv, why is the third parameter of the constructor for MapleAESOFB equal to 0xFFFF - ServerConstants.Version in sendCypher and in recvCypher it’s just ServerConstants.Version? I know from that Packet headers are 4 bytes long and are generated by the send IV, packet length without the header, and the version of MapleStory. The version is negativeOf(version + 1) when sending a packet to the client, otherwise it is just the version. However, 0xFFFF - ServerConstants.Version doesn't look like negativeOf(version + 1).

4.
I have a few questions about the MapleAESOFB class:
a) In the constructor of MapleAESOFB, when setting mapleVersion, this is called: this.mapleVersion = (short) (((mapleVersion >> 8) & 0xFF) | ((mapleVersion << 8) & 0xFF00));. Does this switch the endianness of mapleVersion to Little Endian?
b) are the values in the byte array funnyBytes significant? Can they be any random value?
c) Can someone explain the code in crypt(byte[] data) line by line?

d) Is there any significance to the bytes in byte[] in = {(byte) 0xf2, 0x53, (byte) 0x50, (byte) 0xc6};? Can byte[] in be any set of values?
e) Is there any significance to funnyShit(byte inputByte, byte[] in)? Or is that just creating a new very random IV?
f) I don’t understand the getPacketHeader method. Can someone explain the code line by line?
g) I don’t understand the getPacketLength method. Can someone explain the code line by line?
h) I don’t understand the checkPacket methods. Can someone explain the code line by line in each of those methods?
i) Does MapleAESOFB only account for AES and OFB? Moreover, does MapleAESOFB have anything to do with the custom MapleStory encryption?
j) How can I find out through a packet sniffer that MapleStory uses AES?



5.
In MaplePacketEncoder, why are you adding 4 bytes to the array in final byte[] ret = new byte[unencrypted.length + 4];?


6.
Could you explain the entirety of the MapleCustomEncryption class in a thorough way using simple terminology?


7.
In MaplePacketEncoder, why do you need out.write(IoBuffer.wrap(ret)) as opposed to out.write(ret)? Moreover, how come the IoBuffer.wrap is used here but not in other classes that have out.write(ret)?

8.
In the MaplePacketDecoder class, What does out.write(decryptedPacket) do? If it sends those packets back to the client, why would you send the packet back to the client? Isn’t the point of MaplePacketDecoder to decode incoming packets from the CLIENT into something the SERVER can read?



9.
When you create a new Channel, you call ((SocketSessionConfig) acceptor.getSessionConfig()).setTcpNoDelay(true); which disables Nagle’s algorithm. However, in the net.server.Server class, you don’t call this. Why?

10.
In Channel and in Server, you bind the acceptor to a MapleServerHandler. I am wondering why when a session is opened, sessionOpened is only called once and not called (numberOfChannels + oneForTheServer) times. Example: say I have 3 channels and I have obviously one server in total, when I open one new session, shouldn’t sessionOpened be called 4 times? (numberOfChannels = 3, oneForTheServer=1)



11.
In MapleServerHandler,
a) can you explain the sessionIdle, messageReceived, and sessionOpened classes in their entirety? This includes explaining when each method is called.
b) Can someone explain what is going on in the following methods inside GenericLittleEndianAccessor? In readShort() and similarly those other ones such as readInt() etc, some of the bytes are being rolled to the left and then they are all added together. Why is this happening?


12.
Can you explain how you got the byte values for the enum RecvOpcode and SendOpcode?


13.
Can you explain the following handlers in their entirety and why we need them: LoginRequiringNoOpHandler, KeepAliveHandler, CustomPacketHandler, AcceptToSHandler,AfterLoginHandler, ViewAllCharSelectedWithPicHandler, ViewAllPicRegisterHandler, NPCMoreTalkHandler, ItemSortHandler, and MoveLifeHandler.


14.
I am wondering for each handler, when is handlePacket called?


15.
In some of those handlers, when you’re using EndianAccessors to read bytes from the client it seems like magic the way you know exactly what values are needed for certain events to happen. A great example of this is in AfterLoginHandler when you set c3 to 5 and then go on to say if(c2 ==1 && c3 ==1). Can you explain how you know what those bytes should be and how you know when to read them from the endian accessor to load those values?


16.
In the MaplePacketCreator class, why do some have a SendOpcode written to the MPLEW and others don’t? An example of this is MaplePacketCreator.addCharStats doesn’t have a SendOpcode but sendRecommend does.

17.
What’s the difference between the opcode SERVERLIST_REREQUEST and SERVERLIST_REQUEST? You register the same handler for both of them...


18.
As in question 17, why do you have the same handler for USE_ITEM and USE_RETURN_SCROLL?


19.
In the Server class, is there any specific reason why you bind the IoAcceptor to port 8484?


20.
Where is the use of parallel computing in Moopledev? I think net.server.Server should make use of this but I’m not sure how. I do see some methods are synchronized.


After all questions are answered, I'll post the full answer for each question :)

Thanks so much!
 
Last edited:
Initiate Mage
Joined
Sep 17, 2013
Messages
82
Reaction score
137
1. Basically tells the packets to go through encoder/decoder
2. The methods are called when a packet is received/sent
3. I am guessing you mean the send/receive crypto, which encrypts/decrypts the packets.
4. Transforms the bytes into something the client can read
5. The packet header is 4 bytes
6. Another method to transform the bytes into something the client can read
7. The one with client == null is an unencrypted packet, while the other one is encrypted
8. Decodes incoming packets from the client into something the server can read
9. There's probably no difference, whatever meaning it had originally has been lost
10. The client only connects to 1 port, which is why there is only 1 sessionOpened
11. sessionIdle: when the client hasn't sent a packet for a while
messageReceived: when the server receives a packet
sessionOpened: when a connection is opened
12. either sniffing them from GMS or getting them from the client by reverse engineering
13. LoginRequiringNoOp: no point, just a blank handler
AcceptToS: handles accepting the tos?
afterLogin: not used if pin disabled
viewallcharselectedwithpic: handles select character via view all characters if you have a pic set
viewallpicregister: handles select character via view all characters if you don't have a pic set
npcmoretalk: handles 2nd, 3rd, etc. npc dialogues
itemsort: sorts your inventory
movelife: handles mob movement
14. it's called when you receive the packet with the corresponding header
15. someone probably sniffed them from gms and was too lazy to find out what they actually meant
16. class that gives you packets
17. they're handled the same way, the client just sends different headers at different times
18. kevin or somebody probably bundled the handlers together since they're handled similarly
19. client uses port 8484, thank nexon for that
 
Upvote 0
Joined
Apr 10, 2008
Messages
4,087
Reaction score
1,263
I felt like s4nta's pos was a bit unorganized and missing information, so here's another hand. Oh and, welcome to RaGEZONE. It's really nice to see people who actually want to learn how things work "behind-the-scenes" instead of asking for plain spoonfeeding.

1. Read above.

2. This has to do with the encryption of the packets. The encode method is called when data is sent (to the client), and the decode method is called when data is received (to the server).

3. send and recv are variables of type MapleAESOFB, which takes care of the received data (renewing the IV and decrypting/encrypting data).

4. MapleAESOFB is a short for Maple AES and output feedback. AES is a type of encryption, a short for Advanced Encryption Standard, based on Rijndael cipher.

5. Each packet has a header. The header contains data about the length of the packet, the game's version and some IV data.

6. MapleCustomEncryption is a class that has various tools for encrypting/decrypting sent/received data. MapleStory uses a combination of this type of encryption + AES (the custom encryption is also known as Shanda, originally taken from China MapleStory).

7. Read above.

8. MaplePacketDecoder/Encoder - both are classes that handle sent/received packets so they can be manipulating by decrypting/encrypting.

9. net.server.Server class is the main entry point of the server, which loads up everything. The channel server is a server for itself. Just because the class server is called like that doesn't mean it's an actual server. NoTcpDelay set to true disables that algorithm, you are correct, which fastens up the data speed.

10. MapleServerHandler is a class that handles messages (data) that are received from a client. A client can obviously be bound to one server at one time, so only one session of MapleServerHandler is created for it. Once it changes servers, a new one is created. There's no need to create more than one.

11. Here's an explanation about each method:

sessionIdle: Like s4nta explained above, is called when the session has been idle for too long so a disconnection is expected.
messageReceived: This method is called when the server receives data from the client that needs to be handled. Note that the data is read and decrypted using the length from the header, and then assigned to a binary reader so you can read the data from it.
sessionOpened: This method is called when the acceptor received a new connection.

12. Read above.

13. Here's an explanation about each one of them:

LoginRequiringNoOpHandler: Unsure. Seems useless.
KeepAliveHandler: The client sends a packet when a button is clicked or an operation is done, I'm not sure why. OdinMS named it KeepAlive.
CustomPacketHandler: Not sure.
AcceptToSHandler: Sent when the user has accepted the Terms of Agreement (the notice is popped up when you create a new account).
AfterLoginHandler: Sent after a successful authentication (only in earlier versions, when the PIN-Code was enabled). When the server receives this, it can either pop up the PIN-request window, or login immediately (if the PIN-Code is disabled for any reason).
ViewAllCharSelectedWithPicHandler: Sent when the user has selected a character inside the View All Characters area and has already assigned a PIC-Code.
ViewAllPicRegisterHandler: Sent when the user selected a character inside the View All Characters area and has to register a PIC-Code.
NPCMoreTalkHandler: Sent when the user made an action in a Npc window (exiting, clicking buttons, selecting, etcetera).
ItemSortHandler: Sent when the user pressed the sort button in the inventory.
MoveLifeHandler: Life in MapleStory refers to movable creatures (Characters, Mobs, Npcs, etcetera). In OdinMS, this handler refers to the packet that is sent when a mob requests movement.

14. Read above.

15. It's true that some values of certain packets are unknown to us so we can't name them. The PIN-Code algorithm is one of them. It's unknown, so the values have been given random names. It may seem like magic to you, but it's a pre-defined way that Neckson made, we're just unaware of those. Sometimes guessing is required!

16. MaplePacketCreator is a collection of static methods that build packets (returning an array of bytes) for the requested operation.

17. ServerListRequest is called after a successful authentication, when the client requests the list of the worlds so it can show them for the user. ServerListRerequest is sent when the user returned to the world selection, so the client can renew the worlds (in case there was any change).

18. UseItem is called when the user consumes an usable item (inside the USE tab in the inventory). UseReturnScroll is called when the user used a return scroll. I'm not sure why Neckson made a different packet for those, because the server loads the data and can easily check if the consumable item is a return scroll or not. Oh well!

19. When the MapleStory client is initiated, it will attempt to connect on the port 8484, so it must be 8484, unless edited differently. Note that the clients requires a certain packet to connect to a channel, so channels can be in any port you want. The login, though, must be 8484.
 
Upvote 0
Initiate Mage
Joined
Oct 17, 2008
Messages
16
Reaction score
0
@Fraysa for your response to question 9, wouldn't you want to fasten up the data speed on the channels as well?
I actually joined in 2008 as well :) I have just been very inactive. Thank you guys so much for your help so far. I have updated the first post of this thread indicating what I understand and specifically what I do not understand.
 
Last edited:
Upvote 0
Joined
Apr 10, 2008
Messages
4,087
Reaction score
1,263
@Fraysa for your response to question 9, wouldn't you want to fasten up the data speed on the channels as well?
I actually joined in 2008 as well :) I have just been very inactive. Thank you guys so much for your help so far. I have updated the first post of this thread indicating what I understand and specifically what I do not understand.

It has nothing to do with channels. This is a property of the socket.
 
Upvote 0
Initiate Mage
Joined
Oct 17, 2008
Messages
16
Reaction score
0
@Fraysa my mistake. What I meant to say was in net.server.Server, wouldn't it be beneficial to add ((SocketSessionConfig) acceptor.getSessionConfig()).setTcpNoDelay(true);?
 
Last edited:
Upvote 0
Initiate Mage
Joined
Oct 17, 2008
Messages
16
Reaction score
0
I don't know how Java/Mina's sockets work, so I can't answer that question. You should consult a Java expert like rice or retep998.
Okay I'll do that. I updated my questions above, could you take another look at them? Question 9 still remains the same so ignore that one.
 
Upvote 0
Initiate Mage
Joined
Oct 17, 2008
Messages
16
Reaction score
0
rice is probably one of the most knowledgeable people when it comes to java.
+1 to rice, he's helped me many a time when i was trying to learn

Any help would be much appreciated guys. We've come a long way with this thread in only a few days.
 
Upvote 0
Joined
Mar 14, 2010
Messages
5,363
Reaction score
1,343
@Smexella

16.
In the MaplePacketCreator class, why do some have a SendOpcode written to the MPLEW and others don’t? An example of this is MaplePacketCreator.addCharStats doesn’t have a SendOpcode but sendRecommend does.

What you see such as "addcharstats" is not a packet itself. It's a part of a packet that is used in others. It doesn't have it's own opcode because it's basically a part of different packets. As you can see from what's calling it, it's self explanatory. addCharLook isn't a packet, but it's code is used in more than one packet so it's made into it's own little "addCharLook". So h4rd to explain In depth it's common sense
 
Upvote 0
Junior Spellweaver
Joined
Apr 20, 2013
Messages
103
Reaction score
24
I try to explain the things in your point 4, as I doubt many people are even capable of explain those.

4a) This is the most useless piece of code I have ever seen, it does indeed switch around the hibyte with the lobyte
4b) This naming sense shows the people of odinms barely knew what they were doing, this is the shuffle table for calculating the new IV, changing the value of these bytes will ruin the crypto, not directly, but after a certain amount of packets, if you want you could change them in the client as well as in the server and it should work
4c) I wont explain it per line, its just an AES equivalent with a different block size.
4d) This is the dwDefaultKey, same story as the shuffle table, dont touch it.
4e) This is the shuffle function for calculating the next IV, its actually quite the important function, as this makes the crypto appear almost random, while the client and the server both keep the same IV in sync.
4f+g) I will explain these together, as you can see "getPacketHeader" creates a packet header containing the length hidden by a xor, the only thing "getPacketLength" does is retrieving this length, this is needed because some of the maplestory packets are not send in a single packet.
4h) Just checks if the packet header is valid and created with the same method as "
getPacketHeader", quite useless.
4i) Just bad naming sense, it contains a few crypto functions in a clusterfuck together with MapleStory-only values.
4j) You cant, you can figure that out by disassembling the executable. (as there are other crypto's similar to AES)

I honestly do not feel like explaining the code line by line, as the code is quite ugly and needs improvement.
 
Upvote 0
Initiate Mage
Joined
Oct 12, 2010
Messages
30
Reaction score
18
I try to explain the things in your point 4, as I doubt many people are even capable of explain those.
And apparently neither are you.

4a) Yes that's correct, why would that be useless?
4b) OdinMS was made without using illegal reverse engineering techniques, this code emulates maple story encryption, of course they didn't know why things are done this way. Yes, the crypto is bad, but OdinMS attempts to be a MapleStory compatible server.
4c) I won't explain it by line either but it's the custom maplestory encryption, it has nothing to do with AES
4d) that's correct :D
4e) Shuffling IVs is stupid and bad practice, but yes that's whats going on
4f) Packet headers are handeled differently to packet bodies, that's how it is done.
4g) When using stream based protocols like TCP you need a Packet Length, it's encoded in the packet in the way described in getPacketLength
4h) It checks whether the packet is valid, it's not required
4i) It's mainly the custom Maplestory encryption. Only the calls to cipher.WHATEVER are AES
4j) You can't. AES like any other good crypto makes data look random. You can guess that it's encrypted because it's quite random and you can find aes constant tables in an unencrypted maple story executable. (Standard practice to detect which encryption is being used)



Onwards to the rest of your (unaswered) questions! :D

1+7+8) That's how MINA does things. Icoming packets are sent through a filterchain and each filter writes output for the next filter to process. The OdinMS filterchain is quite shallow though (I believe it only contains a single filter, maybe 2, oh well)
3) Yes IVs are supposed to be random. Pre-choosing the IV like this weakens the encryption significantly. This is not a Maple Story thing but a OdinMS "design decision" - who really needs the packets to be encrypted, we can decrypt them anyway - the magic numbers here are ascii characters ;) (they can also be used to detect OdinMS based servers, go figure)

11b) Endianess conversion. What else would they do? Can you rephrase the question?

16) I believe others have explained this. The add* methods are usually not public and don't create packets themselfes, the send* methods create packets, write headers, then maybe call add* methods to add stuff to the Packet (for recuring things)

20) There is little need for parallel computing. In generel OdinMS handels each incomign request (each Packet) using the Mina N-M thread pools. Therefore a single request ist single threaded and multiple requests are handeled in parallel. This is feasible as a request can usually be processed in a very short amount of time - very little computation intensive stuff is going on - and does not require further parallelisation. MapleMap should have synchronisation constructs (synchronized, AtomicInteger, maybe ReentrantLock if MoopleDev chose to degrade the OdinMS performance by switching to the explicit lock classes instead of using the built in Java Monitors (synchronized))
edit for 20: It appears Moopledev uses ReadWriteLocks here. This ia a good idea (probably)
 
Last edited:
Upvote 0
Initiate Mage
Joined
Oct 17, 2008
Messages
16
Reaction score
0
Aah I see. I had assumed every seperate method in that MaplePacketCreator class was for a seperate packet. Thanks!

Smexella



What you see such as "addcharstats" is not a packet itself. It's a part of a packet that is used in others. It doesn't have it's own opcode because it's basically a part of different packets. As you can see from what's calling it, it's self explanatory. addCharLook isn't a packet, but it's code is used in more than one packet so it's made into it's own little "addCharLook". So h4rd to explain In depth it's common sense


Minike and Snakeday, I have no experience with binary operations at this level of complexity so specific line-by-line explanations would be very helpful: for Question 4 can you explain exactly what the code is doing. Like why are you shifting the bits, why are you ANDing stuff, why are you ORing stuff etc.
4b) Minike can you explain the shuffle table some more. Also, where did you get this shuffle table from?
4c) Minike said AES encryption with a “different block size” but Snakeday said it’s the custom MapleStory encryption which has nothing to do with AES. Who’s correct? Also, an explanation of this method (line-by-line) would be very helpful.
4d) What is a dwDefaultKey? where did this key come from? Why can’t I change it?
4e) How does this specific shuffle IV function keep the client and server’s IV in sync? Can I change this shuffle IV function to anything I want and still keep the client and server’s IV in sync?

I would also like the methods described in 4f), 4g), 4h) to be explained line-by-line. They simply look like gibberish to me.

Snakeday, could you take another look at questions 7 and 8? I'm still having trouble.

11b) Snakeday, you explained it to my liking, thanks!
 
Upvote 0
Initiate Mage
Joined
Sep 17, 2013
Messages
82
Reaction score
137
Honestly, a lot of this stuff is trivial and does not need to be understood if you're just looking to build a server.
If your aim is to make your own server, the extent of what you need to know is:
- Recv: bytes come in, they get processed, and you can read them.
- Send: server writes bytes, they get processed, and the client can read them.

Explaining the code line-by-line is something that nobody wants to do. It's tedious, and a lot of the stuff is again, trivial.
The encryption is something that Wizet came up with and some very talented programmers extracted. There's no real "reason" behind what each bitwise operation does.
 
Upvote 0
Junior Spellweaver
Joined
Apr 20, 2013
Messages
103
Reaction score
24
Like why are you shifting the bits, why are you ANDing stuff, why are you ORing stuff etc.
4b) Minike can you explain the shuffle table some more. Also, where did you get this shuffle table from?
4c) Minike said AES encryption with a “different block size” but Snakeday said it’s the custom MapleStory encryption which has nothing to do with AES. Who’s correct? Also, an explanation of this method (line-by-line) would be very helpful.
4d) What is a dwDefaultKey? where did this key come from? Why can’t I change it?
4e) How does this specific shuffle IV function keep the client and server’s IV in sync? Can I change this shuffle IV function to anything I want and still keep the client and server’s IV in sync?

I would also like the methods described in 4f), 4g), 4h) to be explained line-by-line. They simply look like gibberish to me.

They main reason for those AND or OR is because apparently Java is not able to directly cast part of an byte array to a diff type, this is actually possible in C++ and C#. Thus the calculation is per byte.
The thing they do most of the time in this crypto, is grabbing the the bytes out of a short or an int (as a short is 2 bytes and an int is 4 bytes)
4b) This table is a collection of 16*16 bytes thus covering the max amount you could get with an index as a byte, as this table is used for calculating the value of the next IV, changing it would only work if you also change it inside the client. And as for from where I got this from, I used IDA to figure out how the crypto works and found this table inside the memory of maplestory.
4c) Actually I would say we are both correct, maplestory handles the data as blocks of 0x5B0 and 0x5B4 and uses AES to transform IV after which it will use that IV to XOR (hide) the data in the buffer.
4e) As the server and client both use the same function for the shuffle, the IV on the client and server should stay the same, thus allowing the server and client to be able to decrypt the data (obviously this will mess up horribly if a certain packet does not reach the server). And yes, you can change it, but you will also need to edit the client for this. (this is quite interesting as it will also prevent things like MapleShark from decrypting the packets)

4f) Maybe this would be easier to explain based on some C# code
Code:
*(ushort*)pData = (ushort)(Settings.GameVersion ^ MapleIV.HIWORD);
*((ushort*)pData + 1) = (ushort)(*(ushort*)pData ^ (data.Length - 4));
This code does exactly the same as the "getPacketHeader" from Odin, the only difference is the fact that it handles the data as short instead of bytes (causing it to be basically 2 lines instead of 11).
MapleIV.HIWORD is the same as IV[3] and IV[2]
data.Length - 4 is the same as "int length"
So the thing it does is, the first 2 bytes in the packet header contains the GameVersion XORed with the upper 2 bytes (HIWORD) of the IV
And the last 2 bytes in the header contains the first 2 bytes XORed with the packet length (minus the header we are creating)
The Odin code does the same, but requires some shifting to cast pieces of the data to bytes
4g) As explained above, you can see that inside the packet header the place the length of the packet, thus the only thing we need to do to retrieve the length is to XOR it back with the original value it was XORed with in the first place, which happen to be the first 2 bytes in the packet header.
4h) Like you can see in 4f, the packet also contains the gameversion and the HIWORD of the IV, the "checkPacket" function just checks if the gameversion and IV are what they are supposed to be and thus also if the IV is still in "sync" (see 4e)

Incase you are wondering what a "HIWORD" is, its basically the upper 2 bytes of an int, thus a LOWORD is the lower 2 bytes, for example;
We have the int "305419896" which in hexadecimal would be 0x12345678
Thus if we want the HIWORD, we could shift it 16 times to the right, why 16? because a WORD (short) is 16 bits, and a DWORD (int) is 32bit
Thus 0x12345678 >>> 16 == 0x1234
Getting the LOWORD is quite simple, as you can use the & (AND) to trim off anything above 0xFFFF using & 0xFFFF
Thus 0x12345678 & 0xFFFF == 0x5678
And if you want the original int back, you just use the | (OR)
Thus (0x1234 * 0x10000) | 0x5678 == 0x12345678 (or in this example + would also work)
Of course there are many other methods for achieving the same
 
Last edited:
Upvote 0
Initiate Mage
Joined
Oct 12, 2010
Messages
30
Reaction score
18
4c) Yes, saying that it has nothing to do with AES was probably an overstatement. The call to cipher.doFinal is AES, everything else is custom OFB style encryption of the input data.
In general everything about the encryption is this way just because it is this way, there is little sense to explain any part of the bit shuffling (except for stuff like getting the packet length or the header). This happens exactly this way because it happens this way in the original Maple Story client (and thus also in the original Server), changing anything here would make the server incompatible to the standard client. As Minike has said it would be possible to also change the encryption in a custom client (by patching it in some way (loading a dll or patching the exe)) but I see little point in this exercise (remember, OdinMS is AGPL, you have to distribute the sourcecode of your server to anyone connecting to it)

Generally speaking, code dealing with encryption of data almost always looks like giberish (try to make sense of an AES implementation in a language of your choice)

7) IOBuffer.wrap turns a byte[] into an IoBuffer. The MINA API requires an IOBuffer here. It's not used in other places because it's probably not needed.
The internal OdinMS packet handling (and encryption) is done on byte[], Mina requires IOBuffers. It may be a good idea to transform everything to use IoBuffers (they are a clone of Java ByteBuffers and ByteBuffers have some cool performance characteristics) it might also be possible to remove much of the Endianness swapping by using IOBuffers, I believe they already implement this functionality. (A quick google showed )
Thus we could shift writeMapleAsciiString and the other number encoding stuff to a helper class and not worry about Endianness in the Server code.

8) See for an explanation about protocol codecs.
In general the Mina pipleline works like this: Data arrives (as a IoBuffer), ProtocolDecoder transforms data to a domain object (we use byte[] here, that's a hack), then messageReceived is called on the IoHandler with the decoded object.
For OdinMS it would be cool if the ProtocolDecoders could produce objects like "Movement" or "Attack", or "UserItem". Instead this functionality is completely implemented in the (custom) PacketHandlers. It was done this way so it would not be nescessarry to write tons of classes (because that's pretty exhausting and not fun)
 
Upvote 0
Initiate Mage
Joined
Jul 11, 2013
Messages
80
Reaction score
14
Wow. I found this extremely informative. Thank you to everyone who posted, and to the OP for creating the topic!
 
Upvote 0
Back
Top