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!

[v176] SwordieMS

Newbie Spellweaver
Joined
May 21, 2007
Messages
31
Reaction score
24
SwordieMS is a project of mine that I started around december last year. It's a mostly from-scratch server (I used the networking part of DesuMS), and I'd like to think that it's come pretty far in ~half a year. It will still be updated often, which is why I've decided to post it here.

I won't name all the features, but quite a lot of the base game is working. There are still some basic things (like trading) that haven't been implemented yet, but that will be implemented over time.

The main language is Java, since that is what I'm most comfortable with. Hotswapping + large amount of libraries is also nice. For scripting we use Python (Jython), again because I am comfortable with python. Some other technologies that are use are Hibernate (ORM), Log4j (logging), Netty (networking), and Maven (dependency/build management).

Source itself:


Trello:


For a guide how to set it up, check out our discord (#server-setup-guide):


Also quick shoutout to the maple dev discord:


Note that I won't be helping out with setting up the server. You can always try asking, perhaps you'll get an answer by one of the other contributors.

That said, don't be afraid to fork and contribute. Every little part helps in making a fuller source.

I'm sure that there's some meme design/algo/whatever decisions that are in the source, be sure to point them out so they can be fixed (or even better, make a PR to fix them).

If you have any questions about why some things are as they are, feel free to ask as well!
 
Custom Title Activated
Loyal Member
Joined
Jan 18, 2010
Messages
3,109
Reaction score
1,139
1) Definitely a big +1 for the Nexon-like class names, variables, and enums! (except for Char, m8 what it's User!!!!11111)
2) While you didn't do it to most classes, some of them are C-style named classes like CField and CCashShop, etc. If you're going with a C-style then name the rest like that. My best guess is that you wanted to avoid conflicts with Field (instances) and Field (packets). In that case, I usually have a Field (instances), and a FieldPacket (packets).
3) Another issue with your classes (or source overall) is your packaging; it's a bit confusing. For starters, you split all the field-related classes. You have fields in ms.world.field, but then all of the life objects for the field are sitting in the main package ms.life. You should have life within field, like ms.world.field.life at least. Another example is like the ms.handlers package, why is there EventManagers and packet encoding classes in there when you have the rest of your helper classes in ms.util?
4) I cringed when I saw encodeArr :( y u no encodeBuffer?!!!?!111 Speaking of names, how come some of your packets you follow onPacketName, and some are just packetName? At first I thought maybe when the first word is a verb you ignore it, but if it's a noun you use onPacketName. However, it didn't apply in numerous packets so I'm guessing it's completely random? Personally any packet-related method is onName, but it should at least be either-or..
5) Any reason for using pooled buffers in OutPacket but unpooled buffers in your InPacket?
6) Why we going backwards and using Packet objects again? Your OutPacket class should continue to return getData without having to override the Packet's getData. Then in your encoder you simply get the raw sequence by xoring the initial 2 bytes of the data array (or even do a readShort).
7) Does any public source use IV sequences correctly as integers rather than doing all of these silly array offset checks?
8) Please re-write your movement. I know I see a lot of Odin inspiration, and that's fine and all, but you should seriously handle movement like Nexon does. Plus, in OdinMS they at least had their own made-up names of each movement type rather than Movement1/2/3/etc. Instead, make a single Movement class with a List<Elem> container and a encode/decode function. Decode the packet into new Elems and insert to the list. When you encode the packet, simply loop and re-write all of the elems to your OutPacket. I see you guys have done this with multiple things using your encode() methods, you should update movement to do the same.
9) You should definitely rewrite your CharacterTemporaryStat, I completely forgot people even used long masks still. If you were using Nexon's Flag class, updating buffstats would be a blessing. Rather than having the mask, you just update the indexes like you would an opcode between versions.
10) Glad to see you're using my TwoState's and not padding them with zeroes! EncodeForRemote soonTM, huh? :p

