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!

ColexDev (re-coding Lithium, cuz it sucks)

Joined
Aug 10, 2008
Messages
858
Reaction score
516
Scheduling can be made nicer, but at the heart of it, it's just a scheduled threadpool executor that runs Runnables wrapped in a try/catch. It depends on what kind of problems you are talking about too; monsters stopping spawning/map-related problems are usually because code is not threadsafe, and that code should be fixed instead of the scheduler. Apart from some utility code for checking scheduler health and some fallback logic (but this is already implicitly possible with the wrapped Runnable), nothing done to the executor will address the root problems.
People should be more meticulous about what scheduled tasks to keep in queue. As an example, when a poisoned monster dies, you want to cancel its poison schedule or it could lead to undesired consequences. This can be applied to everything; if a task is going to modify state, then it needs to be monitored and canceled at the right times. It's a bit more complex than that too, because even certain packets shouldn't be sent with old data or they could cause a d/c.

I don't remember original Odin exp tracking, but I thought it was mostly fine. The external synchronization is weird and error-prone, though.

I wasn't talking directly about the backing executor, but more-so the way it is used. The main issue that arises is that the executor threads themselves will block for some reason and nothing scheduled will happen after that. I haven't personally debugged the direct cause, but I know for certain that it does happen in the long run since I have seen that the executor threads are blocked as tasks get added on endlessly without being executed. Also, even in base Odin there wasn't any wrapping with try/catch unless it was within the task to be scheduled itself. In my opinion, there are even points where it is reasonable to say that you could eliminate scheduling for certain tasks because you could just do it through one of the running session threads after handling for something was done. I pretty much did that with mobs a few times back in the day and made them independent of the scheduler.

The reason I mentioned EXP tracking was because it used 5 inner classes and a LinkedList to manage something that could be done with a single HashMap containing the damage and a few helper methods. I ended up changing it when I had to implement 80:20 in Solaxia since it was a nightmare to work with in its previous state.
 
Newbie Spellweaver
Joined
Aug 6, 2014
Messages
56
Reaction score
42
I wasn't talking directly about the backing executor, but more-so the way it is used. The main issue that arises is that the executor threads themselves will block for some reason and nothing scheduled will happen after that. I haven't personally debugged the direct cause, but I know for certain that it does happen in the long run since I have seen that the executor threads are blocked as tasks get added on endlessly without being executed. Also, even in base Odin there wasn't any wrapping with try/catch unless it was within the task to be scheduled itself. In my opinion, there are even points where it is reasonable to say that you could eliminate scheduling for certain tasks because you could just do it through one of the running session threads after handling for something was done. I pretty much did that with mobs a few times back in the day and made them independent of the scheduler.

I'm talking about this thing:
PHP:
private static class LoggingSaveRunnable
This wrapper class wraps every runnable passed to the executor in an empty try/catch by default. You can add metadata to the runnables to find out where and when a problem happens, which could help you fix some things. Ultimately the problem is the code being scheduled, not the TimerManager or the underlying executor. I remember running into these scheduler-related problems like mobs stop spawning in v88 using a v75 shootsource base, but when I rewrote a lot of things, it disappeared and I never saw it again, even while having a couple hundred players on for a few days at a time.
The default scheduled threadpool executor uses an unbounded queue which could be problematic when trying to triage problems as they happen, so maybe wrapping it with some metrics and using a health-checker thread would not be bad while finding the root of the problem. I do think the problems all stem from poor threadsafety, because all public sources suck at it. For example, parties should be synchronized but they aren't in any public source. There's also misuse of things, which can lead to deadlocks.

The reason I mentioned EXP tracking was because it used 5 inner classes and a LinkedList to manage something that could be done with a single HashMap containing the damage and a few helper methods. I ended up changing it when I had to implement 80:20 in Solaxia since it was a nightmare to work with in its previous state.

I have an AttackerEntry interface, SingleAttackerEntry, and PartyAttackerEntry impls. If you got rid of this, how did you accurately (and efficiently) do appropriate bonus party exp when a monster is killed? I don't remember the formulas, but it's something similar to:
Monster has 100 hp,
Player 1 (not in party) does 40 damage
Player 2 and 3 (in party) do 60 damage total

