[v176] SwordieMS

Results 1 to 14 of 14
  1. #1
    Registered swordiemen is offline
    MemberRank
    May 2007 Join Date
    11Posts

    [v176] SwordieMS

    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:
    https://bitbucket.org/swordiemen/swordie/src/master/

    Trello:
    https://trello.com/b/oBugzdjc/swordiems

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

    Also quick shoutout to the maple dev discord:
    https://discordapp.com/invite/7wTsdDx

    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!
    Some know me as Sjonnie.


  2. #2

  3. #3
    Ultimate Member MatanStyle is offline
    MemberRank
    Dec 2008 Join Date
    153Posts

    Re: [v176] SwordieMS

    is alliance and hired merchant working?

  4. #4
    Account Upgraded | Title Enabled! Lowsir is offline
    True MemberRank
    Mar 2011 Join Date
    1,052Posts

    Re: [v176] SwordieMS

    I'll be looking at this everyday, like I do with HeavenMS. Good stuff though.
    Last edited by Lowsir; 07-08-18 at 11:11 PM. Reason: my english is degrading like my coding skills

  5. #5
    I am Groot Baha is offline
    True MemberRank
    Apr 2009 Join Date
    The VoidLocation
    725Posts

    Re: [v176] SwordieMS

    ouuu open source development in 2018. Is this the /f425/ comeback?

  6. #6
    Registered swordiemen is offline
    MemberRank
    May 2007 Join Date
    11Posts

    Re: [v176] SwordieMS

    Quote Originally Posted by MatanStyle View Post
    is alliance and hired merchant working?
    Alliance nope, same for merchants. Wasn't sure about implementing merchants, since the fm is removed in later GMS versions. But all the fields should still be there, so it'll probably be implemented later on.
    Some know me as Sjonnie.

  7. #7
    Moderator Eric is online now
    ModeratorRank
    Jan 2010 Join Date
    DEV CityLocation
    3,042Posts

    Re: [v176] SwordieMS

    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).

  8. #8
    Member Arnah is offline
    MemberRank
    Mar 2016 Join Date
    57Posts

    Re: [v176] SwordieMS

    PHP Code:
    getLifeSchedules().put(dropEventManager.addEvent(() -> removeDrop(drop.getObjectId(), 0true0), GameConstants.DROP_REMAIN_ON_GROUND_TIMETimeUnit.SECONDS)); 
    Pls stop doing this. Create a thread to update all fields.
    PHP Code:
    broadcastWithPredicate 
    You do know how parameters work right?
    Owner of Vertisy source which none should use.

  9. #9
    Registered swordiemen is offline
    MemberRank
    May 2007 Join Date
    11Posts

    Re: [v176] SwordieMS

    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!
    Quote Originally Posted by Eric View Post
    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
    Quote Originally Posted by Eric View Post
    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.
    Quote Originally Posted by Eric View Post
    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.
    Quote Originally Posted by Eric View Post
    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 :^)
    Quote Originally Posted by Eric View Post
    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.
    Quote Originally Posted by Eric View Post
    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.
    Quote Originally Posted by Eric View Post
    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.
    Quote Originally Posted by Eric View Post
    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.
    Quote Originally Posted by Eric View Post
    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
    Quote Originally Posted by Eric View Post
    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
    Quote Originally Posted by Eric View Post
    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.
    Quote Originally Posted by Eric View Post
    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.
    Some know me as Sjonnie.

  10. #10
    Moderator Eric is online now
    ModeratorRank
    Jan 2010 Join Date
    DEV CityLocation
    3,042Posts

    Re: [v176] SwordieMS

    Quote Originally Posted by swordiemen View Post
    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.

    Quote Originally Posted by swordiemen View Post
    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 Code:
    /*
     *     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.
         * 
         * [MENTION=2000183830]para[/MENTION]m pSrc The ClientSocket's current sequence
         * [MENTION=2000183830]para[/MENTION]m nLen The length in bytes of the sequence (default: 4, integer)
         * [MENTION=2000183830]para[/MENTION]m pdwKey If using a special defined Cipher key for hash, specify; otherwise, default: null.
         * [MENTION=850422]return[/MENTION] The new sequence for the ClientSocket.
         */
        
    public static int innoHash(int pSrcint nLenPointer<IntegerpdwKey) {
            
    int dwKey;
            if (
    pdwKey == null || pdwKey.Get() == 0)
                
    dwKey CryptoConstants.DefaultKey;//0xC65053F2
            
    else
                
    dwKey pdwKey.Get();
            if (
    nLen <= 0) {
                return 
    dwKey;
            }
            for (
    int i 0nLeni++) {
                
    dwKey MorphKey(dwKey, (byte) ((pSrc >> (i)) & 0xFF));
            }
            return 
    dwKey;
        }
        
        
    /**
         * Transforms and shuffles a 4-byte key source with the provided
         * previous byte of the keys initial sequence. 
         * 
         * [MENTION=2000183830]para[/MENTION]m pSrc The input key source to modify
         * [MENTION=2000183830]para[/MENTION]m bData The specific byte within the sequence to shuffle
         * 
         * [MENTION=850422]return[/MENTION] The result of the morph/shuffled key
         */
        
    public static int MorphKey(int pSrcbyte 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 << dwKey >>> 0x1D);
            return 
    dwKey;
        }

    Quote Originally Posted by swordiemen View Post
    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
    Implementing Nexon's Flag Class

    Quote Originally Posted by swordiemen View Post
    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 :)

  11. #11
    Member treebeard is offline
    MemberRank
    Jun 2018 Join Date
    North AmericaLocation
    32Posts

    Re: [v176] SwordieMS

    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.

  12. #12
    Newbie shavit is offline
    MemberRank
    Aug 2018 Join Date
    1Posts

    Re: [v176] SwordieMS

    Quote Originally Posted by treebeard View Post
    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.
    I personally had no clue about Maple dev or Java at all but I contributed to Swordie (check commits under my name), it's not that hard :p

  13. #13
    Account Upgraded | Title Enabled! br1337 is offline
    True MemberRank
    Apr 2015 Join Date
    237Posts

    Re: [v176] SwordieMS

    I like the source, however have you ever consider making your database queries run async like in original maple files?

  14. #14
    Registered swordiemen is offline
    MemberRank
    May 2007 Join Date
    11Posts

    Re: [v176] SwordieMS

    Quote Originally Posted by br1337 View Post
    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.
    Some know me as Sjonnie.



Advertisement