Overall, not bad for your first time writing a source from scratch, especially for that high of a version. At least this is like a 100x cleaner than the Lithium sources around this version. Some things around the source are a bit weird or don't follow the same conventions throughout, but that's always something you can change later on with some simple refactors. I think it's pretty cool that you're going the Python route rather than the same JS engine that every other source here has used since the beginning of time (and using Nexon's script names like you do portals!). You should, however, try looking into a stateless script implementation so you can execute on a global scope and never have to dispose. I understand porting over Odin scripts with their current conventions makes life easier, but just a suggestion if you ever plan on rewriting all of the scripts in a simplified way.

One last thing: I'm not sure if your goal is to be GMS-like or not, but one thing Odin sources never got right was broadcasting field packets. In Odin, they at least realized that only a portion of the map would ever receive packets based on the location of the object who's being broadcasted to the field. I noticed you guys just broadcast any packet in the field to every character in the map, do you plan on keeping it this way? A quick example would be that in your current implementation, if you were in henesys standing to the left, you would see remote characters on the minimap all the way on the other side of the map. However, the GMS-like method is to remove the characters from your field split once they walk off the screen, and any further packets broadcasted by those users will never never be sent to your character (until you move into their view again).
 
Newbie Spellweaver
Joined
Mar 25, 2016
Messages
86
Reaction score
26
PHP:
getLifeSchedules().put(drop, EventManager.addEvent(() -> removeDrop(drop.getObjectId(), 0, true, 0), GameConstants.DROP_REMAIN_ON_GROUND_TIME, TimeUnit.SECONDS));
Pls stop doing this. Create a thread to update all fields.
PHP:
broadcastWithPredicate
You do know how parameters work right?
 
Newbie Spellweaver
Joined
May 21, 2007
Messages
31
Reaction score
24
Welp, apparently RZ auto-refreshes after a while. Was almost done with replying, and now everything's gone :( Time to rewrite
Thanks for the large amount of feedback!
1) Definitely a big +1 for the Nexon-like class names, variables, and enums! (except for Char, m8 what it's User!!!!11111)
Yeah Char is just a relic of when I began, and just hasn't been changed since :p
2) While you didn't do it to most classes, some of them are C-style named classes like CField and CCashShop, etc. If you're going with a C-style then name the rest like that. My best guess is that you wanted to avoid conflicts with Field (instances) and Field (packets). In that case, I usually have a Field (instances), and a FieldPacket (packets).
That's exactly the reason why I used those C prefixes. I agree that using namePacket is probably a bit more clear.
3) Another issue with your classes (or source overall) is your packaging; it's a bit confusing. For starters, you split all the field-related classes. You have fields in ms.world.field, but then all of the life objects for the field are sitting in the main package ms.life. You should have life within field, like ms.world.field.life at least. Another example is like the ms.handlers package, why is there EventManagers and packet encoding classes in there when you have the rest of your helper classes in ms.util?
The packaging kind of exploded over time, since I didn't really think all too much about it. It should definitely be refactored.
4) I cringed when I saw encodeArr :( y u no encodeBuffer?!!!?!111 Speaking of names, how come some of your packets you follow onPacketName, and some are just packetName? At first I thought maybe when the first word is a verb you ignore it, but if it's a noun you use onPacketName. However, it didn't apply in numerous packets so I'm guessing it's completely random? Personally any packet-related method is onName, but it should at least be either-or..
I use encodeArr since Intellij keeps trying to get me to use encodeBuffer when I want to use encodeByte. About the on_ things, I thought I had them all removed. Since as a server, you're not reacting to that packet, but sending them. I'll blame Asura for that one, looks like some of his packets still have it :^)
5) Any reason for using pooled buffers in OutPacket but unpooled buffers in your InPacket?
Not really, but I'll probably switch to Pooled for InPacket, since a netty contributor posted that it should be a bit faster than Unpooled.
6) Why we going backwards and using Packet objects again? Your OutPacket class should continue to return getData without having to override the Packet's getData. Then in your encoder you simply get the raw sequence by xoring the initial 2 bytes of the data array (or even do a readShort).
What do you mean by going backwards by using packets? I want to use my own object as long as possible, to have all functionality available whenever possible. In the encoder I currently just grab the raw array, and that's it.I should indeed change the In/OutPackets to use their super's buffer, though. Atm they have their own buffers for no real reason.
7) Does any public source use IV sequences correctly as integers rather than doing all of these silly array offset checks?
Doubt it, but there is a lot of bitshifting as it is right now. Changing the byte[] into an int will probably make the whole thing even more unreadable.
8) Please re-write your movement. I know I see a lot of Odin inspiration, and that's fine and all, but you should seriously handle movement like Nexon does. Plus, in OdinMS they at least had their own made-up names of each movement type rather than Movement1/2/3/etc. Instead, make a single Movement class with a List<Elem> container and a encode/decode function. Decode the packet into new Elems and insert to the list. When you encode the packet, simply loop and re-write all of the elems to your OutPacket. I see you guys have done this with multiple things using your encode() methods, you should update movement to do the same.
This one is actually taken from mushy, as at the time, my IDA wasn't able to read the movement parsing functions. IDA 7 fixed this, however, so rewriting will be certainly be done.
9) You should definitely rewrite your CharacterTemporaryStat, I completely forgot people even used long masks still. If you were using Nexon's Flag class, updating buffstats would be a blessing. Rather than having the mask, you just update the indexes like you would an opcode between versions.
The Flag class would be an option, but the masks work perfectly atm :p So it'll be low prio. The hex numbers are really ugly, but changing that to take normal numbers instead of hex is fairly simple. Remaking constructors soontm
10) Glad to see you're using my TwoState's and not padding them with zeroes! EncodeForRemote soonTM, huh? :p
Thanks for that release, helped a lot! I'll deliver the homework soon, I promise
Overall, not bad for your first time writing a source from scratch, especially for that high of a version. At least this is like a 100x cleaner than the Lithium sources around this version. Some things around the source are a bit weird or don't follow the same conventions throughout, but that's always something you can change later on with some simple refactors. I think it's pretty cool that you're going the Python route rather than the same JS engine that every other source here has used since the beginning of time (and using Nexon's script names like you do portals!). You should, however, try looking into a stateless script implementation so you can execute on a global scope and never have to dispose. I understand porting over Odin scripts with their current conventions makes life easier, but just a suggestion if you ever plan on rewriting all of the scripts in a simplified way.
Thanks! And yeah, about the conventions, even my own conventions changed over the course of half a year. I think I have like 3 different methods of encoding packets that have many different subtypes, like Effect. Having multiple people doesn't help either.I have looked into stateless scripts, but they seemed more hassle than what they were worth at the time, since you basically have to block a thread until a response from the client arrives. It's still in the backlog, though, as it would make the scripter's lives 100x easier.
One last thing: I'm not sure if your goal is to be GMS-like or not, but one thing Odin sources never got right was broadcasting field packets. In Odin, they at least realized that only a portion of the map would ever receive packets based on the location of the object who's being broadcasted to the field. I noticed you guys just broadcast any packet in the field to every character in the map, do you plan on keeping it this way? A quick example would be that in your current implementation, if you were in henesys standing to the left, you would see remote characters on the minimap all the way on the other side of the map. However, the GMS-like method is to remove the characters from your field split once they walk off the screen, and any further packets broadcasted by those users will never never be sent to your character (until you move into their view again).
The goal is to be mostly GMS-like, but it's not like I would add something that I think is bad, just because nexon also does it (unless there's no other way/I cba lol).For the packet broadcasting, I'm not sure how much it is actually worth doing it. When I was thinking about it, it seems that the amount of client performance that you gain is a lot lower than the server power it requires. I guess it'd have the most effect on large maps with a large amount of people, but those are not common, so I'm not sure if it's worth it to actually implement it.
 