Player 2 and 3 should get something like monsterExp * .6 * .1 party bonus exp when the monster dies. I might be way off (both in the formula and in my game knowledge), but basically if it's just a hashmap, how did you efficiently calculate the collective damage ratio of the party when applying bonus exp?
 
Joined
Aug 10, 2008
Messages
858
Reaction score
516
I have an AttackerEntry interface, SingleAttackerEntry, and PartyAttackerEntry impls. If you got rid of this, how did you accurately (and efficiently) do appropriate bonus party exp when a monster is killed? I don't remember the formulas, but it's something similar to:
Monster has 100 hp,
Player 1 (not in party) does 40 damage
Player 2 and 3 (in party) do 60 damage total

Player 2 and 3 should get something like monsterExp * .6 * .1 party bonus exp when the monster dies. I might be way off (both in the formula and in my game knowledge), but basically if it's just a hashmap, how did you efficiently calculate the collective damage ratio of the party when applying bonus exp?

I compiled all of the damage into party pools when the monster died and then distributed based on that.

Map creation:
Code:
    private final HashMap<Integer, AtomicInteger> takenDamage = new HashMap<>();
Left is character's ID and right is damage dealt.

Tracking portion in damage method:
Code:
        int trueDamage = Math.min(hp, damage);

        hp -= damage;
        if (takenDamage.containsKey(from.getId())) {
            takenDamage.get(from.getId()).addAndGet(trueDamage);
        } else {
            takenDamage.put(from.getId(), new AtomicInteger(trueDamage));
        }

Actual calculation code after monster death (gets invoked by killBy later on) in a new method distributeExperience:
Code:
public void distributeExperience(int killerId) {
        if (isAlive()) {
            return;
        }
        int exp = getExp();
        int totalHealth = getMaxHp();
        Map<Integer, Integer> expDist = new HashMap<>();
        Map<Integer, Integer> partyExp = new HashMap<>();
        // 80% of pool is split amongst all the damagers
        for (Entry<Integer, AtomicInteger> damage : takenDamage.entrySet()) {
            expDist.put(damage.getKey(), (int) (0.80f * exp * damage.getValue().get() / totalHealth));
        }
        map.getCharacterReadLock().lock(); // avoid concurrent mod
        try {
            Collection<MapleCharacter> chrs = map.getCharacters();
            for (MapleCharacter mc : chrs) {
                if (expDist.containsKey(mc.getId())) {
                    boolean isKiller = mc.getId() == killerId;
                    int xp = expDist.get(mc.getId());
                    if (isKiller) {
                        xp += exp / 5;
                    }
                    MapleParty p = mc.getParty();
                    if (p != null) {
                        int pID = p.getId();
                        int pXP = xp + (partyExp.containsKey(pID) ? partyExp.get(pID) : 0);
                        partyExp.put(pID, pXP);
                    } else {
                        giveExpToCharacter(mc, xp, isKiller, 1);
                    }
                }
            }
        } finally {
            map.getCharacterReadLock().unlock();
        }
        for (Entry<Integer, Integer> party : partyExp.entrySet()) {
            distributeExperienceToParty(party.getKey(), party.getValue(), killerId, expDist);
        }
    }


distributeExperienceToParty
Code:
private void distributeExperienceToParty(int pid, int exp, int killer, Map<Integer, Integer> expDist) {
        LinkedList<MapleCharacter> members = new LinkedList<>();

        map.getCharacterReadLock().lock();
        Collection<MapleCharacter> chrs = map.getCharacters();
        try {
            for (MapleCharacter mc : chrs) {
                if (mc.getPartyId() == pid) {
                    members.add(mc);
                }
            }
        } finally {
            map.getCharacterReadLock().unlock();
        }

        final int minLevel = getLevel() - 5;

        int partyLevel = 0;
        int leechMinLevel = 0;

        for (MapleCharacter mc : members) {
            if (mc.getLevel() >= minLevel) {
                leechMinLevel = Math.min(mc.getLevel() - 5, minLevel);
            }
        }

        int leechCount = 0;
        for (MapleCharacter mc : members) {
            if (mc.getLevel() >= leechMinLevel) {
                partyLevel += mc.getLevel();
                leechCount++;
            }
        }

        for (MapleCharacter mc : members) {
            int id = mc.getId();
            int level = mc.getLevel();
            if (expDist.containsKey(id)
                    || level >= leechMinLevel) {
                boolean isKiller = killer == id;
                int xp = (int) (exp * 0.80f * level / partyLevel);
                if (isKiller) {
                    xp += (exp * 0.20f);
                }
                giveExpToCharacter(mc, xp, isKiller, leechCount);
            }
        }
    }

