feat(api): add immutable packet listener

This commit is contained in:
Maurice Eisenblätter 2025-03-26 17:52:32 +01:00
parent 30fe6465db
commit 0b03f1da71
No known key found for this signature in database
GPG Key ID: 2E553EFBAE92FB3A
17 changed files with 157 additions and 106 deletions

View File

@ -37,13 +37,9 @@ public class Example {
packet.accessor().update(a -> {
a.update(ParticleData.class, 0, b -> {
b.set(int.class, 0, -1);
});
});
packet.accessor().getAccessor(ParticleData.class, 0).update(accessor -> {
});
packet.accessor().update(accessor -> {
@ -51,8 +47,10 @@ public class Example {
accessor.set(Integer.class, 1, 1235);
accessor.set(String.class, 1, "world");
accessor.getAccessor(Object.class, 1).getAccessor(Object.class, 1).update(mutableAccessor -> {
accessor.update(Object.class, 1, a -> {
a.update(Object.class, 1, b -> {
b.set(int.class, 2, -1);
});
});
accessor.update(Object.class, 1, mutableAccessor -> {
@ -98,7 +96,7 @@ public class Example {
// do processing here ...
// write changes to packet ...
context.addAsyncTransmissionListener(chunk::markSent);
context.addTransmissionListener(chunk::markSent);
context.resumeProcessing();
});
@ -115,7 +113,7 @@ public class Example {
// do heavy processing here ...
// write changes to packet ...
context.addAsyncTransmissionListener(chunk::markSent);
context.addTransmissionListener(chunk::markSent);
context.resumeProcessing();
});
});

View File

@ -1,23 +0,0 @@
package dev.protocollib.api.listener;
import org.jetbrains.annotations.NotNull;
/**
* Representing the context of an asynchronous packet listener.
*/
public interface AsyncPacketListenerContext extends SyncPacketListenerContext {
/**
* Singles the listener is done with processing the packet.
*/
void resumeProcessing();
/**
* Singles the listener is done with processing the packet and finished
* with an exception.
*
* @param throwable the processing exception
*/
void resumeProcessingWithException(@NotNull Throwable throwable);
}

View File

@ -4,9 +4,21 @@ import org.jetbrains.annotations.NotNull;
import dev.protocollib.api.packet.PacketContainer;
/**
* Functional interface for handling immutable packets.
*
* <p>An immutable packet listener can be executed either synchronously or asynchronously
* depending on how it was registered. If registered asynchronously, it will be executed
* in parallel with other listeners, ensuring non-blocking packet processing.</p>
*/
@FunctionalInterface
public interface ImmutablePacketListener {
/**
* Handles a packet that was sent or received.
*
* @param packet the immutable packet to handle
* @param context the context providing additional information about the packet
*/
void handlePacket(@NotNull PacketContainer packet, @NotNull ImmutablePacketListenerContext context);
}

View File

@ -4,6 +4,9 @@ import org.jetbrains.annotations.NotNull;
import dev.protocollib.api.Connection;
/**
* Context for immutable packet listeners.
*/
public interface ImmutablePacketListenerContext {
/**
@ -19,12 +22,14 @@ public interface ImmutablePacketListenerContext {
*
* @return true if the packet is cancelled, false otherwise
*/
boolean isCancelledVolatile();
boolean isCancelled();
/**
* Adds a listener to be invoked after the packet is sent or received.
* Adds a listener to be invoked after the packet is fully sent or received.
* The transmission listener will get invoked on the underlying channel's
* event-loop.
*
* @param listener the transmission listener to invoke
*/
void addAsyncTransmissionListener(@NotNull PacketTransmissionListener listener);// TODO async via netty
void addTransmissionListener(@NotNull PacketTransmissionListener listener);
}

View File

@ -5,7 +5,7 @@ import org.jetbrains.annotations.NotNull;
import dev.protocollib.api.packet.MutablePacketContainer;
/**
* Functional interface for handling packets asynchronously.
* Functional interface for handling and manipulating packets asynchronously.
*
* <p>Once a packet is processed by the listener, the context's
* {@code resumeProcessing()} or {@code resumeProcessingWithException(Throwable)}
@ -16,19 +16,19 @@ import dev.protocollib.api.packet.MutablePacketContainer;
* </p>
*/
@FunctionalInterface
public interface AsyncPacketListener {
public interface MutableAsyncPacketListener {
/**
* Handles a packet that was sent or received, asynchronously.
*
* <p>Once processing is complete, ensure that one of the {@code resumeProcessing}
* methods from the {@link AsyncPacketListenerContext} is called. This allows the
* methods from the {@link MutableAsyncPacketListenerContext} is called. This allows the
* packet to continue to the next listener. If not called, the packet will remain
* in a waiting state and will only proceed after a timeout occurs.</p>
* in a waiting state and will eventually timeout.</p>
*
* @param packet the packet to handle
* @param context the context providing additional information about the packet and connection
*/
void handlePacket(@NotNull MutablePacketContainer packet, @NotNull AsyncPacketListenerContext context);
void handlePacket(@NotNull MutablePacketContainer packet, @NotNull MutableAsyncPacketListenerContext context);
}

View File

@ -0,0 +1,25 @@
package dev.protocollib.api.listener;
import org.jetbrains.annotations.NotNull;
/**
* Context of a mutable asynchronous packet listener.
*/
public interface MutableAsyncPacketListenerContext extends MutableSyncPacketListenerContext {
/**
* Singles the listener is done with processing the packet. Handing
* it over to the next asynchronous packet listener.
*/
void resumeProcessing();
/**
* Singles the listener is done with processing the packet and finished
* with an exception. Handing it over to the next asynchronous packet
* listener.
*
* @param throwable the processing exception
*/
void resumeProcessingWithException(@NotNull Throwable throwable);
}

View File

@ -5,17 +5,17 @@ import org.jetbrains.annotations.NotNull;
import dev.protocollib.api.packet.MutablePacketContainer;
/**
* Functional interface for handling packets synchronously.
* Functional interface for handling and manipulating packets synchronously.
*/
@FunctionalInterface
public interface SyncPacketListener {
public interface MutableSyncPacketListener {
/**
* Synchronously handles a packet that was sent or received.
* Handles a packet that was sent or received, synchronously.
*
* @param packet the packet to handle
* @param context the context providing additional information about the packet and functions
*/
void handlePacket(@NotNull MutablePacketContainer packet, @NotNull SyncPacketListenerContext context);
void handlePacket(@NotNull MutablePacketContainer packet, @NotNull MutableSyncPacketListenerContext context);
}

View File

@ -0,0 +1,18 @@
package dev.protocollib.api.listener;
/**
* Context of a mutable synchronous packet listener.
*/
public interface MutableSyncPacketListenerContext extends ImmutablePacketListenerContext {
/**
* Sets whether the packet handling is cancelled. If cancelled, the packet will
* not be processed further unless a listener is using the
* {@link PacketListenerBuilder.WithType#includeCanceledPackets() includeCanceledPackets} flag.
*
* @param cancelled true to cancel the packet, false to allow processing
*
* @see PacketListenerBuilder
*/
void setCancelled(boolean cancelled);
}

View File

@ -9,6 +9,10 @@ import dev.protocollib.api.packet.PacketType;
/**
* Builder for creating and registering packet listeners.
*
* <p>This builder allows configuring packet listeners with various settings,
* including the packet types they should handle, execution priority, and behavior
* regarding canceled packets and packet bundles.</p>
*/
public interface PacketListenerBuilder {
@ -49,6 +53,7 @@ public interface PacketListenerBuilder {
*
* @param bundleBehavior the bundle behavior to apply
* @return the same builder for further configuration
*
* @see PacketListenerBundleBehavior
*/
@Contract("_ -> this")
@ -67,37 +72,55 @@ public interface PacketListenerBuilder {
* Allows the listener to modify packets. By default, listeners are read-only and
* cannot modify packets.
*
* @return the same builder for further configuration
* @return the mutable packet listener builder for further configuration
*/
@NotNull
PacketListenerBuilder.Mutable mutable();
/**
* Registers the packet listener to operate synchronously. The listener will be executed
* in order relative to other synchronous listeners.
*
* @param listener the immutable synchronous packet listener to register
* @return the packet listener registration instance
*/
@NotNull
PacketListenerRegistration registerSync(@NotNull ImmutablePacketListener listener);
/**
* Registers the packet listener to operate asynchronously. The listener will be executed
* in parallel with other asynchronous listeners, ensuring non-blocking packet processing.
*
* @param listener the immutable asynchronous packet listener to register
* @return the packet listener registration instance
*/
@NotNull
PacketListenerRegistration registerAsync(@NotNull ImmutablePacketListener listener);
}
/**
* Interface for building a mutable packet listener, allowing packet modifications.
*/
public interface Mutable {
/**
* Registers the packet listener to operate synchronously. The listener
* will always get called on the main game thread.
* Registers the packet listener to operate synchronously. The listener will always
* be executed on the main game thread.
*
* @param listener the synchronous packet listener to register
* @return the same builder for further configuration
* @param listener the synchronous mutable packet listener to register
* @return the packet listener registration instance
*/
@NotNull
PacketListenerRegistration registerSync(@NotNull SyncPacketListener listener);
PacketListenerRegistration registerSync(@NotNull MutableSyncPacketListener listener);
/**
* Registers the packet listener to operate asynchronously.
* Registers the packet listener to operate asynchronously. The listener will be executed
* in parallel with other asynchronous listeners, ensuring non-blocking packet processing.
*
* @param listener the asynchronous packet listener to register
* @return the same builder for further configuration
* @param listener the asynchronous mutable packet listener to register
* @return the packet listener registration instance
*/
@NotNull
PacketListenerRegistration registerAsync(@NotNull AsyncPacketListener listener);
PacketListenerRegistration registerAsync(@NotNull MutableAsyncPacketListener listener);
}
}

View File

@ -1,41 +0,0 @@
package dev.protocollib.api.listener;
import org.jetbrains.annotations.NotNull;
import dev.protocollib.api.Connection;
/**
* Representing the context of a synchronous packet listener.
*/
public interface SyncPacketListenerContext {
/**
* Retrieves the connection associated with the packet.
*
* @return the connection handling the packet
*/
@NotNull
Connection connection();
/**
* Checks if the packet handling has been cancelled.
*
* @return true if the packet is cancelled, false otherwise
*/
boolean isCancelled();
/**
* Sets whether the packet handling is cancelled. If cancelled, the packet
* will not be processed further.
*
* @param cancelled true to cancel the packet, false to allow processing
*/
void setCancelled(boolean cancelled);
/**
* Adds a listener to be invoked after the packet is sent or received.
*
* @param listener the transmission listener to invoke
*/
void addAsyncTransmissionListener(@NotNull PacketTransmissionListener listener);// TODO async via netty
}

View File

@ -1,5 +1,7 @@
package dev.protocollib.api.packet;
import org.jetbrains.annotations.NotNull;
/**
* Representing a raw binary packet with a packet id and payload.
*/
@ -17,5 +19,6 @@ public non-sealed interface BinaryPacket extends PacketLike {
*
* @return the packet payload as a byte array
*/
@NotNull
byte[] payload();
}

View File

@ -5,22 +5,45 @@ import org.jetbrains.annotations.Nullable;
import dev.protocollib.api.reflect.MutableGenericAccessor;
/**
* Represents a mutable packet container that allows modifications.
*/
public interface MutablePacketContainer extends PacketContainer {
/**
* Retrieves the raw packet object.
*
* @return the packet object
* <p>This method provides access to the underlying packet instance,
* allowing direct interaction with its data.</p>
*
* @return the raw packet object
*/
@NotNull
Object packet();
/**
* Provides a {@link MutableGenericAccessor} for modifying packet fields reflectively.
*
* @return the mutable generic accessor for packet data
*/
@NotNull
MutableGenericAccessor accessor();
/**
* Retrieves the packet bundle that this packet is part of, if any.
*
* @return the packet bundle containing this packet, or {@code null} if not part of a bundle
*/
@Nullable
MutablePacketContainer bundle();
/**
* Creates and returns a mutable copy of this packet.
*
* <p>The cloned packet retains mutability, allowing further modifications.</p>
*
* @return a mutable clone of this packet container
*/
@Nullable
MutablePacketContainer clone();
}

View File

@ -6,7 +6,7 @@ import org.jetbrains.annotations.Nullable;
import dev.protocollib.api.reflect.GenericAccessor;
/**
* Representing a container for a packet.
* Represents a container for a packet.
*/
public non-sealed interface PacketContainer extends PacketLike {
@ -18,6 +18,11 @@ public non-sealed interface PacketContainer extends PacketLike {
@NotNull
PacketType packetType();
/**
* Provides a {@link GenericAccessor} for accessing packet fields reflectively.
*
* @return the generic accessor for packet data
*/
@NotNull
GenericAccessor accessor();
@ -32,8 +37,10 @@ public non-sealed interface PacketContainer extends PacketLike {
/**
* Creates and returns a mutable copy of this packet.
*
* @return a clone of this instance
*
* <p>The cloned packet allows modifications, unlike the immutable {@code PacketContainer}.</p>
*
* @return a mutable clone of this packet container
*/
@Nullable
PacketContainer clone();

View File

@ -26,6 +26,8 @@ public interface PacketOperationBuilder {
/**
* Registers a listener to be called once the packet has been sent or received.
* The transmission listener will get invoked on the underlying channel's
* event-loop.
*
* @param listener the listener to be notified upon packet transmission
* @return the same builder for further configuration

View File

@ -2,6 +2,8 @@ package dev.protocollib.api.packet;
import java.util.Optional;
import org.jetbrains.annotations.NotNull;
import dev.protocollib.api.ProtocolDirection;
import dev.protocollib.api.ProtocolPhase;
import net.kyori.adventure.key.Keyed;
@ -20,6 +22,7 @@ public interface PacketType extends Keyed {
*
* @return the {@link ProtocolDirection} of the packet, either clientbound or serverbound
*/
@NotNull
ProtocolDirection protocolDirection();
/**
@ -29,6 +32,7 @@ public interface PacketType extends Keyed {
*
* @return the {@link ProtocolPhase} associated with this packet
*/
@NotNull
ProtocolPhase protocolPhase();
/**
@ -42,6 +46,7 @@ public interface PacketType extends Keyed {
*
* @return an {@link Optional} containing the class of the packet, or empty if not applicable
*/
@NotNull
Optional<Class<?>> packetClass();
/**

View File

@ -5,9 +5,6 @@ import java.util.function.UnaryOperator;
public interface GenericMutator extends GenericAccessor {
MutableGenericAccessor getAccessor(Class<?> type, int ordinal);
MutableGenericAccessor getAccessorOrThrow(Class<?> type, int ordinal);
// ====================================================
// Value Modification
// ====================================================

View File

@ -4,9 +4,6 @@ import java.util.function.Consumer;
public interface MutableGenericAccessor extends GenericAccessor {
MutableGenericAccessor getAccessor(Class<?> type, int ordinal);
MutableGenericAccessor getAccessorOrThrow(Class<?> type, int ordinal);
void update(Consumer<GenericMutator> consumer);
}