Custom Title Activated
Loyal Member
Joined
Jan 18, 2010
Messages
3,109
Reaction score
1,139
What do you mean by going backwards by using packets? I want to use my own object as long as possible, to have all functionality available whenever possible. In the encoder I currently just grab the raw array, and that's it.I should indeed change the In/OutPackets to use their super's buffer, though. Atm they have their own buffers for no real reason.

I'm not sure I follow.. What exactly do you mean you want to use your own object as long as possible? Your Packet class simply contains getData(), of which is overridden from OutPacket, getLength(), which you can just get from Netty to determine your buffer length (or return length of the getData()), and getHeader(), which just xor's the first two bytes of your array. If you were to remove your Packet class and simply return getData(), getLength(), and even getHeader() directly within your OutPacket class itself, it would be no different, would it? Then, instead of everything returning a Packet object everywhere, it would just return a byte-array reference. Once it reaches the encoder, you just encrypt the getData() returned from the OutPacket instance. By backwards, I mean this was originally implemented in OdinMS - all packets were returning a MaplePacket class which would then return the byte-array. Basically every source after MoopleDEV removed that class and simplified it to just return byte-array packets. I guess I just don't really see any benefit of it is all.

Doubt it, but there is a lot of bitshifting as it is right now. Changing the byte[] into an int will probably make the whole thing even more unreadable.