And giveExpToCharacter party distribution:
Code:
final int partyModifier = numExpSharers > 1 ? (110 + (5 * (numExpSharers - 2))) : 0;
...
                if (partyModifier > 0) {
                    partyExp = (int) (personalExp * ServerConstants.PARTY_EXPERIENCE_MOD * partyModifier / 1000f);
                }

And finally the code where it uses everything above in killBy(MapleCharacter)
Code:
        distributeExperience(killer != null ? killer.getId() : 0);

That formula also changed later on to 80:20 which splits the experience 80% damage and 20% last hit between foreign parties. This is further split by local party as 80% of the gain by level and 20% by last hit (this may be most damage on the party level in the actual server, but I was lazy and various online sources weren't clear about this). Also, I think that the killer is supposed to get the full 20% of the EXP rather than 20% of the party pool which is added on primarily because your party can potentially get a larger piece of that 20% than maybe someone would expect.
 
Newbie Spellweaver
Joined
Aug 6, 2014
Messages
56
Reaction score
42
I compiled all of the damage into party pools when the monster died and then distributed based on that.

Map creation:
Code:
    private final HashMap<Integer, AtomicInteger> takenDamage = new HashMap<>();
Left is character's ID and right is damage dealt.

Tracking portion in damage method:
Code:
        int trueDamage = Math.min(hp, damage);

        hp -= damage;
        if (takenDamage.containsKey(from.getId())) {
            takenDamage.get(from.getId()).addAndGet(trueDamage);
        } else {
            takenDamage.put(from.getId(), new AtomicInteger(trueDamage));
        }

Actual calculation code after monster death (gets invoked by killBy later on) in a new method distributeExperience:
Code:
public void distributeExperience(int killerId) {
        if (isAlive()) {
            return;
        }
        int exp = getExp();
        int totalHealth = getMaxHp();
        Map<Integer, Integer> expDist = new HashMap<>();
        Map<Integer, Integer> partyExp = new HashMap<>();
        // 80% of pool is split amongst all the damagers
        for (Entry<Integer, AtomicInteger> damage : takenDamage.entrySet()) {
            expDist.put(damage.getKey(), (int) (0.80f * exp * damage.getValue().get() / totalHealth));
        }
        map.getCharacterReadLock().lock(); // avoid concurrent mod
        try {
            Collection<MapleCharacter> chrs = map.getCharacters();
            for (MapleCharacter mc : chrs) {
                if (expDist.containsKey(mc.getId())) {
                    boolean isKiller = mc.getId() == killerId;
                    int xp = expDist.get(mc.getId());
                    if (isKiller) {
                        xp += exp / 5;
                    }
                    MapleParty p = mc.getParty();
                    if (p != null) {
                        int pID = p.getId();
                        int pXP = xp + (partyExp.containsKey(pID) ? partyExp.get(pID) : 0);
                        partyExp.put(pID, pXP);
                    } else {
                        giveExpToCharacter(mc, xp, isKiller, 1);
                    }
                }
            }
        } finally {
            map.getCharacterReadLock().unlock();
        }
        for (Entry<Integer, Integer> party : partyExp.entrySet()) {
            distributeExperienceToParty(party.getKey(), party.getValue(), killerId, expDist);
        }
    }


distributeExperienceToParty
Code:
private void distributeExperienceToParty(int pid, int exp, int killer, Map<Integer, Integer> expDist) {
        LinkedList<MapleCharacter> members = new LinkedList<>();

        map.getCharacterReadLock().lock();
        Collection<MapleCharacter> chrs = map.getCharacters();
        try {
            for (MapleCharacter mc : chrs) {
                if (mc.getPartyId() == pid) {
                    members.add(mc);
                }
            }
        } finally {
            map.getCharacterReadLock().unlock();
        }

        final int minLevel = getLevel() - 5;

        int partyLevel = 0;
        int leechMinLevel = 0;

        for (MapleCharacter mc : members) {
            if (mc.getLevel() >= minLevel) {
                leechMinLevel = Math.min(mc.getLevel() - 5, minLevel);
            }
        }

        int leechCount = 0;
        for (MapleCharacter mc : members) {
            if (mc.getLevel() >= leechMinLevel) {
                partyLevel += mc.getLevel();
                leechCount++;
            }
        }

        for (MapleCharacter mc : members) {
            int id = mc.getId();
            int level = mc.getLevel();
            if (expDist.containsKey(id)
                    || level >= leechMinLevel) {
                boolean isKiller = killer == id;
                int xp = (int) (exp * 0.80f * level / partyLevel);
                if (isKiller) {
                    xp += (exp * 0.20f);
                }
                giveExpToCharacter(mc, xp, isKiller, leechCount);
            }
        }
    }

And giveExpToCharacter party distribution:
Code:
final int partyModifier = numExpSharers > 1 ? (110 + (5 * (numExpSharers - 2))) : 0;
...
                if (partyModifier > 0) {
                    partyExp = (int) (personalExp * ServerConstants.PARTY_EXPERIENCE_MOD * partyModifier / 1000f);
                }

And finally the code where it uses everything above in killBy(MapleCharacter)
Code:
        distributeExperience(killer != null ? killer.getId() : 0);

That formula also changed later on to 80:20 which splits the experience 80% damage and 20% last hit between foreign parties. This is further split by local party as 80% of the gain by level and 20% by last hit (this may be most damage on the party level in the actual server, but I was lazy and various online sources weren't clear about this). Also, I think that the killer is supposed to get the full 20% of the EXP rather than 20% of the party pool which is added on primarily because your party can potentially get a larger piece of that 20% than maybe someone would expect.


I may be off in some details because I have not looked at your code in detail, just looking through it.

Let N = number of characters in map
Let M = number of parties in map
Let K = number of people who actually hit the monster
Let T = number of AttackerEntries in odinms
--
In your way, you go through every character in the map, even if they didn't hit the monster -- O(N) and populate a HashMap with all of the parties.
Then, for every party, you go through all the characters in the map again (?!) to check if they are in a party -- O(M * N)
I haven't stepped through your code too much, but it looks like you may not give bonus exp to party members who have not hit the monster, or are giving bonus exp to party members who aren't in the map. So your algorithm is O(M*N) for every monster, even if most players and parties in the map don't hit the monster, and MAY have a bug for an edge case.

Now compare to Odin's way.
At each hit, if an AttackerEntry doesn't exist, then it's made and added, else, it's gotten and added to (this can use a HashMap to be O(1) amortized) -- O(T).
The aggregation happens as damage is done, so all of the computation is O(1).
Then, when the monster dies, for all AttackerEntries, compute the damage ratio: O(T) (which is almost always <= O(N)). During computation for PartyAttackerEntry, the party is already known and the attackers are already known. This eliminates the O(M * N) computation.
Almost everything is done in constant time, except for the actual distribution of exp, which is Omega-bounded by K anyway.

You also use some janky external locking which I do not recommend at all. It's extremely error prone and is not worth just creating a new ImmutableSet or something in a threadsafe way. Also, unless you keep the Odin way of externally synchronizing on the monster during damage/kill (which I don't like), your code may not be threadsafe.

This isn't a jab at your code, but Odin's way is actually one of the most optimal ways to do this. Looking back at original OdinMS code, there is one class (OnePartyAttacker) which may be a little redundant. I didn't use it, but I was also implementing this like 4 years ago when I didn't know nearly as much as I do now, and if I was in this headspace again I might even use it.
 
Last edited:
Legendary Battlemage
Joined
Jan 23, 2013
Messages
695
Reaction score
101
To those commenting about RMI, and to the OP.

Unless RMI was added back into the source, lithium does not have it. Lithium was based off of the released version of arcanedev (tetrasea/crypticsea) and RMI was removed a long time ago by either RMZ, emily, or lightpepsi. I don't exactly remember which.
 
(O_o(o_O(O_O)o_O)O_o)
Loyal Member
Joined
Apr 9, 2009
Messages
1,088
Reaction score
322
To those commenting about RMI, and to the OP.

Unless RMI was added back into the source, lithium does not have it. Lithium was based off of the released version of arcanedev (tetrasea/crypticsea) and RMI was removed a long time ago by either RMZ, emily, or lightpepsi. I don't exactly remember which.

We are aware of that. The big question is whether or not rmi is a better option over mina, or maybe even netty. There's enough to pick from. Odin used RMI. Lithium uses mina and opinions seem to be very differentiating. For now however I'm still focussed on re-coding different parts of the source, and will leave mina in place. Eventually it will look like i'll need to conduct some performance test to decide whether to stick with mina or not.
 
Custom Title Activated
Loyal Member
Joined
Jan 18, 2010
Messages
3,109
Reaction score
1,139
We are aware of that. The big question is whether or not rmi is a better option over mina, or maybe even netty. There's enough to pick from. Odin used RMI. Lithium uses mina and opinions seem to be very differentiating. For now however I'm still focussed on re-coding different parts of the source, and will leave mina in place. Eventually it will look like i'll need to conduct some performance test to decide whether to stick with mina or not.

RMI != Mina. mina-core is used both in OdinMS AND Lithium, while RMI was a OdinMS thing and removed in Lithium. My personal opinion is Netty, but if you aren't writing that portion from scratch, then mina isn't the worst thing in the world to use (like others have already said)..
 
Skilled Illusionist
Joined
Dec 20, 2011
Messages
313
Reaction score
115
Netty. Maplestory has always had problems with mina.
 
(O_o(o_O(O_O)o_O)O_o)
Loyal Member
Joined
Apr 9, 2009
Messages
1,088
Reaction score
322
So small update I guess, as i stated before i'm not working around the clock to get this thing done asap. I just want to take time whenever I got some with the hope of creating something just slightly better.

Currently done:

- Odin alike packet processor
- Netty implementation instead of mina
- General re-structuring

I'm doing scripts (maps/ items externally) and WZ data providing (nx files?) next.

As for the netty implementation: I've been working on that quite a while, I was new to that kind of thing. Eventually I did use some bits that even released. I was personally creating listening Channels in multiple server files, but his/her implementation had 1 pretty neat and usefull class that could be called. Obv. I should've done that myself right away, but I can be messy sometimes and it could take a couple days before I'd check it again and see I got the same thing going on 3 locations, so I could write it shorter.
 
Skilled Illusionist
Joined
Dec 20, 2011
Messages
313
Reaction score
115
Can we see what kind of restructuring you are doing?
If you copied most of even's code, I would look into rewriting it for better implementation of netty. I am sure it works but it could of been written much better.
What made you switch to the old PacketProcessor?
 
(O_o(o_O(O_O)o_O)O_o)
Loyal Member
Joined
Apr 9, 2009
Messages
1,088
Reaction score
322
What made you switch to the old PacketProcessor?

The switch case xD
Nah, in a slightly older thread (i think i included it in the main post) there was a discussion in which moogra/superlol pointed out how it worked.

In the case of handling packets a linear array is fastest. There are mony reasons, including the layout in assembly, but i suggest reading the entire thread if you don't know the differences between laying out linearly or switching or if/else statements (though i assume you know more of it then me). Generally it seems to just be the best option.

I never really liked the way the switch looked either. I've always found the way the packet processor looked nicer and personally prefer separate, organised handlers over large files with multiple handlers belonging to the same category (though some tend to find that more ogranised).

As for the netty implementation, I used even's classes that handle initializing listening servers, since they seemed very neat and simple. I did all the initializing of the different servers and the mapleserverhandler myself after that. (aka i use his handling.netty classes). It gave me insight in how those things worked, but I'm still looking more into netty to see if the implementation is actually good, since atm i can't really tell yet.

I'm putting the thing on github soon, but want to finish some more things first :p
 
Joined
Nov 9, 2012
Messages
608
Reaction score
164
The switch case xD
Nah, in a slightly older thread (i think i included it in the main post) there was a discussion in which moogra/superlol pointed out how it worked.

In the case of handling packets a linear array is fastest. There are mony reasons, including the layout in assembly, but i suggest reading the entire thread if you don't know the differences between laying out linearly or switching or if/else statements (though i assume you know more of it then me). Generally it seems to just be the best option.

I never really liked the way the switch looked either. I've always found the way the packet processor looked nicer and personally prefer separate, organised handlers over large files with multiple handlers belonging to the same category (though some tend to find that more ogranised).

As for the netty implementation, I used even's classes that handle initializing listening servers, since they seemed very neat and simple. I did all the initializing of the different servers and the mapleserverhandler myself after that. (aka i use his handling.netty classes). It gave me insight in how those things worked, but I'm still looking more into netty to see if the implementation is actually good, since atm i can't really tell yet.

I'm putting the thing on github soon, but want to finish some more things first :p

I noticed in v83 server transitions were much faster (so going in-game after selecting a char, going in and out of cashshop, etc).
 
Skilled Illusionist
Joined
Dec 20, 2011
Messages
313
Reaction score
115
The switch case xD
Nah, in a slightly older thread (i think i included it in the main post) there was a discussion in which moogra/superlol pointed out how it worked.

In the case of handling packets a linear array is fastest. There are mony reasons, including the layout in assembly, but i suggest reading the entire thread if you don't know the differences between laying out linearly or switching or if/else statements (though i assume you know more of it then me). Generally it seems to just be the best option.

I don't think the speed of the packetprocessor vs the switch is something that is very noticeable.

I never really liked the way the switch looked either. I've always found the way the packet processor looked nicer and personally prefer separate, organised handlers over large files with multiple handlers belonging to the same category (though some tend to find that more ogranised).

Personally I would fall into the category that the switch allows more organized handling. You can use something similar to what Fraysa released to allow for annotation handling (This is better than what lithium does imo) and keep the small amount of files vs having 500 files for 500 different opcodes.

As for the netty implementation, I used even's classes that handle initializing listening servers, since they seemed very neat and simple. I did all the initializing of the different servers and the mapleserverhandler myself after that. (aka i use his handling.netty classes). It gave me insight in how those things worked, but I'm still looking more into netty to see if the implementation is actually good, since atm i can't really tell yet.

I'm putting the thing on github soon, but want to finish some more things first :p

I'm a tad curious on what your restructure looks like, pics please!
 
(O_o(o_O(O_O)o_O)O_o)
Loyal Member
Joined
Apr 9, 2009
Messages
1,088
Reaction score
322
I don't think the speed of the packetprocessor vs the switch is something that is very noticeable.

I didn't really notice a difference in speed no. But still I like the idea of it being technically the better solution speed wise, even though you won't notice a thing. And again, I prefer the structure.

Personally I would fall into the category that the switch allows more organized handling. You can use something similar to what Fraysa released to allow for annotation handling (This is better than what lithium does imo) and keep the small amount of files vs having 500 files for 500 different opcodes.

You and many others ;)
I was chatting with Fraysa when he was releasing annotation based packet handling, and at that point I was still thinking about doing that. But in the end I still went with the packet processor. That doesn't mean I won't use the annotation based system somewhere else though ;) (skill handling anyone?)

