Generics in Events?

I am trying to use generics in a costum event. Like this:

public class Packetevent<P extends Packet> extends AbstractEvent implements Cancellable {

    private P packet;
    private boolean cancel = false;

    public Packetevent(P packet){
        this.packet = packet;
    }

    public P getPacket() {
        return packet;
    }

    public boolean hasPacket(){
        return getPacket() != null;
    }

    @Override
    public boolean isCancelled() {
        return this.cancel;
    }

    @Override
    public void setCancelled(boolean cancel) {
        this.cancel = cancel;
    }
}

Basicly if I give a playerJoinPacket in an event. If a plugin is listening with this:

@Subscribe
    public void onPacketsend(PacketEvent<PacketUpdateSign> event){
}

It should only give of an event when the event contains a PacketUpdateSign. But no. Even with a normal Packet it gives of this event. Even without the packet being a PacketUpdateSign?

Am I misunderstanding something? Or making things to difficult? Because I refuse to make an event for every freaking packet in the game xD.

Erasure… Generics aren’t preserved at runtime. So that won’t work unless the eventbus is specifically built for that functionality.

(I know that that’s not 100% accurate)

1 Like

Oh indeed, but can this be seen as a flaw in the eventbus? Or is this way of handling events inappropriate?

It is not a flaw of the eventbus.

Generics are not existent at runtime, that means every PacketEvent<xyz> becomes a simple PacketEvent.

You can create an event class for every packet type, or build your own event system.

2 Likes

I think I will have to make an event for every packet than. Building my own event system will have the same problem as the sponge eventbus.

Not really, you can have a GenericWrapper that stores the actual class and has a getInstance that uses that stored class as the parameter. That’s still plenty ugly, but less ugly than many many events.

Hmmm can’t really follow to be honest ;-;. How will that help the eventbus to filter out not needed events? I would like to avoid that many plugins do this.

if (object instanceof PacketWhatever)
   ((PacketWhatever)object).getWhatever();

I understood that instanceof and casting is really expensive. Imaging 3 plugins doing this on the same event.

I misunderstood the issue, you could have the methods have a @HandlesPacket(PacketType). And register the methods in the event system as Map<Class<? extends BasePacket>, List<Method>> to store the methods that get called for the packet events.

1 Like

Hmm I don’t know if I wanna go this approach, because the custom event system. But it gave me an idea thought :smiley: .

This is actually really just a limitation in Sponge’s event bus. While it is correct that generics are mostly removed at compile time due to type erasure, they’re not deleted everywhere. You would be able to get the packet class used in the generic event handler method, but matching an instance of PacketEvent to one of these event handler methods would be difficult.

Generally said for Sponge’s event bus:

  • We can get which type arguments are referenced in the event handler method at runtime (using reflection)

  • We can not get which type arguments were used when creating the event instance (due to type erasure), which would be required for matching an existing event instance to a list of event handlers with their type arguments.

    • For example, if Sponge’s event bus would register event handler methods with their type arguments, you would have a CommandEvent<T extends CommandSource>, and give a CommandEvent<Player> to our event bus.
      We would have no way to say if the given event instance is a CommandEvent<Player> or maybe a CommandEvent<Console>, because due to type erasure we only know it’s a CommandEvent from the instance at runtime, but not which CommandEvent specifically.

This makes support for generic events for custom events almost impossible, because you can’t say which type arguments the event instance is representing.

Because you know how the packet event classes look like, there would be no problem to implement this using a custom event bus, because you can simply check like packetEvent.getPacket().getClass() to get the type argument required to listen to this specific PacketEvent.

Here is a small (inefficient) example with a custom event bus proving it is possible:

When running the application, it will correctly print the following:

CHAT: Hello!
COMMAND: /dosomething
1 Like

Thats awsome :smiley:. But why would you call this inefficient? Looks more efficient than what I have now. (Sadly enough I still don’t have a workspace up, Its just running in the sponge workspace and I can’t push that to github ;-:wink:

Personally I wouldn’t rely on reflection for calling event handlers for events which will be frequently called. Packets will get fired several times every tick so you need to make sure your packet API isn’t slowing down the whole server.

Akward, The SpongeEventBus also uses reflection. But I think I may go for an Costum Observer implementation without annotations. A bit like the java swing actionhandlers are build.

No, it doesn’t. It uses reflection to find the event handlers, but it is generating classes dynamically at runtime to call the event handlers.

2 Likes