I'm trying to code a packet listener for a little server. I'm very new to Java and this is the first time i mess around with networking. The whole idea it's recive the packet, match the packet id with it's class, pass the input stream to the constructor of the packet so it can be constructed and then give it to the packetHander, wich will have an overladed method for each packet. To achive this im using an array that maps the packets ids to the classes of each one, and using a method called decode to construct the packet. The problem it's the overloading of handlePacket it's not behaving as expected. Lets see some code.
I've the packet listener running in a thread and the run method looks like this:
public void run() {
try {
int packet_id;
while ((packet_id = istream.readInt()) != -1) {
plugin.getServer().getConsoleSender().sendMessage("[Comm] Recived packet " + packet_id);
Packet packet = decode(packet_id, istream);
plugin.getServer().getConsoleSender().sendMessage("[Comm] Packet is " + Class.forName(packet.getClass().getName()));
plugin.getServer().getConsoleSender().sendMessage("[Comm] Class is " + Packet00ReqIdentify.class.cast(packet).getClass().getName());
plugin.getServer().getConsoleSender().sendMessage("[Comm] Class is " + Class.forName(packet.getClass().getName()).getName());
handlePacket(packet);
}
} catch (IOException | NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | ClassNotFoundException e) {
e.printStackTrace();
}
}
The decode and handlePacket methods looks like this:
private void handlePacket(Packet00ReqIdentify packet) throws IOException {
plugin.getServer().getConsoleSender().sendMessage("[Comm] Got it!");
}
private void handlePacket(Packet packet) {
plugin.getServer().getConsoleSender().sendMessage("[Comm] Woops!");
}
private Packet decode(int packet_id, PacketInputStream istream) throws NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException, ClassNotFoundException, IOException {
Class<? extends Packet> packet_class = packets_ids.get(packet_id);
try {
Constructor<?> packet_constructor = packet_class.getConstructor(PacketInputStream.class);
return Class.forName(packet_class.getName()).asSubclass(Packet.class).cast(packet_constructor.newInstance(istream));
} catch (NoSuchMethodException e) {
return Class.forName(packet_class.getName()).asSubclass(Packet.class).cast(packet_class.newInstance());
}
}
packets_ids it's an array that contains the reference to the class of each packet, indexed by theirs ids:
private static ArrayList<Class<? extends Packet>> packets_ids;
It gets initialized this way:
private static void registerPacket(int id, Class<? extends Packet> oclass) {
packets_ids.add(id, oclass);
}
static {
packets_ids = new ArrayList<Class<? extends Packet>>();
registerPacket(Packet00ReqIdentify.assigned_pid, Packet00ReqIdentify.class);
registerPacket(Packet01Identify.assigned_pid, Packet01Identify.class);
registerPacket(Packet02Heartbeat.assigned_pid, Packet02Heartbeat.class);
}
If i execute this and test it sending a packet of type 00, i get this:
17:37:49 [INFO] [Comm] Connection established to localhost:11000
17:37:49 [INFO] [Comm] Recived packet 0
17:37:49 [INFO] [Comm] Packet is class com.gamerarg.commclient.protocol.Packet00ReqIdentify
17:37:49 [INFO] [Comm] Class is com.gamerarg.commclient.protocol.Packet00ReqIdentify
17:37:49 [INFO] [Comm] Class is com.gamerarg.commclient.protocol.Packet00ReqIdentify
17:37:49 [INFO] [Comm] Woops!
So it means the packet00 has not been cathed by "handlePacket(Packet00ReqIdentify packet)". If I make an explicit cast to "packet" in handlePacket call it works. So the questions are:
Why this it's not working? When i print the class names for both i get the same.
How can I make it work? I been struggling with this for 6 or 7 hours for now, reading, googling, trying and seeing code from others. One simpler solution it's to make an switch using the packet id but i want something more elegant. Maybye i'm wrong from the base idea so that's why i posted the code, i'm open to suggestions and ideas of more experienced people in the subject, incluiding recomendations of material in the subject.
Thanks!
In each of your
Packet
subclasses, implement a methodpublic void handle()
which does what you need to handle the packet. Eitherhandle()
inPacket
, orhandle()
as abstract inPacket
, and makePacket
an abstract class, orhandle()
inPacket
and makePacket
an interface.Then replace
with
This is polymorphism in action. It will work at run time, checking the class of the object that
packet
references, and calling the right version of thehandle
method.If
handle()
needs access to the originalPacketListener
, then declare it aspublic void handle(PacketListener listener)
and call it aspacket.handle(this);