I'm a tad curious on what your restructure looks like, pics please!

NO! I won't show untill i find it worthy of showing!! xD Besides, at this point it's mostly deleted files, renamed files, moved files, a couple of changed up handlers in both name and function.. Not THAT much. You'll still recognise lithium with a shitload of new handler categories for recv. packets right away.

Again it'll be on github soon enough.
 
(O_o(o_O(O_O)o_O)O_o)
Loyal Member
Joined
Apr 9, 2009
Messages
1,088
Reaction score
322
How'd you guys feel about a GUI to do some basic server management? It'll be a window in which on the left you'll se the console running and it will include multiple options for server management and debugging.

I'm also thinking about creating a client for that, that can connect to the server and do basic administrator management aswell. Just so that you can manage it without always being on the host machine.

Let me know what you people think and if you like it which functions you want to see in that.

It would all be made in java using swing, since that's the only way I know how to make gui's atm.
 
Custom Title Activated
Loyal Member
Joined
Jan 18, 2010
Messages
3,109
Reaction score
1,139
How'd you guys feel about a GUI to do some basic server management? It'll be a window in which on the left you'll se the console running and it will include multiple options for server management and debugging.

I'm also thinking about creating a client for that, that can connect to the server and do basic administrator management aswell. Just so that you can manage it without always being on the host machine.

