[Tools] [Release] using methods as packet handlers

Joined
Jul 28, 2009
Messages
339
Reaction score
200
Code:
package chatservernew.util;

import chatservernew.net.Client;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import org.jboss.netty.buffer.ChannelBuffer;

/**
 *
 * @author Jay
 */
public class HandlerManager {

    public static enum HandlerList {
        CHAT("chatservernew.net.handlers.AuthHandler");
        
        private final String className;

        private HandlerList(String className) {
            this.className = className;
        }

        public String getName() {
            return className;
        }
    }

    private static Map<Byte, Method> handlers = new HashMap<Byte, Method>();

    static {
        try {
            initialize();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    private static void initialize() throws ClassNotFoundException {
        int count = 0;
        for (HandlerList handler : HandlerList.values()) {
            Class c = Class.forName(handler.getName());
            for (Method method : c.getMethods()) {
                PacketIdentifier annotation = method.getAnnotation(PacketIdentifier.class);
                if (annotation != null) {
                    if (isValidMethod(method)) {
                        if (handlers.containsKey(annotation.packetId())) {
                            System.out.println("Duplicate handler for packetId: " + annotation.packetId());
                        } else {
                            count++;
                            handlers.put(annotation.packetId(), method);
                        }
                    } else {
                        System.out.println("Failed to add handler with method name of: " + method.getName() + " in " + handler.getName());
                    }
                }
            }
        }
        System.out.println(count + " handlers loaded.");
    }

    private static boolean isValidMethod(Method method) {
        Class[] types = method.getParameterTypes();
        return types.length == 2 && types[0].equals(Client.class) && types[1].equals(ChannelBuffer.class);
    }

    public static void handle(Client client, ChannelBuffer data, byte packetId) {
        Method method = handlers.get(packetId);
        try {
            if (method != null) {
                method.invoke(null, client, data);
            }
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }
}

Code:
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package chatservernew.util;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 *
 * @author Jay
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface PacketIdentifier {
    byte packetId();
}

Sample:
Code:
    @PacketIdentifier(packetId = Opcodes.RECV_LOGIN)
    public static void handleLogin(Client client, ChannelBuffer buffer) {

needs some changing for odin.

see: isValidMethod
 
Last edited:
Re: [Release] using methods as packet handlers

Sexy Jay, but how about you figure out how to do that wacky C# method in Java. :D:
 
Re: [Release] using methods as packet handlers

This forum is full of fail.
What's with the ugly fucking loading of hashmaps through jar iterators, like wtf? (referring to other release)

Edit:
Nice to see some annotation usage, but I assume you don't know the downsides of RetentionPolicy.RUNTIME ?

Another edit:
Ignore what I've just stated, this release is quite okay. Helps a lot to clean up some code. The other release about the loading of handlers through jar iterator is the one full of fail.
 
Last edited:
Re: [Release] using methods as packet handlers

What's the pro of using methods for this though? Seems to me like you would get huge ass methods... and I think classes were invented for that very reason.

Btw, what do the @'s do at:

Code:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface PacketIdentifier {
    byte packetId();
}

?
 
Last edited:
Re: [Release] using methods as packet handlers

What's the pro of using methods for this though? Seems to me like you would get huge ass methods... and I think classes were invented for that very reason.

Btw, what do the @'s do at:

Code:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface PacketIdentifier {
    byte packetId();
}

?

 
Re: [Release] using methods as packet handlers

That's Java ._.

Was talking about this method Osi:
Code:
public uint ReadOffset()
{
   uint offset = (uint)BaseStream.Position;
   offset = (offset - Header.FStart) ^ uint.MaxValue;
   offset *= Hash;
   offset -= CryptoConstants.WZ_OffsetConstant;
   offset = WzTool.RotateLeft(offset, (byte)(offset & 0x1F));
   uint encryptedOffset = ReadUInt32();
   offset ^= encryptedOffset;
   offset += Header.FStart * 2;
   return offset;
}
With the uint multiplication I approached you with yesterday. :X

Since this release is obviously Java...
 
Re: [Release] using methods as packet handlers

What's the pro of using methods for this though? Seems to me like you would get huge ass methods... and I think classes were invented for that very reason.

Btw, what do the @'s do at:

Code:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface PacketIdentifier {
    byte packetId();
}

?

Take a look at Vana. They use a switch & functions for handlers. And seperating one big method per class is a stupid idea.
 
Re: [Release] using methods as packet handlers

That's what me and Erwin did on Clarity. We create a class
for the ChannelServer or LoginServer "User Packet Handlers",
Then we have it's functions. For example..

Code:
Private Sub HandleLoadCharacter(ByRef pPacket As PacketReader)
        Dim id As Integer = pPacket.ReadInt()
        If Not mIncomingConnections.ContainsKey(mSession.mRemoteIP) Then
            'mSession.Disconnect()
            'Return
            ' IP check, doesn't really work yet.
        Else
            mIncomingConnections.Remove(mSession.mRemoteIP)
        End If

        If DatabaseConnection.ExecuteQuery("SELECT * FROM characters WHERE ID = " & id & " AND world_id = " & mWorldID) = 0 Then
            mSession.Disconnect()
            Return
        End If

        Dim reader = DatabaseConnection.mReader
        reader.Read()
        mCharacter = New Character()

        Dim character = mCharacter ' I'm lazy

        character.ID = reader.GetInt32("ID")
        character.Name = reader.GetString("name")
        character.Gender = reader.GetByte("gender")
        character.Skin = reader.GetByte("skin")
        character.Hair = reader.GetInt32("hair")
        character.Face = reader.GetInt32("face")

        character.MapID = reader.GetInt32("map")
        character.IntialSpawnPoint = reader.GetByte("pos")

        character.Level = reader.GetByte("level")
        character.Job = reader.GetInt16("job")
        character.Str = reader.GetInt16("str")
        character.Dex = reader.GetInt16("dex")
        character.Int = reader.GetInt16("int")
        character.Luk = reader.GetInt16("luk")
        character.HP = reader.GetInt16("chp")
        character.MaxHP = reader.GetInt16("mhp")
        character.MP = reader.GetInt16("cmp")
        character.MaxMP = reader.GetInt16("mmp")
        character.RemainingAP = reader.GetInt16("ap")
        character.RemainingSP = reader.GetInt16("sp")
        character.EXP = reader.GetInt32("exp")
        character.Fame = reader.GetInt16("fame")
        mBuddylistSize = reader.GetInt16("buddylist_size")

        character.LoadEquips()

        mInventory = New CharacterInventory(Me)
        mSkills = New CharacterSkills(Me)
        mQuests = New CharacterQuests(Me)
        mBook = New CharacterMonsterbook(Me)
        DatabaseConnection.ExecuteQuery("UPDATE accounts SET loggedin = 1 WHERE id = " & character.ID) 'Just in case.. ;).
        MapPackets.SendEnterFieldNew(Me)
    End Sub
 
Re: [Release] using methods as packet handlers

That's what me and Erwin did on Clarity. We create a class
for the ChannelServer or LoginServer "User Packet Handlers",
Then we have it's functions. For example..

Code:
Private Sub HandleLoadCharacter(ByRef pPacket As PacketReader)
        Dim id As Integer = pPacket.ReadInt()
        If Not mIncomingConnections.ContainsKey(mSession.mRemoteIP) Then
            'mSession.Disconnect()
            'Return
            ' IP check, doesn't really work yet.
        Else
            mIncomingConnections.Remove(mSession.mRemoteIP)
        End If

        If DatabaseConnection.ExecuteQuery("SELECT * FROM characters WHERE ID = " & id & " AND world_id = " & mWorldID) = 0 Then
            mSession.Disconnect()
            Return
        End If

        Dim reader = DatabaseConnection.mReader
        reader.Read()
        mCharacter = New Character()

        Dim character = mCharacter ' I'm lazy

        character.ID = reader.GetInt32("ID")
        character.Name = reader.GetString("name")
        character.Gender = reader.GetByte("gender")
        character.Skin = reader.GetByte("skin")
        character.Hair = reader.GetInt32("hair")
        character.Face = reader.GetInt32("face")

        character.MapID = reader.GetInt32("map")
        character.IntialSpawnPoint = reader.GetByte("pos")

        character.Level = reader.GetByte("level")
        character.Job = reader.GetInt16("job")
        character.Str = reader.GetInt16("str")
        character.Dex = reader.GetInt16("dex")
        character.Int = reader.GetInt16("int")
        character.Luk = reader.GetInt16("luk")
        character.HP = reader.GetInt16("chp")
        character.MaxHP = reader.GetInt16("mhp")
        character.MP = reader.GetInt16("cmp")
        character.MaxMP = reader.GetInt16("mmp")
        character.RemainingAP = reader.GetInt16("ap")
        character.RemainingSP = reader.GetInt16("sp")
        character.EXP = reader.GetInt32("exp")
        character.Fame = reader.GetInt16("fame")
        mBuddylistSize = reader.GetInt16("buddylist_size")

        character.LoadEquips()

        mInventory = New CharacterInventory(Me)
        mSkills = New CharacterSkills(Me)
        mQuests = New CharacterQuests(Me)
        mBook = New CharacterMonsterbook(Me)
        DatabaseConnection.ExecuteQuery("UPDATE accounts SET loggedin = 1 WHERE id = " & character.ID) 'Just in case.. ;).
        MapPackets.SendEnterFieldNew(Me)
    End Sub

So, it's a a really huge class filled with subroutines? I hope you guys don't change version and the structure doesn't change.
 
Re: [Release] using methods as packet handlers

Nice release Jay, but not really anything towards an improvement. If using packet handlers, the simplest and easiest way to go is by using a switch statement based on Op-Codes.

But to go about everything in a professional way would be to not use Packet Handlers at all. Handling receiving/sending packets in their appropriate classes is how most games do it.

Example: c.getPlayer().Move(inPacket);

Creating packets would be the same way. Instead of called a static class to build packets, they are built where you are using them. Simplifies things because they can be built custom according to the situation, and it removes all of the messy varying dependencies like having a bunch of if-statements for different situations.

But, as with any coding, it's all about the programmer's preference. =)

So, once again, nice job Jay! Very interesting approach.
 
Re: [Release] using methods as packet handlers

Nice release Jay, but not really anything towards an improvement. If using packet handlers, the simplest and easiest way to go is by using a switch statement based on Op-Codes.

But to go about everything in a professional way would be to not use Packet Handlers at all. Handling receiving/sending packets in their appropriate classes is how most games do it.

Example: c.getPlayer().Move(inPacket);

Creating packets would be the same way. Instead of called a static class to build packets, they are built where you are using them. Simplifies things because they can be built custom according to the situation, and it removes all of the messy varying dependencies like having a bunch of if-statements for different situations.

But, as with any coding, it's all about the programmer's preference. =)

So, once again, nice job Jay! Very interesting approach.

You could map handlers inside of MapleCharacter if you wanted to.

That's what me and Erwin did on Clarity. We create a class
for the ChannelServer or LoginServer "User Packet Handlers",
Then we have it's functions. For example..

Code:
Private Sub HandleLoadCharacter(ByRef pPacket As PacketReader)
        Dim id As Integer = pPacket.ReadInt()
        If Not mIncomingConnections.ContainsKey(mSession.mRemoteIP) Then
            'mSession.Disconnect()
            'Return
            ' IP check, doesn't really work yet.
        Else
            mIncomingConnections.Remove(mSession.mRemoteIP)
        End If

        If DatabaseConnection.ExecuteQuery("SELECT * FROM characters WHERE ID = " & id & " AND world_id = " & mWorldID) = 0 Then
            mSession.Disconnect()
            Return
        End If

        Dim reader = DatabaseConnection.mReader
        reader.Read()
        mCharacter = New Character()

        Dim character = mCharacter ' I'm lazy

        character.ID = reader.GetInt32("ID")
        character.Name = reader.GetString("name")
        character.Gender = reader.GetByte("gender")
        character.Skin = reader.GetByte("skin")
        character.Hair = reader.GetInt32("hair")
        character.Face = reader.GetInt32("face")

        character.MapID = reader.GetInt32("map")
        character.IntialSpawnPoint = reader.GetByte("pos")

        character.Level = reader.GetByte("level")
        character.Job = reader.GetInt16("job")
        character.Str = reader.GetInt16("str")
        character.Dex = reader.GetInt16("dex")
        character.Int = reader.GetInt16("int")
        character.Luk = reader.GetInt16("luk")
        character.HP = reader.GetInt16("chp")
        character.MaxHP = reader.GetInt16("mhp")
        character.MP = reader.GetInt16("cmp")
        character.MaxMP = reader.GetInt16("mmp")
        character.RemainingAP = reader.GetInt16("ap")
        character.RemainingSP = reader.GetInt16("sp")
        character.EXP = reader.GetInt32("exp")
        character.Fame = reader.GetInt16("fame")
        mBuddylistSize = reader.GetInt16("buddylist_size")

        character.LoadEquips()

        mInventory = New CharacterInventory(Me)
        mSkills = New CharacterSkills(Me)
        mQuests = New CharacterQuests(Me)
        mBook = New CharacterMonsterbook(Me)
        DatabaseConnection.ExecuteQuery("UPDATE accounts SET loggedin = 1 WHERE id = " & character.ID) 'Just in case.. ;).
        MapPackets.SendEnterFieldNew(Me)
    End Sub

You should do what Astaelan did and use attributes & reflection to get the same effect as this :)
 
Last edited:
Re: [Release] using methods as packet handlers

You could map handlers inside of MapleCharacter if you wanted to.



You should do what Astaelan did and use attributes & reflection to get the same effect as this :)