Well, here's my IV transformation and morphing if you ever wish to switch in the future.

PHP:
/*
 *     This file is part of Development, a MapleStory Emulator Project.
 *     Copyright (C) 2015 Eric Smith <muffinman75013@yahoo.com>
 *
 *     This program is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     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 General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public License
 */

package base.network;

import base.common.CryptoConstants;
import base.util.Pointer;

/**
 * CIGCipher
 * 
 * @author Eric
 */
public class IGCipher {
    
    /**
     * Generates (or, shuffles) a new hash for the ClientSocket's Sequence.
     * 
     * [USER=2000183830]para[/USER]m pSrc The ClientSocket's current sequence
     * [USER=2000183830]para[/USER]m nLen The length in bytes of the sequence (default: 4, integer)
     * [USER=2000183830]para[/USER]m pdwKey If using a special defined Cipher key for hash, specify; otherwise, default: null.
     * [USER=850422]return[/USER] The new sequence for the ClientSocket.
     */
    public static int innoHash(int pSrc, int nLen, Pointer<Integer> pdwKey) {
        int dwKey;
        if (pdwKey == null || pdwKey.Get() == 0)
            dwKey = CryptoConstants.DefaultKey;//0xC65053F2
        else
            dwKey = pdwKey.Get();
        if (nLen <= 0) {
            return dwKey;
        }
        for (int i = 0; i < nLen; i++) {
            dwKey = MorphKey(dwKey, (byte) ((pSrc >> (8 * i)) & 0xFF));
        }
        return dwKey;
    }
    
    /**
     * Transforms and shuffles a 4-byte key source with the provided
     * previous byte of the keys initial sequence. 
     * 
     * [USER=2000183830]para[/USER]m pSrc The input key source to modify
     * [USER=2000183830]para[/USER]m bData The specific byte within the sequence to shuffle
     * 
     * [USER=850422]return[/USER] The result of the morph/shuffled key
     */
    public static int MorphKey(int pSrc, byte bData) {
        // Construct a temporary key array to mimic accessing the offsets of the Src pointer in C.
        byte[] pdwKey = new byte[] { 
            (byte) (pSrc & 0xFF), 
            (byte) ((pSrc >> 8) & 0xFF), 
            (byte) ((pSrc >> 16) & 0xFF), 
            (byte) ((pSrc >> 24) & 0xFF) 
        };
        
        // Perform bitwise operations, morphing our key
        pdwKey[0] += CryptoConstants.Shuffle[(int)pdwKey[1] & 0xFF] - bData;
        pdwKey[1] -= (byte) ((pdwKey[2] ^ CryptoConstants.Shuffle[(int) bData & 0xFF]) & 0xFF);
        pdwKey[2] ^= (byte) (bData + CryptoConstants.Shuffle[(int)pdwKey[3] & 0xFF]);
        pdwKey[3] = (byte) ((CryptoConstants.Shuffle[(int) bData & 0xFF] & 0xFF) + (byte) (pdwKey[3] - (int)pdwKey[0] & 0xFF));
        
        // Reconstruct our key source value from the updated temporary array
        int dwKey = ((int) pdwKey[0]) & 0xFF | (pdwKey[1] << 8) & 0xFF00 | (pdwKey[2] << 16) & 0xFF0000 | (pdwKey[3] << 24) & 0xFF000000;
        dwKey = (dwKey << 3 | dwKey >>> 0x1D);
        return dwKey;
    }
}