Let me know what you people think and if you like it which functions you want to see in that.

It would all be made in java using swing, since that's the only way I know how to make gui's atm.

A console would be cool. Tenchio had made one a while back (or, he was working on one; not sure if he ever finished). You can find it here: https://forum.ragezone.com/f427/v83-miscellaneous-1041962/

I liked his idea of attaching it like so and having a chat where you can just input command and view other information.
 
(O_o(o_O(O_O)o_O)O_o)
Loyal Member
Joined
Apr 9, 2009
Messages
1,088
Reaction score
322
A console would be cool. Tenchio had made one a while back (or, he was working on one; not sure if he ever finished). You can find it here: https://forum.ragezone.com/f427/v83-miscellaneous-1041962/

I liked his idea of attaching it like so and having a chat where you can just input command and view other information.

Ah pretty cool, guess i'll take a look at this and maybe borrow some structure from it ;)

I'm adding a few more "advanced" features though, such as the ability to send custom packets / opcodes more easily than trying to type them in-game. That's just something I want to be included.

As for other features; feel free to post requests xD
 
<3
Joined
Feb 4, 2011
Messages
481
Reaction score
123
Ah pretty cool, guess i'll take a look at this and maybe borrow some structure from it ;)

I'm adding a few more "advanced" features though, such as the ability to send custom packets / opcodes more easily than trying to type them in-game. That's just something I want to be included.