We also use switch select case for Opcode like xcheater and Moo do, you can take a look.
Code:
Public Sub OnPacketReceived(ByRef pPacket As PacketReader)
        Select Case pPacket.ReadShort()
            Case ClientMessages.Login
                TryLogin(pPacket)
            Case ClientMessages.Pong
                mPonged = True
            Case ClientMessages.AfterLogin
                HandlePIN(pPacket)
            Case ClientMessages.SetGender
                HandleGenderSet(pPacket)
            Case ClientMessages.RegisterPin
                HandleRegisterPIN(pPacket)
            Case ClientMessages.GetWorldList
                HandleWorldlist()
            Case ClientMessages.GetWorldLoadAgain
                HandleWorldlist()
            Case ClientMessages.GetWorldLoad
                HandleWorldLoad(pPacket)
            Case ClientMessages.SelectChannel
                HandleSelectChannel(pPacket)
            Case ClientMessages.PlayerNameCheck
                HandleNameCheck(pPacket)
            Case ClientMessages.PlayerCreate
                HandleCreateCharacter(pPacket)
            Case ClientMessages.ViewAllCharacters
                HandleViewAllCharacters(pPacket)

            Case ClientMessages.DeleteCharacter
                HandleCharacterDelete(pPacket)
            Case ClientMessages.SelectCharacterNoPIC
                HandleNoPICAndConnect(pPacket)
            Case ClientMessages.SelectCharacterRegisterPIC
                HandleRegisterPICAndConnect(pPacket)
            Case ClientMessages.SelectCharacterUsePIC
                HandleUsePICAndConnect(pPacket)

            Case ClientMessages.SelectVaCCharacterRegisterPIC
                HandleRegisterPICAndConnect(pPacket, True)
            Case ClientMessages.SelectVaCCharacterUsePIC
                HandleUsePICAndConnect(pPacket, True)

            Case ClientMessages.ViewAllCharactersFlag
                Dim isOn As Boolean = pPacket.ReadBoolean()
                If isOn And mState = UserLoginState.SelectingWorld Then
                    mState = UserLoginState.ChoosingCharacter
                ElseIf Not isOn And mState = UserLoginState.ChoosingCharacter Then
                    mState = UserLoginState.SelectingWorld
                End If

            Case Else
                Logger.writeLog(LogTypes.Info, "Unknown packet received from client {0}: {1}", mSession.mRemoteEndPoint, pPacket)
        End Select
    End Sub

And i'm also sure someone will ask. ClientMessages is RecvOpcode, yeah.
 
Last edited:
Back