The Flag class would be an option, but the masks work perfectly atm :p So it'll be low prio. The hex numbers are really ugly, but changing that to take normal numbers instead of hex is fairly simple. Remaking constructors soontm

http://forum.ragezone.com/f427/implementing-nexons-flag-class-1151997/

Thanks! And yeah, about the conventions, even my own conventions changed over the course of half a year. I think I have like 3 different methods of encoding packets that have many different subtypes, like Effect. Having multiple people doesn't help either.I have looked into stateless scripts, but they seemed more hassle than what they were worth at the time, since you basically have to block a thread until a response from the client arrives. It's still in the backlog, though, as it would make the scripter's lives 100x easier.
Well, I plan to release OrionAlpha soon (my recent KMS Alpha emulator), which will include exactly that: A Python (jython) stateless script engine. Feel free to rip it :)
 
Junior Spellweaver
Joined
Jun 1, 2018
Messages
105
Reaction score
40
Considering making a Bitbucket account just to contribute! From just a glance, I'd say not too shabby for a first shot.
Gonna read into the source a bit more, can't say I know squat about Maplestory's protocol though.
 
Skilled Illusionist
Joined
Apr 26, 2015
Messages
302
Reaction score
77
I like the source, however have you ever consider making your database queries run async like in original maple files?
 
Newbie Spellweaver
Joined
May 21, 2007
Messages
31
Reaction score
24
I like the source, however have you ever consider making your database queries run async like in original maple files?
Not really, but most DB queries are run either at startup, or at the end of a response. So I don't think it'd be much of an advantage.
 
Initiate Mage
Joined
Dec 18, 2013
Messages
3
Reaction score
0
Hi there! When trying to run server.java using Intelliji IDEA, I get this error (In the spoiler box)

Big picture link:


2VfWfj8 - [v176] SwordieMS - RaGEZONE Forums

zNKnPxI - [v176] SwordieMS - RaGEZONE Forums

Help is much much appreciated!



EDIT : Solved. Edited hibernation file -> changed port from and password line
 

Attachments

You must be registered for see attachments list
Last edited:
Initiate Mage
Joined
Aug 31, 2019
Messages
4
Reaction score
0
Anyone able to fix nx weapons not showing up for other players connected to your server?
 
Newbie Spellweaver
Joined
Apr 23, 2018
Messages
66
Reaction score
0
I want test the server with my friend how can i chance the server-ip ?
 
Newbie Spellweaver
Joined
Sep 27, 2018
Messages
91
Reaction score
20
I want test the server with my friend how can i chance the server-ip ?

Send Nexon your IP + your friends IPs so they can black list your IPs from connecting to their servers, it will instead redirect to your own!!!!!! Don't forget to tell them you need them to do this to run a private server.
 
Newbie Spellweaver
Joined
Jun 17, 2020
Messages
6
Reaction score
1
What did you change it too? cause im getting the same error

Hi there! When trying to run server.java using Intelliji IDEA, I get this error (In the spoiler box)

Big picture link:


2VfWfj8 - [v176] SwordieMS - RaGEZONE Forums

zNKnPxI - [v176] SwordieMS - RaGEZONE Forums

Help is much much appreciated!



EDIT : Solved. Edited hibernation file -> changed port from and password line
 

Attachments

You must be registered for see attachments list
Back
Top