As for other features; feel free to post requests xD

You should borrow some ideas from various clientless bots (check out Hendi's clientless bot, and rice's) and adapt it for a private server staff member's usage. :)
 
(O_o(o_O(O_O)o_O)O_o)
Loyal Member
Joined
Apr 9, 2009
Messages
1,088
Reaction score
322
Sup? For those still following this (lemme guess: Nobody? xD), here's the next tiny-update. (Bigger updates soon! Almost done with my last exams!)

I've implemented netty a while back, but after some discussion with a friend i'm going to re-write it again. Trying to follow Eric 's advice removing the need for mapleclient and client_key attributes. I'm not 100% sure how i'm going to do that yet :p So far i only know the basics of netty/mina but I'm progressing ;) I'm here to learn!

Also by now I got a little question: What would you prefer? I've been chatting with some people and they all told me their experience with RMI was that it was pretty slow. I'm thinking of splitting the server into loose instances using Netty instead. Idk how that would play out, but maybe it's a bit more interesting then just plain RMI. But I know for sure I want to split things, since i'd like to be able to run Servers seperately.

I've also started re-organising skills, not focussing too much on how they are handled yet. Why? Well i find it hard to find structure in the chaos that is MapleStatEffect. I want to organise that before I look into handling.

I'm putting the git on in a month or so, after I got netty and skills done.

Also dw people! I'm putting credits in where it's due! (from people who guided me in this learning process and snippets of code i've used made by others, so far for example solaxia has been quite the inspirition, i really like it! so have ideas from people like fraysa, eric, dynamik, etc! they got some good tips all the damn time!)
 
Back
Top