mirror of
https://github.com/GeyserMC/Geyser.git
synced 2025-12-19 14:59:27 +00:00
Refactor a bunch of things
This commit is contained in:
@@ -29,13 +29,18 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.api.connection.GeyserConnection;
|
||||
import org.geysermc.geyser.api.event.connection.ConnectionEvent;
|
||||
import org.geysermc.geyser.api.network.NetworkChannel;
|
||||
import org.geysermc.geyser.api.network.message.Message;
|
||||
import org.geysermc.geyser.api.network.message.MessageBuffer;
|
||||
import org.geysermc.geyser.api.network.message.MessageCodec;
|
||||
import org.geysermc.geyser.api.network.message.MessageFactory;
|
||||
import org.geysermc.geyser.api.network.message.MessageHandler;
|
||||
import org.geysermc.geyser.api.network.message.MessagePriority;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Called whenever Geyser is registering network channels.
|
||||
* @since 2.8.2
|
||||
* @since 2.9.1
|
||||
*/
|
||||
public abstract class SessionDefineNetworkChannelsEvent extends ConnectionEvent {
|
||||
|
||||
@@ -44,19 +49,159 @@ public abstract class SessionDefineNetworkChannelsEvent extends ConnectionEvent
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new network channel with a message factory.
|
||||
* Defines the registration of a new network channel with a message factory.
|
||||
*
|
||||
* @param channel the channel to register
|
||||
* @param messageFactory the factory to create messages from the buffer
|
||||
* @param <M> the message type created by the factory
|
||||
* @return a registration builder to configure handlers
|
||||
*/
|
||||
public abstract void register(@NonNull NetworkChannel channel, @NonNull MessageFactory<MessageBuffer> messageFactory);
|
||||
public abstract <M extends Message<MessageBuffer>> Builder.@NonNull Initial<M> define(@NonNull NetworkChannel channel, @NonNull MessageFactory<MessageBuffer, M> messageFactory);
|
||||
|
||||
/**
|
||||
* Registers a new network channel with a message factory.
|
||||
* Defines the registration of a new network channel with a codec and message factory.
|
||||
*
|
||||
* @param channel the channel to register
|
||||
* @param codec the codec to use to encode/decode the buffer
|
||||
* @param messageFactory the factory to create messages from the buffer
|
||||
* @param <T> the buffer type
|
||||
* @param <M> the message type created by the factory
|
||||
* @return a registration builder to configure handlers
|
||||
*/
|
||||
public abstract <T extends MessageBuffer> void register(@NonNull NetworkChannel channel, @NonNull MessageCodec<T> codec, @NonNull MessageFactory<T> messageFactory);
|
||||
public abstract <T extends MessageBuffer, M extends Message<T>> Builder.@NonNull Initial<M> define(@NonNull NetworkChannel channel, @NonNull MessageCodec<T> codec, @NonNull MessageFactory<T, M> messageFactory);
|
||||
|
||||
/**
|
||||
* Registration builder for attaching handlers to a channel.
|
||||
*
|
||||
* @param <M> the message type
|
||||
*/
|
||||
public interface Builder<M extends Message<? extends MessageBuffer>> {
|
||||
|
||||
/**
|
||||
* Configures the pipeline for this handler.
|
||||
*
|
||||
* @param pipeline the pipeline consumer
|
||||
* @return the builder instance
|
||||
*/
|
||||
@NonNull
|
||||
Builder<M> pipeline(@NonNull Consumer<Pipeline> pipeline);
|
||||
|
||||
/**
|
||||
* Finalizes the registration.
|
||||
*
|
||||
* @return the completed registration
|
||||
*/
|
||||
@NonNull
|
||||
Registration<M> register();
|
||||
|
||||
interface Initial<M extends Message<? extends MessageBuffer>> extends Sided<M>, Bidirectional<M> {
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
@NonNull
|
||||
Initial<M> pipeline(@NonNull Consumer<Pipeline> pipeline);
|
||||
}
|
||||
|
||||
interface Sided<M extends Message<? extends MessageBuffer>> extends Builder<M> {
|
||||
|
||||
/**
|
||||
* Register a clientbound handler.
|
||||
*/
|
||||
@NonNull
|
||||
Sided<M> clientbound(MessageHandler.@NonNull Sided<M> handler);
|
||||
|
||||
/**
|
||||
* Register a clientbound handler with a priority.
|
||||
*/
|
||||
@NonNull
|
||||
Sided<M> clientbound(@NonNull MessagePriority priority, MessageHandler.@NonNull Sided<M> handler);
|
||||
|
||||
/**
|
||||
* Register a serverbound handler.
|
||||
*/
|
||||
@NonNull
|
||||
Sided<M> serverbound(MessageHandler.@NonNull Sided<M> handler);
|
||||
|
||||
/**
|
||||
* Register a serverbound handler with a priority.
|
||||
*/
|
||||
@NonNull
|
||||
Sided<M> serverbound(@NonNull MessagePriority priority, MessageHandler.@NonNull Sided<M> handler);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
@NonNull
|
||||
Sided<M> pipeline(@NonNull Consumer<Pipeline> pipeline);
|
||||
}
|
||||
|
||||
interface Bidirectional<M extends Message<? extends MessageBuffer>> extends Builder<M> {
|
||||
|
||||
/**
|
||||
* Register a bidirectional handler receiving the message and direction.
|
||||
*/
|
||||
@NonNull
|
||||
Bidirectional<M> bidirectional(@NonNull MessageHandler<M> handler);
|
||||
|
||||
/**
|
||||
* Register a bidirectional handler with a priority.
|
||||
*/
|
||||
@NonNull
|
||||
Bidirectional<M> bidirectional(@NonNull MessagePriority priority, @NonNull MessageHandler<M> handler);
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
@NonNull
|
||||
Bidirectional<M> pipeline(@NonNull Consumer<Pipeline> pipeline);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pipeline configuration for ordering handlers.
|
||||
*/
|
||||
interface Pipeline {
|
||||
|
||||
/**
|
||||
* Tags this handler in the pipeline.
|
||||
*
|
||||
* @param tag the tag to apply
|
||||
* @return the pipeline instance
|
||||
*/
|
||||
@NonNull
|
||||
Pipeline tag(@NonNull String tag);
|
||||
|
||||
/**
|
||||
* Places this handler before the handler with the given tag.
|
||||
* <p>
|
||||
* The tag used here must be previously defined in a separate handler using {@link #tag(String)}.
|
||||
* However, it should be noted that if the specified tag does not exist at the time of registration,
|
||||
* the handler will be added to the end of the pipeline without throwing an error.
|
||||
*
|
||||
* @param tag the tag to place before
|
||||
* @return the pipeline instance
|
||||
*/
|
||||
@NonNull
|
||||
Pipeline before(@NonNull String tag);
|
||||
|
||||
/**
|
||||
* Places this handler after the handler with the given tag.
|
||||
* <p>
|
||||
* The tag used here must be previously defined in a separate handler using {@link #tag(String)}.
|
||||
* However, it should be noted that if the specified tag does not exist at the time of registration,
|
||||
* the handler will be added to the end of the pipeline without throwing an error.
|
||||
*
|
||||
* @param tag the tag to place after
|
||||
* @return the pipeline instance
|
||||
*/
|
||||
@NonNull
|
||||
Pipeline after(@NonNull String tag);
|
||||
}
|
||||
}
|
||||
|
||||
public interface Registration<M extends Message<? extends MessageBuffer>> {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2025 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.api.event.java;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.event.Cancellable;
|
||||
import org.geysermc.geyser.api.connection.GeyserConnection;
|
||||
import org.geysermc.geyser.api.event.connection.ConnectionEvent;
|
||||
import org.geysermc.geyser.api.network.MessageDirection;
|
||||
import org.geysermc.geyser.api.network.NetworkChannel;
|
||||
import org.geysermc.geyser.api.network.message.Message;
|
||||
|
||||
/**
|
||||
* Called when Geyser receives a network message from the server.
|
||||
* @since 2.8.2
|
||||
*/
|
||||
public final class ServerReceiveNetworkMessageEvent extends ConnectionEvent implements Cancellable {
|
||||
private final NetworkChannel channel;
|
||||
private final Message<?> message;
|
||||
private final MessageDirection direction;
|
||||
private boolean cancelled = false;
|
||||
|
||||
public ServerReceiveNetworkMessageEvent(@NonNull GeyserConnection connection, @NonNull NetworkChannel channel, @NonNull Message<?> message, @NonNull MessageDirection direction) {
|
||||
super(connection);
|
||||
|
||||
this.channel = channel;
|
||||
this.message = message;
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the channel that received the message.
|
||||
* <p>
|
||||
* See {@link NetworkChannel} for more information.
|
||||
*
|
||||
* @return the channel that received the message
|
||||
*/
|
||||
@NonNull
|
||||
public NetworkChannel channel() {
|
||||
return this.channel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the message that was received.
|
||||
*
|
||||
* @return the received message
|
||||
*/
|
||||
@NonNull
|
||||
public Message<?> message() {
|
||||
return this.message;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the direction of the message.
|
||||
*
|
||||
* @return the direction of the message
|
||||
*/
|
||||
@NonNull
|
||||
public MessageDirection direction() {
|
||||
return this.direction;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return this.cancelled;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@Override
|
||||
public void setCancelled(boolean cancelled) {
|
||||
this.cancelled = cancelled;
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,7 @@ package org.geysermc.geyser.api.network;
|
||||
|
||||
/**
|
||||
* Represents the direction of a message.
|
||||
* @since 2.8.2
|
||||
* @since 2.9.1
|
||||
*/
|
||||
public enum MessageDirection {
|
||||
/**
|
||||
|
||||
@@ -27,6 +27,7 @@ package org.geysermc.geyser.api.network;
|
||||
|
||||
import org.checkerframework.checker.index.qual.NonNegative;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.api.GeyserApi;
|
||||
import org.geysermc.geyser.api.extension.Extension;
|
||||
import org.geysermc.geyser.api.util.Identifier;
|
||||
|
||||
@@ -67,11 +68,11 @@ import org.geysermc.geyser.api.util.Identifier;
|
||||
*
|
||||
* <p>
|
||||
* Packet channels can also be registered against packet objects from
|
||||
* exterbak protocol libraries, such as the ones provided in Geyser. For
|
||||
* external protocol libraries, such as the ones provided in Geyser. For
|
||||
* an example on how to do this, please see the
|
||||
* <a href="https://geysermc.org/wiki/geyser/networking-api">Networking API documentation</a>.
|
||||
*
|
||||
* @since 2.8.2
|
||||
* @since 2.9.1
|
||||
*/
|
||||
public interface NetworkChannel {
|
||||
|
||||
@@ -91,58 +92,61 @@ public interface NetworkChannel {
|
||||
boolean isPacket();
|
||||
|
||||
/**
|
||||
* Creates a new {@link NetworkChannel} instance.
|
||||
* Creates a new external {@link NetworkChannel} instance.
|
||||
* <p>
|
||||
* Extensions should use this method to register
|
||||
* their own channels for more robust identification.
|
||||
*
|
||||
* @param extension the extension that registered this channel
|
||||
* @param channel the name of the channel
|
||||
* @return a new {@link NetworkChannel} instance
|
||||
* @param messageType the type of the message sent over this channel
|
||||
* @return a new external {@link NetworkChannel} instance
|
||||
*/
|
||||
@NonNull
|
||||
static NetworkChannel of(@NonNull Extension extension, @NonNull String channel) {
|
||||
return new ExtensionNetworkChannel(extension, channel);
|
||||
static NetworkChannel of(@NonNull Extension extension, @NonNull String channel, @NonNull Class<?> messageType) {
|
||||
return GeyserApi.api().provider(NetworkChannel.class, extension, channel, messageType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link NetworkChannel} instance.
|
||||
* Creates a new external {@link NetworkChannel} instance.
|
||||
* <p>
|
||||
* This method is used for external channels provided
|
||||
* by third parties, such as plugins or mods.
|
||||
*
|
||||
* @param id the channel id
|
||||
* @param channel the name of the channel
|
||||
* @return a new {@link NetworkChannel} instance
|
||||
* @param messageType the type of the message sent over this channel
|
||||
* @return a new external {@link NetworkChannel} instance
|
||||
*/
|
||||
@NonNull
|
||||
static NetworkChannel of(@NonNull String id, @NonNull String channel) {
|
||||
return of(Identifier.of(id, channel));
|
||||
static NetworkChannel of(@NonNull String id, @NonNull String channel, @NonNull Class<?> messageType) {
|
||||
return of(Identifier.of(id, channel), messageType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link NetworkChannel} instance.
|
||||
* Creates a new external {@link NetworkChannel} instance.
|
||||
* <p>
|
||||
* This method is used for external channels provided
|
||||
* by third parties, such as plugins or mods.
|
||||
*
|
||||
* @param identifier the {@link Identifier} of the channel
|
||||
* @return a new {@link NetworkChannel} instance
|
||||
* @param messageType the type of the message sent over this channel
|
||||
* @return a new external {@link NetworkChannel} instance
|
||||
*/
|
||||
@NonNull
|
||||
static NetworkChannel of(@NonNull Identifier identifier) {
|
||||
return new ExternalNetworkChannel(identifier);
|
||||
static NetworkChannel of(@NonNull Identifier identifier, @NonNull Class<?> messageType) {
|
||||
return GeyserApi.api().provider(NetworkChannel.class, identifier, messageType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link PacketChannel} instance for a packet channel.
|
||||
* Creates a new packet {@link NetworkChannel} instance for a packet channel.
|
||||
*
|
||||
* @param key the packet key
|
||||
* @param packetId the packet ID
|
||||
* @param packetType the type of the packet
|
||||
* @return a new {@link PacketChannel} instance for a packet channel
|
||||
* @return a new packet {@link NetworkChannel} instance for a packet channel
|
||||
*/
|
||||
static NetworkChannel packet(@NonNull String key, @NonNegative int packetId, @NonNull Class<?> packetType) {
|
||||
return new PacketChannel(key, packetId, packetType);
|
||||
return GeyserApi.api().provider(NetworkChannel.class, key, packetId, packetType);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,7 +36,7 @@ import java.util.Set;
|
||||
* Represents the network manager responsible for handling network operations
|
||||
* for a {@link GeyserConnection}.
|
||||
*
|
||||
* @since 2.8.2
|
||||
* @since 2.9.1
|
||||
*/
|
||||
public interface NetworkManager {
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ import java.util.Optional;
|
||||
* Represents a data type that can be sent or received over the network.
|
||||
*
|
||||
* @param <T> the type
|
||||
* @since 2.8.2
|
||||
* @since 2.9.1
|
||||
*/
|
||||
public final class DataType<T> {
|
||||
/**
|
||||
|
||||
@@ -33,7 +33,7 @@ import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* Represents a message that can be sent over the network.
|
||||
* @since 2.8.2
|
||||
* @since 2.9.1
|
||||
*/
|
||||
public interface Message<T extends MessageBuffer> {
|
||||
|
||||
@@ -69,8 +69,10 @@ public interface Message<T extends MessageBuffer> {
|
||||
* @param packet the packet object to create the message from
|
||||
* @return a new packet message
|
||||
*/
|
||||
static <T extends MessageBuffer> PacketWrapped<T> of(@NonNull Object packet) {
|
||||
return GeyserApi.api().provider(PacketWrapped.class, packet);
|
||||
@SuppressWarnings("unchecked")
|
||||
@NonNull
|
||||
static <T extends MessageBuffer, P> PacketWrapped<T, P> of(@NonNull Object packet) {
|
||||
return (PacketWrapped<T, P>) GeyserApi.api().provider(PacketWrapped.class, packet);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -80,7 +82,7 @@ public interface Message<T extends MessageBuffer> {
|
||||
* @return a new packet message
|
||||
*/
|
||||
@NonNull
|
||||
static <T extends MessageBuffer> MessageFactory<T> of(@NonNull Supplier<Object> packetSupplier) {
|
||||
static <T extends MessageBuffer, P> MessageFactory<T, PacketWrapped<T, P>> of(@NonNull Supplier<P> packetSupplier) {
|
||||
return buffer -> of(packetSupplier.get());
|
||||
}
|
||||
|
||||
@@ -92,7 +94,7 @@ public interface Message<T extends MessageBuffer> {
|
||||
* @return a new packet message factory
|
||||
*/
|
||||
@NonNull
|
||||
static <T extends MessageBuffer, V> MessageFactory<T> of(@NonNull Function<T, V> substitutor, @NonNull Function<V, Object> packetSupplier) {
|
||||
static <T extends MessageBuffer, V, P> MessageFactory<T, PacketWrapped<T, P>> of(@NonNull Function<T, V> substitutor, @NonNull Function<V, P> packetSupplier) {
|
||||
return buffer -> of(packetSupplier.apply(substitutor.apply(buffer)));
|
||||
}
|
||||
}
|
||||
@@ -102,7 +104,7 @@ public interface Message<T extends MessageBuffer> {
|
||||
*
|
||||
* @param <T> the type of message buffer
|
||||
*/
|
||||
interface PacketWrapped<T extends MessageBuffer> extends PacketBase<T> {
|
||||
interface PacketWrapped<T extends MessageBuffer, P> extends PacketBase<T> {
|
||||
|
||||
/**
|
||||
* Gets the packet associated with this message.
|
||||
@@ -110,6 +112,6 @@ public interface Message<T extends MessageBuffer> {
|
||||
* @return the packet
|
||||
*/
|
||||
@NonNull
|
||||
Object packet();
|
||||
P packet();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
/**
|
||||
* A buffer for messages that can be sent over the network.
|
||||
* @since 2.8.2
|
||||
* @since 2.9.1
|
||||
*/
|
||||
public interface MessageBuffer {
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
* A codec for encoding and decoding messages.
|
||||
*
|
||||
* @param <T> the type of {@link MessageBuffer} used for encoding and decoding
|
||||
* @since 2.8.2
|
||||
* @since 2.9.1
|
||||
*/
|
||||
public interface MessageCodec<T extends MessageBuffer> {
|
||||
|
||||
|
||||
@@ -31,10 +31,11 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
* A factory interface for creating messages from a given message buffer.
|
||||
*
|
||||
* @param <T> the type of the message buffer
|
||||
* @since 2.8.2
|
||||
* @param <M> the type of the message
|
||||
* @since 2.9.1
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface MessageFactory<T extends MessageBuffer> {
|
||||
public interface MessageFactory<T extends MessageBuffer, M extends Message<T>> {
|
||||
|
||||
/**
|
||||
* Creates a new message from the provided buffer.
|
||||
@@ -43,5 +44,5 @@ public interface MessageFactory<T extends MessageBuffer> {
|
||||
* @return a new message created from the buffer
|
||||
*/
|
||||
@NonNull
|
||||
Message<T> create(@NonNull T buffer);
|
||||
M create(@NonNull T buffer);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* Copyright (c) 2025 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.api.network.message;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.api.network.MessageDirection;
|
||||
|
||||
/**
|
||||
* Represents a handler for processing messages.
|
||||
*
|
||||
* @param <T> the type of message to handle
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface MessageHandler<T extends Message<? extends MessageBuffer>> {
|
||||
|
||||
/**
|
||||
* Handles the given message in the specified direction.
|
||||
*
|
||||
* @param message the message to handle
|
||||
* @param direction the direction of the message
|
||||
* @return the state after handling the message
|
||||
*/
|
||||
@NonNull
|
||||
State handle(@NonNull T message, @NonNull MessageDirection direction);
|
||||
|
||||
/**
|
||||
* A message handler that belongs to a specific side (clientbound or serverbound).
|
||||
*
|
||||
* @param <T> the type of message to handle
|
||||
*/
|
||||
interface Sided<T extends Message<? extends MessageBuffer>> {
|
||||
|
||||
/**
|
||||
* Handles the given message in the specified direction.
|
||||
*
|
||||
* @param message the message to handle
|
||||
* @return the state after handling the message
|
||||
*/
|
||||
@NonNull
|
||||
State handle(@NonNull T message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents the state after handling a message.
|
||||
*/
|
||||
enum State {
|
||||
/**
|
||||
* The message was handled and should not be processed further.
|
||||
*/
|
||||
HANDLED,
|
||||
/**
|
||||
* The message was not handled and should be passed through for further processing.
|
||||
*/
|
||||
UNHANDLED,
|
||||
/**
|
||||
* Indicates that the message has been modified but should still be
|
||||
* passed through to the next handler or processing step.
|
||||
*/
|
||||
MODIFIED
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright (c) 2025 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.api.network.message;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
|
||||
/**
|
||||
* Represents the priority of a message when being processed.
|
||||
* @since 2.9.1
|
||||
*/
|
||||
public enum MessagePriority {
|
||||
FIRST(100),
|
||||
EARLY(50),
|
||||
NORMAL(0),
|
||||
LATE(-50),
|
||||
LAST(-100);
|
||||
|
||||
private final int value;
|
||||
|
||||
MessagePriority(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the numeric value associated with this priority. Higher means earlier.
|
||||
*
|
||||
* @return the priority value
|
||||
*/
|
||||
public int value() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a custom priority in the range [-100, 100].
|
||||
*
|
||||
* @param value the priority value
|
||||
* @return the priority
|
||||
* @throws IllegalArgumentException if outside allowed range
|
||||
*/
|
||||
@NonNull
|
||||
public static MessagePriority of(int value) {
|
||||
if (value >= 75) return LAST;
|
||||
if (value >= 25) return LATE;
|
||||
if (value <= -75) return FIRST;
|
||||
if (value <= -25) return EARLY;
|
||||
return NORMAL;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2025 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.network;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.api.network.NetworkChannel;
|
||||
|
||||
public abstract class BaseNetworkChannel implements NetworkChannel {
|
||||
private final Class<?> messageType;
|
||||
|
||||
public BaseNetworkChannel(Class<?> messageType) {
|
||||
this.messageType = messageType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of message this channel handles.
|
||||
*
|
||||
* @return the message type
|
||||
*/
|
||||
@NonNull
|
||||
public Class<?> messageType() {
|
||||
return messageType;
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.api.network;
|
||||
package org.geysermc.geyser.network;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.api.extension.Extension;
|
||||
@@ -33,13 +33,14 @@ import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Represents a network channel associated with an extension.
|
||||
* @since 2.8.2
|
||||
*/
|
||||
public class ExtensionNetworkChannel implements NetworkChannel {
|
||||
public class ExtensionNetworkChannel extends BaseNetworkChannel {
|
||||
private final Extension extension;
|
||||
private final String channel;
|
||||
|
||||
protected ExtensionNetworkChannel(@NonNull Extension extension, @NonNull String channel) {
|
||||
public ExtensionNetworkChannel(@NonNull Extension extension, @NonNull String channel, @NonNull Class<?> messageType) {
|
||||
super(messageType);
|
||||
|
||||
this.extension = extension;
|
||||
this.channel = channel;
|
||||
}
|
||||
@@ -63,14 +64,14 @@ public class ExtensionNetworkChannel implements NetworkChannel {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || !NetworkChannel.class.isAssignableFrom(o.getClass())) return false;
|
||||
NetworkChannel that = (NetworkChannel) o;
|
||||
return Objects.equals(this.identifier(), that.identifier());
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ExtensionNetworkChannel that = (ExtensionNetworkChannel) o;
|
||||
return Objects.equals(this.extension, that.extension) && Objects.equals(this.channel, that.channel) && Objects.equals(this.messageType(), that.messageType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.identifier());
|
||||
return Objects.hash(this.identifier(), this.messageType());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -78,6 +79,7 @@ public class ExtensionNetworkChannel implements NetworkChannel {
|
||||
return "ExtensionNetworkChannel{" +
|
||||
"extension=" + this.extension.description().id() +
|
||||
", channel='" + this.channel + '\'' +
|
||||
", messageType=" + this.messageType() +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.api.network;
|
||||
package org.geysermc.geyser.network;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.api.util.Identifier;
|
||||
@@ -34,12 +34,13 @@ import java.util.Objects;
|
||||
* Represents a network channel not associated with any specific extension.
|
||||
* <p>
|
||||
* This can be used for external communication channels, like mods or plugins.
|
||||
* @since 2.8.2
|
||||
*/
|
||||
public class ExternalNetworkChannel implements NetworkChannel {
|
||||
public class ExternalNetworkChannel extends BaseNetworkChannel {
|
||||
private final Identifier identifier;
|
||||
|
||||
protected ExternalNetworkChannel(@NonNull Identifier identifier) {
|
||||
public ExternalNetworkChannel(@NonNull Identifier identifier, @NonNull Class<?> messageType) {
|
||||
super(messageType);
|
||||
|
||||
this.identifier = identifier;
|
||||
}
|
||||
|
||||
@@ -62,20 +63,21 @@ public class ExternalNetworkChannel implements NetworkChannel {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == null || !NetworkChannel.class.isAssignableFrom(o.getClass())) return false;
|
||||
NetworkChannel that = (NetworkChannel) o;
|
||||
return Objects.equals(this.identifier(), that.identifier());
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
ExternalNetworkChannel that = (ExternalNetworkChannel) o;
|
||||
return Objects.equals(this.identifier, that.identifier) && Objects.equals(this.messageType(), that.messageType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(this.identifier());
|
||||
return Objects.hash(this.identifier(), this.messageType());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "ExternalNetworkChannel{" +
|
||||
"identifier='" + this.identifier + '\'' +
|
||||
", messageType=" + this.messageType() +
|
||||
'}';
|
||||
}
|
||||
}
|
||||
@@ -31,30 +31,33 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
|
||||
import net.kyori.adventure.key.Key;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodec;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.BedrockCodecHelper;
|
||||
import org.cloudburstmc.protocol.bedrock.codec.BedrockPacketDefinition;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket;
|
||||
import org.geysermc.geyser.api.GeyserApi;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.api.event.bedrock.SessionDefineNetworkChannelsEvent;
|
||||
import org.geysermc.geyser.api.event.java.ServerReceiveNetworkMessageEvent;
|
||||
import org.geysermc.geyser.api.network.MessageDirection;
|
||||
import org.geysermc.geyser.api.network.NetworkChannel;
|
||||
import org.geysermc.geyser.api.network.NetworkManager;
|
||||
import org.geysermc.geyser.api.network.PacketChannel;
|
||||
import org.geysermc.geyser.api.network.message.Message;
|
||||
import org.geysermc.geyser.api.network.message.MessageBuffer;
|
||||
import org.geysermc.geyser.api.network.message.MessageCodec;
|
||||
import org.geysermc.geyser.api.network.message.MessageFactory;
|
||||
import org.geysermc.geyser.api.network.message.MessageHandler;
|
||||
import org.geysermc.geyser.network.message.BedrockPacketMessage;
|
||||
import org.geysermc.geyser.network.message.ByteBufCodec;
|
||||
import org.geysermc.geyser.network.message.ByteBufMessageBuffer;
|
||||
import org.geysermc.geyser.network.message.JavaPacketMessage;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.mcprotocollib.protocol.packet.common.serverbound.ServerboundCustomPayloadPacket;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.VisibleForTesting;
|
||||
|
||||
import java.util.HashMap;
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
@@ -62,7 +65,7 @@ import java.util.function.Function;
|
||||
public class GeyserNetworkManager implements NetworkManager {
|
||||
private final GeyserSession session;
|
||||
|
||||
private final Map<NetworkChannel, MessageDefinition<?>> definitions = new HashMap<>();
|
||||
private final Map<NetworkChannel, List<MessageDefinition<?, ?>>> definitions = new LinkedHashMap<>();
|
||||
private final Int2ObjectMap<PacketChannel> packetChannels = new Int2ObjectOpenHashMap<>();
|
||||
|
||||
public GeyserNetworkManager(GeyserSession session) {
|
||||
@@ -71,31 +74,77 @@ public class GeyserNetworkManager implements NetworkManager {
|
||||
SessionDefineNetworkChannelsEvent event = new SessionDefineNetworkChannelsEvent(session) {
|
||||
|
||||
@Override
|
||||
public void register(@NonNull NetworkChannel channel, @NonNull MessageFactory<MessageBuffer> messageFactory) {
|
||||
GeyserNetworkManager.this.registerMessage(channel, new MessageDefinition<>(ByteBufCodec.INSTANCE, messageFactory));
|
||||
public <M extends Message<MessageBuffer>> Builder.@NonNull Initial<M> define(@NonNull NetworkChannel channel, @NonNull MessageFactory<MessageBuffer, M> messageFactory) {
|
||||
return new NetworkDefinitionBuilder<>(registration -> onRegister(channel, ByteBufCodec.INSTANCE, messageFactory, registration));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends MessageBuffer> void register(@NonNull NetworkChannel channel, @NonNull MessageCodec<T> codec, @NonNull MessageFactory<T> messageFactory) {
|
||||
GeyserNetworkManager.this.registerMessage(channel, new MessageDefinition<>(codec, messageFactory));
|
||||
public <T extends MessageBuffer, M extends Message<T>> Builder.@NonNull Initial<M> define(@NonNull NetworkChannel channel, @NonNull MessageCodec<T> codec, @NonNull MessageFactory<T, M> messageFactory) {
|
||||
return new NetworkDefinitionBuilder<>(registration -> onRegister(channel, codec, messageFactory, registration));
|
||||
}
|
||||
};
|
||||
|
||||
GeyserApi.api().eventBus().fire(event);
|
||||
GeyserImpl.getInstance().getEventBus().fire(event);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Set<NetworkChannel> registeredChannels() {
|
||||
return Set.copyOf(this.definitions.keySet());
|
||||
@VisibleForTesting
|
||||
<M extends Message<MessageBuffer>> void onRegister(@NonNull NetworkChannel channel, @NonNull MessageFactory<MessageBuffer, M> messageFactory, NetworkDefinitionBuilder.@NonNull RegistrationImpl<M> registration) {
|
||||
this.onRegister(channel, ByteBufCodec.INSTANCE, messageFactory, registration);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T extends MessageBuffer, M extends Message<T>> void onRegister(@NonNull NetworkChannel channel, @NonNull MessageCodec<? extends T> codec,
|
||||
@NonNull MessageFactory<T, M> messageFactory, NetworkDefinitionBuilder.@NonNull RegistrationImpl<M> registration) {
|
||||
MessageHandler<M> handler;
|
||||
int priority;
|
||||
|
||||
NetworkDefinitionBuilder.HandlerEntry<M> bidirectional = registration.handler();
|
||||
NetworkDefinitionBuilder.SidedHandlerEntry<M> clientbound = registration.clientbound();
|
||||
NetworkDefinitionBuilder.SidedHandlerEntry<M> serverbound = registration.serverbound();
|
||||
|
||||
if (bidirectional != null) {
|
||||
handler = bidirectional.handler();
|
||||
priority = bidirectional.priority() != null ? bidirectional.priority().value() : 0;
|
||||
} else {
|
||||
handler = null;
|
||||
|
||||
int cbPriority = clientbound != null && clientbound.priority() != null ? clientbound.priority().value() : Integer.MIN_VALUE;
|
||||
int sbPriority = serverbound != null && serverbound.priority() != null ? serverbound.priority().value() : Integer.MIN_VALUE;
|
||||
priority = Math.max(cbPriority, sbPriority);
|
||||
|
||||
if (priority == Integer.MIN_VALUE) {
|
||||
priority = 0;
|
||||
}
|
||||
}
|
||||
|
||||
MessageDefinition<T, M> definition = new MessageDefinition<>((MessageCodec<T>) codec,
|
||||
messageFactory,
|
||||
handler,
|
||||
clientbound != null ? clientbound.handler() : null,
|
||||
serverbound != null ? serverbound.handler() : null,
|
||||
priority,
|
||||
clientbound != null && clientbound.priority() != null ? clientbound.priority().value() : null,
|
||||
serverbound != null && serverbound.priority() != null ? serverbound.priority().value() : null,
|
||||
registration.tag(),
|
||||
registration.beforeTag(),
|
||||
registration.afterTag()
|
||||
);
|
||||
|
||||
this.registerMessage(channel, definition);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nonnull
|
||||
public Set<NetworkChannel> registeredChannels() {
|
||||
return Set.copyOf(this.definitions.keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends MessageBuffer> void send(@NonNull NetworkChannel channel, @NonNull Message<T> message, @NonNull MessageDirection direction) {
|
||||
if (channel.isPacket() && message instanceof Message.PacketBase<T> packetBase) {
|
||||
if (packetBase instanceof BedrockPacketMessage packetMessage) {
|
||||
if (packetBase instanceof BedrockPacketMessage<?> packetMessage) {
|
||||
this.session.sendUpstreamPacket(packetMessage.packet());
|
||||
} else if (packetBase instanceof JavaPacketMessage packetMessage) {
|
||||
} else if (packetBase instanceof JavaPacketMessage<?> packetMessage) {
|
||||
this.session.sendDownstreamPacket(packetMessage.packet());
|
||||
} else if (packetBase instanceof Message.Packet packet) {
|
||||
PacketChannel packetChannel = (PacketChannel) channel;
|
||||
@@ -123,10 +172,7 @@ public class GeyserNetworkManager implements NetworkManager {
|
||||
return;
|
||||
}
|
||||
|
||||
MessageDefinition<T> definition = (MessageDefinition<T>) this.definitions.get(channel);
|
||||
if (definition == null) {
|
||||
throw new IllegalArgumentException("No message definition registered for channel: " + channel);
|
||||
}
|
||||
MessageDefinition<T, Message<T>> definition = this.findMessageDefinition(channel, message);
|
||||
|
||||
T buffer = definition.codec.createBuffer();
|
||||
message.encode(buffer);
|
||||
@@ -139,29 +185,40 @@ public class GeyserNetworkManager implements NetworkManager {
|
||||
this.session.sendDownstreamPacket(packet);
|
||||
}
|
||||
|
||||
public <T extends MessageBuffer> Message<T> createMessage(@NonNull NetworkChannel channel, byte @NotNull[] data) {
|
||||
return this.createMessage0(channel, definition -> definition.createBuffer(data));
|
||||
@NonNull
|
||||
public <T extends MessageBuffer> List<Message<T>> createMessages(@NonNull NetworkChannel channel, byte @NonNull[] data) {
|
||||
return this.createMessages0(channel, definition -> definition.createBuffer(data));
|
||||
}
|
||||
|
||||
public <T extends MessageBuffer> Message<T> createMessage(@NonNull NetworkChannel channel, @NonNull T buffer) {
|
||||
return this.createMessage0(channel, def -> buffer);
|
||||
@NonNull
|
||||
public <T extends MessageBuffer> List<Message<T>> createMessages(@NonNull NetworkChannel channel, @NonNull T buffer) {
|
||||
return this.createMessages0(channel, def -> buffer);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <T extends MessageBuffer> Message<T> createMessage0(@NonNull NetworkChannel channel, @NonNull Function<MessageDefinition<T>, T> creator) {
|
||||
MessageDefinition<T> definition = (MessageDefinition<T>) this.definitions.get(channel);
|
||||
if (definition == null) {
|
||||
@NonNull
|
||||
private <T extends MessageBuffer, M extends Message<T>> List<M> createMessages0(@NonNull NetworkChannel channel, @NonNull Function<MessageDefinition<T, M>, T> creator) {
|
||||
List<MessageDefinition<?, ?>> definitions = this.definitions.get(channel);
|
||||
if (definitions == null || definitions.isEmpty()) {
|
||||
throw new IllegalArgumentException("No message definition registered for channel: " + channel);
|
||||
}
|
||||
|
||||
List<M> messages = new ArrayList<>();
|
||||
for (MessageDefinition<?, ?> def : definitions) {
|
||||
MessageDefinition<T, M> definition = (MessageDefinition<T, M>) def;
|
||||
T buffer = creator.apply(definition);
|
||||
Message<T> message = definition.createMessage(buffer);
|
||||
if (message instanceof BedrockPacketMessage packetMessage) {
|
||||
M message = definition.createMessage(buffer);
|
||||
if (message instanceof BedrockPacketMessage<?> packetMessage) {
|
||||
packetMessage.postProcess(this.session, (ByteBufMessageBuffer) buffer);
|
||||
}
|
||||
return message;
|
||||
|
||||
messages.add(message);
|
||||
}
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public PacketChannel getPacketChannel(int packetId) {
|
||||
return this.packetChannels.get(packetId);
|
||||
}
|
||||
@@ -179,42 +236,184 @@ public class GeyserNetworkManager implements NetworkManager {
|
||||
return true;
|
||||
}
|
||||
|
||||
Message<?> message;
|
||||
if (channel.packetType().isInstance(packet)) {
|
||||
message = new BedrockPacketMessage(packet);
|
||||
List<Message<ByteBufMessageBuffer>> messages;
|
||||
if (channel.messageType().isInstance(packet)) {
|
||||
messages = List.of(new BedrockPacketMessage<>(packet));
|
||||
} else {
|
||||
ByteBuf buffer = Unpooled.buffer();
|
||||
definition.getSerializer().serialize(buffer, this.session.getUpstream().getCodecHelper(), packet);
|
||||
message = this.createMessage(channel, new ByteBufMessageBuffer(ByteBufCodec.INSTANCE_LE, buffer));
|
||||
messages = this.createMessages(channel, new ByteBufMessageBuffer(ByteBufCodec.INSTANCE_LE, buffer));
|
||||
}
|
||||
|
||||
ServerReceiveNetworkMessageEvent event = new ServerReceiveNetworkMessageEvent(this.session, channel, message, direction);
|
||||
this.session.getGeyser().eventBus().fire(event);
|
||||
|
||||
// If the event is canceled, we do not want to process the packet further
|
||||
return !event.isCancelled();
|
||||
return this.handleMessages(channel, messages, direction);
|
||||
}
|
||||
|
||||
private <T extends MessageBuffer> void registerMessage(@NonNull NetworkChannel channel, @NonNull MessageDefinition<T> codec) {
|
||||
if (this.definitions.containsKey(channel)) {
|
||||
throw new IllegalArgumentException("Channel is already registered: " + channel);
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T extends MessageBuffer> boolean handleMessages(@NonNull NetworkChannel channel, @NonNull List<Message<T>> messages, @NonNull MessageDirection direction) {
|
||||
List<MessageDefinition<?, ?>> rawList = this.definitions.get(channel);
|
||||
if (rawList == null || rawList.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
this.definitions.put(channel, codec);
|
||||
// Build a direction-aware ordered list while preserving pipeline tag anchors
|
||||
List<MessageDefinition<?, ?>> ordered = new ArrayList<>();
|
||||
List<MessageDefinition<?, ?>> unpinnedBlock = new ArrayList<>();
|
||||
for (MessageDefinition<?, ?> def : rawList) {
|
||||
boolean pinned = def.tag() != null || def.beforeTag() != null || def.afterTag() != null;
|
||||
if (pinned) {
|
||||
// flush any accumulated unpinned block sorted by effective priority for this direction
|
||||
if (!unpinnedBlock.isEmpty()) {
|
||||
unpinnedBlock.sort((a, b) -> Integer.compare(
|
||||
b.priority(direction),
|
||||
a.priority(direction)
|
||||
));
|
||||
ordered.addAll(unpinnedBlock);
|
||||
unpinnedBlock.clear();
|
||||
}
|
||||
ordered.add(def);
|
||||
} else {
|
||||
unpinnedBlock.add(def);
|
||||
}
|
||||
}
|
||||
if (!unpinnedBlock.isEmpty()) {
|
||||
unpinnedBlock.sort((a, b) -> Integer.compare(
|
||||
b.priority(direction),
|
||||
a.priority(direction)
|
||||
));
|
||||
ordered.addAll(unpinnedBlock);
|
||||
unpinnedBlock.clear();
|
||||
}
|
||||
|
||||
for (Message<T> message : messages) {
|
||||
for (MessageDefinition<?, ?> def : ordered) {
|
||||
if (!(channel instanceof BaseNetworkChannel base) || !base.messageType().isInstance(message)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
MessageDefinition<T, Message<T>> definition = (MessageDefinition<T, Message<T>>) def;
|
||||
|
||||
MessageHandler.State state;
|
||||
if (definition.handler != null) {
|
||||
state = definition.handler.handle(message, direction);
|
||||
} else if (direction == MessageDirection.CLIENTBOUND && definition.clientboundHandler != null) {
|
||||
state = definition.clientboundHandler.handle(message);
|
||||
} else if (direction == MessageDirection.SERVERBOUND && definition.serverboundHandler != null) {
|
||||
state = definition.serverboundHandler.handle(message);
|
||||
} else {
|
||||
continue; // no suitable handler; try next definition
|
||||
}
|
||||
|
||||
if (state == MessageHandler.State.HANDLED) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@NonNull
|
||||
private <T extends MessageBuffer, M extends Message<T>> MessageDefinition<T, M> findMessageDefinition(@NonNull NetworkChannel channel, @NonNull Message<T> message) {
|
||||
List<MessageDefinition<?, ?>> definitions = this.definitions.get(channel);
|
||||
if (definitions == null || definitions.isEmpty()) {
|
||||
throw new IllegalArgumentException("No message definition registered for channel: " + channel);
|
||||
}
|
||||
|
||||
MessageDefinition<T, Message<T>> definition = null;
|
||||
for (MessageDefinition<?, ?> def : definitions) {
|
||||
if (channel instanceof BaseNetworkChannel baseChannel) {
|
||||
if (baseChannel.messageType().isInstance(message)) {
|
||||
definition = (MessageDefinition<T, Message<T>>) def;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (definition == null) {
|
||||
throw new IllegalArgumentException("No suitable message definition found for channel: " + channel + " and message type: " + message.getClass());
|
||||
}
|
||||
|
||||
return (MessageDefinition<T, M>) definition;
|
||||
}
|
||||
|
||||
private <T extends MessageBuffer, M extends Message<T>> void registerMessage(@NonNull NetworkChannel channel, @NonNull MessageDefinition<T, M> definition) {
|
||||
List<MessageDefinition<?, ?>> list = this.definitions.computeIfAbsent(channel, key -> new ArrayList<>());
|
||||
|
||||
// Determine the insert position based on pipeline tags or priority
|
||||
int insertIndex = -1;
|
||||
if (definition.beforeTag() != null) {
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
MessageDefinition<?, ?> existing = list.get(i);
|
||||
if (definition.beforeTag().equals(existing.tag())) {
|
||||
insertIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (definition.afterTag() != null) {
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
MessageDefinition<?, ?> existing = list.get(i);
|
||||
if (definition.afterTag().equals(existing.tag())) {
|
||||
insertIndex = i + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (insertIndex == -1) {
|
||||
// Fallback: insert by descending priority
|
||||
insertIndex = list.size();
|
||||
for (int i = 0; i < list.size(); i++) {
|
||||
MessageDefinition<?, ?> existing = list.get(i);
|
||||
if (definition.priority() > existing.priority()) {
|
||||
insertIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
list.add(insertIndex, definition);
|
||||
|
||||
if (channel.isPacket() && channel instanceof PacketChannel packetChannel) {
|
||||
int packetId = packetChannel.packetId();
|
||||
this.packetChannels.put(packetId, packetChannel);
|
||||
}
|
||||
}
|
||||
|
||||
public record MessageDefinition<T extends MessageBuffer>(MessageCodec<? extends T> codec, MessageFactory<T> messageFactory) {
|
||||
public record MessageDefinition<T extends MessageBuffer, M extends Message<T>>(
|
||||
@NonNull MessageCodec<T> codec,
|
||||
@NonNull MessageFactory<T, M> messageFactory,
|
||||
@Nullable MessageHandler<M> handler,
|
||||
MessageHandler.Sided<M> clientboundHandler,
|
||||
MessageHandler.Sided<M> serverboundHandler,
|
||||
int priority,
|
||||
@Nullable Integer clientboundPriority,
|
||||
@Nullable Integer serverboundPriority,
|
||||
@Nullable String tag,
|
||||
@Nullable String beforeTag,
|
||||
@Nullable String afterTag
|
||||
) {
|
||||
|
||||
public T createBuffer(byte @NotNull[] data) {
|
||||
@NonNull
|
||||
public T createBuffer(byte @NonNull[] data) {
|
||||
return this.codec.createBuffer(data);
|
||||
}
|
||||
|
||||
public Message<T> createMessage(@NonNull T buffer) {
|
||||
@NonNull
|
||||
public M createMessage(@NonNull T buffer) {
|
||||
return this.messageFactory.create(buffer);
|
||||
}
|
||||
|
||||
public int priority(@NonNull MessageDirection direction) {
|
||||
if (this.handler != null) {
|
||||
return this.priority;
|
||||
}
|
||||
|
||||
if (direction == MessageDirection.CLIENTBOUND) {
|
||||
return this.clientboundPriority != null ? this.clientboundPriority : 0;
|
||||
}
|
||||
|
||||
return this.serverboundPriority != null ? this.serverboundPriority : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright (c) 2025 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.network;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.api.event.bedrock.SessionDefineNetworkChannelsEvent;
|
||||
import org.geysermc.geyser.api.network.message.Message;
|
||||
import org.geysermc.geyser.api.network.message.MessageBuffer;
|
||||
import org.geysermc.geyser.api.network.message.MessageHandler;
|
||||
import org.geysermc.geyser.api.network.message.MessagePriority;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class NetworkDefinitionBuilder<M extends Message<? extends MessageBuffer>> implements SessionDefineNetworkChannelsEvent.Builder.Initial<M> {
|
||||
private HandlerEntry<M> handler;
|
||||
private SidedHandlerEntry<M> clientbound;
|
||||
private SidedHandlerEntry<M> serverbound;
|
||||
|
||||
private String tag;
|
||||
private String beforeTag;
|
||||
private String afterTag;
|
||||
|
||||
private final Consumer<RegistrationImpl<M>> registrationCallback;
|
||||
private boolean registered;
|
||||
|
||||
public NetworkDefinitionBuilder(@NonNull Consumer<RegistrationImpl<M>> registrationCallback) {
|
||||
this.registrationCallback = registrationCallback;
|
||||
}
|
||||
|
||||
public SessionDefineNetworkChannelsEvent.Builder.@NonNull Initial<M> pipeline(@NonNull Consumer<Pipeline> pipeline) {
|
||||
Objects.requireNonNull(pipeline, "pipeline");
|
||||
PipelineImpl impl = new PipelineImpl();
|
||||
pipeline.accept(impl);
|
||||
this.tag = impl.tag;
|
||||
this.beforeTag = impl.beforeTag;
|
||||
this.afterTag = impl.afterTag;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SessionDefineNetworkChannelsEvent.Builder.@NonNull Sided<M> clientbound(MessageHandler.@NonNull Sided<M> handler) {
|
||||
return this.clientbound(MessagePriority.NORMAL, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SessionDefineNetworkChannelsEvent.Builder.@NonNull Sided<M> clientbound(@NonNull MessagePriority priority, MessageHandler.@NonNull Sided<M> handler) {
|
||||
Objects.requireNonNull(priority, "priority");
|
||||
Objects.requireNonNull(handler, "handler");
|
||||
this.clientbound = new SidedHandlerEntry<>(priority, handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SessionDefineNetworkChannelsEvent.Builder.@NonNull Sided<M> serverbound(MessageHandler.@NonNull Sided<M> handler) {
|
||||
return this.serverbound(MessagePriority.NORMAL, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SessionDefineNetworkChannelsEvent.Builder.@NonNull Sided<M> serverbound(@NonNull MessagePriority priority, MessageHandler.@NonNull Sided<M> handler) {
|
||||
Objects.requireNonNull(priority, "priority");
|
||||
Objects.requireNonNull(handler, "handler");
|
||||
this.serverbound = new SidedHandlerEntry<>(priority, handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SessionDefineNetworkChannelsEvent.Builder.@NonNull Bidirectional<M> bidirectional(@NonNull MessageHandler<M> handler) {
|
||||
return this.bidirectional(MessagePriority.NORMAL, handler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SessionDefineNetworkChannelsEvent.Builder.@NonNull Bidirectional<M> bidirectional(@NonNull MessagePriority priority, @NonNull MessageHandler<M> handler) {
|
||||
Objects.requireNonNull(priority, "priority");
|
||||
Objects.requireNonNull(handler, "handler");
|
||||
this.handler = new HandlerEntry<>(priority, handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SessionDefineNetworkChannelsEvent.@NonNull Registration<M> register() {
|
||||
Preconditions.checkState(!this.registered, "This message has already been registered");
|
||||
Preconditions.checkState(this.handler == null || (this.clientbound == null && this.serverbound == null), "Cannot register both bidirectional and sided handlers for the same message");
|
||||
RegistrationImpl<M> registration = new RegistrationImpl<>(
|
||||
this.handler,
|
||||
this.clientbound,
|
||||
this.serverbound,
|
||||
this.tag,
|
||||
this.beforeTag,
|
||||
this.afterTag
|
||||
);
|
||||
|
||||
this.registered = true;
|
||||
this.registrationCallback.accept(registration);
|
||||
return registration;
|
||||
}
|
||||
|
||||
private static final class PipelineImpl implements SessionDefineNetworkChannelsEvent.Builder.Pipeline {
|
||||
private String tag;
|
||||
private String beforeTag;
|
||||
private String afterTag;
|
||||
|
||||
@Override
|
||||
public SessionDefineNetworkChannelsEvent.Builder.@NonNull Pipeline tag(@NonNull String tag) {
|
||||
this.tag = Objects.requireNonNull(tag, "tag");
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SessionDefineNetworkChannelsEvent.Builder.@NonNull Pipeline before(@NonNull String tag) {
|
||||
this.beforeTag = Objects.requireNonNull(tag, "tag");
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SessionDefineNetworkChannelsEvent.Builder.@NonNull Pipeline after(@NonNull String tag) {
|
||||
this.afterTag = Objects.requireNonNull(tag, "tag");
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public record RegistrationImpl<M extends Message<? extends MessageBuffer>>(
|
||||
HandlerEntry<M> handler,
|
||||
SidedHandlerEntry<M> clientbound,
|
||||
SidedHandlerEntry<M> serverbound,
|
||||
String tag,
|
||||
String beforeTag,
|
||||
String afterTag
|
||||
) implements SessionDefineNetworkChannelsEvent.Registration<M> {
|
||||
}
|
||||
|
||||
public record SidedHandlerEntry<T extends Message<? extends MessageBuffer>>(
|
||||
MessagePriority priority,
|
||||
MessageHandler.Sided<T> handler
|
||||
) {
|
||||
}
|
||||
|
||||
public record HandlerEntry<T extends Message<? extends MessageBuffer>>(
|
||||
MessagePriority priority,
|
||||
MessageHandler<T> handler
|
||||
) {
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,7 @@
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.api.network;
|
||||
package org.geysermc.geyser.network;
|
||||
|
||||
import org.checkerframework.checker.index.qual.NonNegative;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
@@ -37,19 +37,16 @@ import java.util.Objects;
|
||||
* This channel is used for listening to communication over
|
||||
* packets between the server and client and can be used to
|
||||
* send or receive packets.
|
||||
* @since 2.8.2
|
||||
*/
|
||||
public class PacketChannel extends ExternalNetworkChannel {
|
||||
private static final String PACKET_CHANNEL_KEY = "packet";
|
||||
|
||||
private final int packetId;
|
||||
private final Class<?> packetType;
|
||||
|
||||
protected PacketChannel(@NonNull String key, @NonNegative int packetId, @NonNull Class<?> packetType) {
|
||||
super(Identifier.of(PACKET_CHANNEL_KEY, key));
|
||||
public PacketChannel(@NonNull String key, @NonNegative int packetId, @NonNull Class<?> packetType) {
|
||||
super(Identifier.of(PACKET_CHANNEL_KEY, key), packetType);
|
||||
|
||||
this.packetId = packetId;
|
||||
this.packetType = packetType;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,16 +59,6 @@ public class PacketChannel extends ExternalNetworkChannel {
|
||||
return this.packetId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the type of the packet associated with this channel.
|
||||
*
|
||||
* @return the class of the packet type
|
||||
*/
|
||||
@NonNull
|
||||
public Class<?> packetType() {
|
||||
return this.packetType;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@@ -33,7 +33,7 @@ import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket;
|
||||
import org.geysermc.geyser.api.network.message.Message;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
|
||||
public record BedrockPacketMessage(@NonNull BedrockPacket packet) implements Message.PacketWrapped<ByteBufMessageBuffer> {
|
||||
public record BedrockPacketMessage<T extends BedrockPacket>(@NonNull T packet) implements Message.PacketWrapped<ByteBufMessageBuffer, T> {
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void postProcess(@NonNull GeyserSession session, @NonNull ByteBufMessageBuffer buffer) {
|
||||
|
||||
@@ -29,7 +29,6 @@ import io.netty.buffer.Unpooled;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.protocol.common.util.VarInts;
|
||||
import org.geysermc.geyser.api.network.message.MessageCodec;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@@ -41,62 +40,62 @@ public class ByteBufCodec implements MessageCodec<ByteBufMessageBuffer> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean readBoolean(@NotNull ByteBufMessageBuffer buffer) {
|
||||
public boolean readBoolean(@NonNull ByteBufMessageBuffer buffer) {
|
||||
return buffer.buffer().readBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte readByte(@NotNull ByteBufMessageBuffer buffer) {
|
||||
public byte readByte(@NonNull ByteBufMessageBuffer buffer) {
|
||||
return buffer.buffer().readByte();
|
||||
}
|
||||
|
||||
@Override
|
||||
public short readShort(@NotNull ByteBufMessageBuffer buffer) {
|
||||
public short readShort(@NonNull ByteBufMessageBuffer buffer) {
|
||||
return buffer.buffer().readShort();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readInt(@NotNull ByteBufMessageBuffer buffer) {
|
||||
public int readInt(@NonNull ByteBufMessageBuffer buffer) {
|
||||
return buffer.buffer().readInt();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float readFloat(@NotNull ByteBufMessageBuffer buffer) {
|
||||
public float readFloat(@NonNull ByteBufMessageBuffer buffer) {
|
||||
return buffer.buffer().readFloat();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double readDouble(@NotNull ByteBufMessageBuffer buffer) {
|
||||
public double readDouble(@NonNull ByteBufMessageBuffer buffer) {
|
||||
return buffer.buffer().readDouble();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readLong(@NotNull ByteBufMessageBuffer buffer) {
|
||||
public long readLong(@NonNull ByteBufMessageBuffer buffer) {
|
||||
return buffer.buffer().readLong();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readVarInt(@NotNull ByteBufMessageBuffer buffer) {
|
||||
public int readVarInt(@NonNull ByteBufMessageBuffer buffer) {
|
||||
return VarInts.readInt(buffer.buffer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readUnsignedVarInt(@NotNull ByteBufMessageBuffer buffer) {
|
||||
public int readUnsignedVarInt(@NonNull ByteBufMessageBuffer buffer) {
|
||||
return VarInts.readUnsignedInt(buffer.buffer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readVarLong(@NotNull ByteBufMessageBuffer buffer) {
|
||||
public long readVarLong(@NonNull ByteBufMessageBuffer buffer) {
|
||||
return VarInts.readLong(buffer.buffer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readUnsignedVarLong(@NotNull ByteBufMessageBuffer buffer) {
|
||||
public long readUnsignedVarLong(@NonNull ByteBufMessageBuffer buffer) {
|
||||
return VarInts.readUnsignedLong(buffer.buffer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String readString(@NotNull ByteBufMessageBuffer buffer) {
|
||||
public @NonNull String readString(@NonNull ByteBufMessageBuffer buffer) {
|
||||
int size = VarInts.readUnsignedInt(buffer.buffer());
|
||||
byte[] bytes = new byte[size];
|
||||
buffer.buffer().readBytes(bytes);
|
||||
@@ -105,74 +104,74 @@ public class ByteBufCodec implements MessageCodec<ByteBufMessageBuffer> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeBoolean(@NotNull ByteBufMessageBuffer buffer, boolean value) {
|
||||
public void writeBoolean(@NonNull ByteBufMessageBuffer buffer, boolean value) {
|
||||
buffer.buffer().writeBoolean(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeByte(@NotNull ByteBufMessageBuffer buffer, byte value) {
|
||||
public void writeByte(@NonNull ByteBufMessageBuffer buffer, byte value) {
|
||||
buffer.buffer().writeByte(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeShort(@NotNull ByteBufMessageBuffer buffer, short value) {
|
||||
public void writeShort(@NonNull ByteBufMessageBuffer buffer, short value) {
|
||||
buffer.buffer().writeShort(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeInt(@NotNull ByteBufMessageBuffer buffer, int value) {
|
||||
public void writeInt(@NonNull ByteBufMessageBuffer buffer, int value) {
|
||||
buffer.buffer().writeInt(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeFloat(@NotNull ByteBufMessageBuffer buffer, float value) {
|
||||
public void writeFloat(@NonNull ByteBufMessageBuffer buffer, float value) {
|
||||
buffer.buffer().writeFloat(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeDouble(@NotNull ByteBufMessageBuffer buffer, double value) {
|
||||
public void writeDouble(@NonNull ByteBufMessageBuffer buffer, double value) {
|
||||
buffer.buffer().writeDouble(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeLong(@NotNull ByteBufMessageBuffer buffer, long value) {
|
||||
public void writeLong(@NonNull ByteBufMessageBuffer buffer, long value) {
|
||||
buffer.buffer().writeLong(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeVarInt(@NotNull ByteBufMessageBuffer buffer, int value) {
|
||||
public void writeVarInt(@NonNull ByteBufMessageBuffer buffer, int value) {
|
||||
VarInts.writeInt(buffer.buffer(), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeUnsignedVarInt(@NotNull ByteBufMessageBuffer buffer, int value) {
|
||||
public void writeUnsignedVarInt(@NonNull ByteBufMessageBuffer buffer, int value) {
|
||||
VarInts.writeUnsignedInt(buffer.buffer(), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeVarLong(@NotNull ByteBufMessageBuffer buffer, long value) {
|
||||
public void writeVarLong(@NonNull ByteBufMessageBuffer buffer, long value) {
|
||||
VarInts.writeLong(buffer.buffer(), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeUnsignedVarLong(@NotNull ByteBufMessageBuffer buffer, long value) {
|
||||
public void writeUnsignedVarLong(@NonNull ByteBufMessageBuffer buffer, long value) {
|
||||
VarInts.writeUnsignedLong(buffer.buffer(), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeString(@NotNull ByteBufMessageBuffer buffer, @NonNull String value) {
|
||||
public void writeString(@NonNull ByteBufMessageBuffer buffer, @NonNull String value) {
|
||||
byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
|
||||
VarInts.writeUnsignedInt(buffer.buffer(), bytes.length);
|
||||
buffer.buffer().writeBytes(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ByteBufMessageBuffer createBuffer() {
|
||||
public @NonNull ByteBufMessageBuffer createBuffer() {
|
||||
return new ByteBufMessageBuffer(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ByteBufMessageBuffer createBuffer(byte @NotNull [] data) {
|
||||
public @NonNull ByteBufMessageBuffer createBuffer(byte @NonNull [] data) {
|
||||
return new ByteBufMessageBuffer(this, Unpooled.wrappedBuffer(data));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,6 @@ import io.netty.buffer.Unpooled;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.cloudburstmc.protocol.common.util.VarInts;
|
||||
import org.geysermc.geyser.api.network.message.MessageCodec;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
@@ -39,62 +38,62 @@ public class ByteBufCodecLE implements MessageCodec<ByteBufMessageBuffer> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean readBoolean(@NotNull ByteBufMessageBuffer buffer) {
|
||||
public boolean readBoolean(@NonNull ByteBufMessageBuffer buffer) {
|
||||
return buffer.buffer().readBoolean();
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte readByte(@NotNull ByteBufMessageBuffer buffer) {
|
||||
public byte readByte(@NonNull ByteBufMessageBuffer buffer) {
|
||||
return buffer.buffer().readByte();
|
||||
}
|
||||
|
||||
@Override
|
||||
public short readShort(@NotNull ByteBufMessageBuffer buffer) {
|
||||
public short readShort(@NonNull ByteBufMessageBuffer buffer) {
|
||||
return buffer.buffer().readShortLE();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readInt(@NotNull ByteBufMessageBuffer buffer) {
|
||||
public int readInt(@NonNull ByteBufMessageBuffer buffer) {
|
||||
return buffer.buffer().readIntLE();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float readFloat(@NotNull ByteBufMessageBuffer buffer) {
|
||||
public float readFloat(@NonNull ByteBufMessageBuffer buffer) {
|
||||
return buffer.buffer().readFloatLE();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double readDouble(@NotNull ByteBufMessageBuffer buffer) {
|
||||
public double readDouble(@NonNull ByteBufMessageBuffer buffer) {
|
||||
return buffer.buffer().readDoubleLE();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readLong(@NotNull ByteBufMessageBuffer buffer) {
|
||||
public long readLong(@NonNull ByteBufMessageBuffer buffer) {
|
||||
return buffer.buffer().readLongLE();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readVarInt(@NotNull ByteBufMessageBuffer buffer) {
|
||||
public int readVarInt(@NonNull ByteBufMessageBuffer buffer) {
|
||||
return VarInts.readInt(buffer.buffer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int readUnsignedVarInt(@NotNull ByteBufMessageBuffer buffer) {
|
||||
public int readUnsignedVarInt(@NonNull ByteBufMessageBuffer buffer) {
|
||||
return VarInts.readUnsignedInt(buffer.buffer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readVarLong(@NotNull ByteBufMessageBuffer buffer) {
|
||||
public long readVarLong(@NonNull ByteBufMessageBuffer buffer) {
|
||||
return VarInts.readLong(buffer.buffer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public long readUnsignedVarLong(@NotNull ByteBufMessageBuffer buffer) {
|
||||
public long readUnsignedVarLong(@NonNull ByteBufMessageBuffer buffer) {
|
||||
return VarInts.readUnsignedLong(buffer.buffer());
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull String readString(@NotNull ByteBufMessageBuffer buffer) {
|
||||
public @NonNull String readString(@NonNull ByteBufMessageBuffer buffer) {
|
||||
int size = VarInts.readUnsignedInt(buffer.buffer());
|
||||
byte[] bytes = new byte[size];
|
||||
buffer.buffer().readBytes(bytes);
|
||||
@@ -103,74 +102,74 @@ public class ByteBufCodecLE implements MessageCodec<ByteBufMessageBuffer> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeBoolean(@NotNull ByteBufMessageBuffer buffer, boolean value) {
|
||||
public void writeBoolean(@NonNull ByteBufMessageBuffer buffer, boolean value) {
|
||||
buffer.buffer().writeBoolean(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeByte(@NotNull ByteBufMessageBuffer buffer, byte value) {
|
||||
public void writeByte(@NonNull ByteBufMessageBuffer buffer, byte value) {
|
||||
buffer.buffer().writeByte(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeShort(@NotNull ByteBufMessageBuffer buffer, short value) {
|
||||
public void writeShort(@NonNull ByteBufMessageBuffer buffer, short value) {
|
||||
buffer.buffer().writeShortLE(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeInt(@NotNull ByteBufMessageBuffer buffer, int value) {
|
||||
public void writeInt(@NonNull ByteBufMessageBuffer buffer, int value) {
|
||||
buffer.buffer().writeIntLE(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeFloat(@NotNull ByteBufMessageBuffer buffer, float value) {
|
||||
public void writeFloat(@NonNull ByteBufMessageBuffer buffer, float value) {
|
||||
buffer.buffer().writeFloatLE(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeDouble(@NotNull ByteBufMessageBuffer buffer, double value) {
|
||||
public void writeDouble(@NonNull ByteBufMessageBuffer buffer, double value) {
|
||||
buffer.buffer().writeDoubleLE(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeLong(@NotNull ByteBufMessageBuffer buffer, long value) {
|
||||
public void writeLong(@NonNull ByteBufMessageBuffer buffer, long value) {
|
||||
buffer.buffer().writeLongLE(value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeVarInt(@NotNull ByteBufMessageBuffer buffer, int value) {
|
||||
public void writeVarInt(@NonNull ByteBufMessageBuffer buffer, int value) {
|
||||
VarInts.writeInt(buffer.buffer(), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeUnsignedVarInt(@NotNull ByteBufMessageBuffer buffer, int value) {
|
||||
public void writeUnsignedVarInt(@NonNull ByteBufMessageBuffer buffer, int value) {
|
||||
VarInts.writeUnsignedInt(buffer.buffer(), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeVarLong(@NotNull ByteBufMessageBuffer buffer, long value) {
|
||||
public void writeVarLong(@NonNull ByteBufMessageBuffer buffer, long value) {
|
||||
VarInts.writeLong(buffer.buffer(), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeUnsignedVarLong(@NotNull ByteBufMessageBuffer buffer, long value) {
|
||||
public void writeUnsignedVarLong(@NonNull ByteBufMessageBuffer buffer, long value) {
|
||||
VarInts.writeUnsignedLong(buffer.buffer(), value);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeString(@NotNull ByteBufMessageBuffer buffer, @NonNull String value) {
|
||||
public void writeString(@NonNull ByteBufMessageBuffer buffer, @NonNull String value) {
|
||||
byte[] bytes = value.getBytes(StandardCharsets.UTF_8);
|
||||
VarInts.writeUnsignedInt(buffer.buffer(), bytes.length);
|
||||
buffer.buffer().writeBytes(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ByteBufMessageBuffer createBuffer() {
|
||||
public @NonNull ByteBufMessageBuffer createBuffer() {
|
||||
return new ByteBufMessageBuffer(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ByteBufMessageBuffer createBuffer(byte @NotNull [] data) {
|
||||
public @NonNull ByteBufMessageBuffer createBuffer(byte @NonNull [] data) {
|
||||
return new ByteBufMessageBuffer(this, Unpooled.wrappedBuffer(data));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.api.network.message.Message;
|
||||
import org.geysermc.mcprotocollib.protocol.codec.MinecraftPacket;
|
||||
|
||||
public record JavaPacketMessage(MinecraftPacket packet) implements Message.PacketWrapped<ByteBufMessageBuffer> {
|
||||
public record JavaPacketMessage<T extends MinecraftPacket>(T packet) implements Message.PacketWrapped<ByteBufMessageBuffer, T> {
|
||||
|
||||
@Override
|
||||
public void encode(@NonNull ByteBufMessageBuffer buffer) {
|
||||
|
||||
@@ -40,6 +40,7 @@ import org.geysermc.geyser.api.extension.Extension;
|
||||
import org.geysermc.geyser.api.item.custom.CustomItemData;
|
||||
import org.geysermc.geyser.api.item.custom.CustomItemOptions;
|
||||
import org.geysermc.geyser.api.item.custom.NonVanillaCustomItemData;
|
||||
import org.geysermc.geyser.api.network.NetworkChannel;
|
||||
import org.geysermc.geyser.api.network.message.Message;
|
||||
import org.geysermc.geyser.api.pack.PathPackCodec;
|
||||
import org.geysermc.geyser.api.pack.UrlPackCodec;
|
||||
@@ -61,6 +62,9 @@ import org.geysermc.geyser.level.block.GeyserGeometryComponent;
|
||||
import org.geysermc.geyser.level.block.GeyserJavaBlockState;
|
||||
import org.geysermc.geyser.level.block.GeyserMaterialInstance;
|
||||
import org.geysermc.geyser.level.block.GeyserNonVanillaCustomBlockData;
|
||||
import org.geysermc.geyser.network.ExtensionNetworkChannel;
|
||||
import org.geysermc.geyser.network.ExternalNetworkChannel;
|
||||
import org.geysermc.geyser.network.PacketChannel;
|
||||
import org.geysermc.geyser.network.message.BedrockPacketMessage;
|
||||
import org.geysermc.geyser.network.message.JavaPacketMessage;
|
||||
import org.geysermc.geyser.pack.option.GeyserPriorityOption;
|
||||
@@ -72,6 +76,7 @@ import org.geysermc.geyser.registry.provider.ProviderSupplier;
|
||||
import org.geysermc.mcprotocollib.protocol.codec.MinecraftPacket;
|
||||
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -121,14 +126,29 @@ public class ProviderRegistryLoader implements RegistryLoader<Map<Class<?>, Prov
|
||||
}
|
||||
|
||||
if (args[0] instanceof BedrockPacket bedrockPacket) {
|
||||
return new BedrockPacketMessage(bedrockPacket);
|
||||
return new BedrockPacketMessage<>(bedrockPacket);
|
||||
} else if (args[0] instanceof MinecraftPacket javaPacket) {
|
||||
return new JavaPacketMessage(javaPacket);
|
||||
return new JavaPacketMessage<>(javaPacket);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported packet type: " + args[0].getClass().getName());
|
||||
}
|
||||
});
|
||||
|
||||
providers.put(NetworkChannel.class, args -> {
|
||||
// Extension network channel
|
||||
if (args.length == 3 && args[0] instanceof Extension extension && args[1] instanceof String channel && args[2] instanceof Class<?> messageType) {
|
||||
return new ExtensionNetworkChannel(extension, channel, messageType);
|
||||
} else if (args.length == 3 && args[0] instanceof String key && args[1] instanceof Integer packetId && args[2] instanceof Class<?> packetType) {
|
||||
// Packet channel
|
||||
return new PacketChannel(key, packetId, packetType);
|
||||
} else if (args.length == 2 && args[0] instanceof Identifier identifier && args[1] instanceof Class<?> messageType) {
|
||||
// External network channel
|
||||
return new ExternalNetworkChannel(identifier, messageType);
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unknown arguments provided for NetworkChannel provider. Could not create a channel given the arguments: " + Arrays.toString(args));
|
||||
}
|
||||
});
|
||||
|
||||
return providers;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,8 +40,6 @@ import org.geysermc.erosion.packet.geyserbound.GeyserboundPacket;
|
||||
import org.geysermc.floodgate.pluginmessage.PluginMessageChannels;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.GeyserLogger;
|
||||
import org.geysermc.geyser.api.event.EventBus;
|
||||
import org.geysermc.geyser.api.event.java.ServerReceiveNetworkMessageEvent;
|
||||
import org.geysermc.geyser.api.network.MessageDirection;
|
||||
import org.geysermc.geyser.api.network.NetworkChannel;
|
||||
import org.geysermc.geyser.api.network.message.Message;
|
||||
@@ -54,6 +52,9 @@ import org.geysermc.mcprotocollib.protocol.packet.common.clientbound.Clientbound
|
||||
import org.geysermc.mcprotocollib.protocol.packet.common.serverbound.ServerboundCustomPayloadPacket;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
@Translator(packet = ClientboundCustomPayloadPacket.class)
|
||||
public class JavaCustomPayloadTranslator extends PacketTranslator<ClientboundCustomPayloadPacket> {
|
||||
@@ -153,16 +154,28 @@ public class JavaCustomPayloadTranslator extends PacketTranslator<ClientboundCus
|
||||
} else {
|
||||
session.ensureInEventLoop(() -> {
|
||||
GeyserNetworkManager networkManager = session.getNetworkManager();
|
||||
NetworkChannel networkChannel = NetworkChannel.of(packet.getChannel().namespace(), packet.getChannel().value());
|
||||
if (!networkManager.registeredChannels().contains(networkChannel)) {
|
||||
logger.debug("Received a custom payload for an unregistered channel: " + networkChannel.channel());
|
||||
Set<NetworkChannel> channels = networkManager.registeredChannels();
|
||||
if (channels.isEmpty()) {
|
||||
this.logger.debug("Received a custom payload for an unregistered channel: " + channel);
|
||||
return;
|
||||
}
|
||||
|
||||
Message<MessageBuffer> message = networkManager.createMessage(networkChannel, packet.getData());
|
||||
List<NetworkChannel> identifiedChannels = new ArrayList<>();
|
||||
for (NetworkChannel registeredChannel : channels) {
|
||||
if (!registeredChannel.isPacket() && registeredChannel.identifier().toString().equals(channel)) {
|
||||
identifiedChannels.add(registeredChannel);
|
||||
}
|
||||
}
|
||||
|
||||
EventBus<?> eventBus = session.getGeyser().getEventBus();
|
||||
eventBus.fire(new ServerReceiveNetworkMessageEvent(session, networkChannel, message, MessageDirection.CLIENTBOUND));
|
||||
if (identifiedChannels.isEmpty()) {
|
||||
this.logger.debug("Received a custom payload for an unregistered channel: " + channel);
|
||||
return;
|
||||
}
|
||||
|
||||
for (NetworkChannel networkChannel : identifiedChannels) {
|
||||
List<Message<MessageBuffer>> message = networkManager.createMessages(networkChannel, packet.getData());
|
||||
networkManager.handleMessages(networkChannel, message, MessageDirection.CLIENTBOUND);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -137,7 +137,8 @@ public class JavaLoginTranslator extends PacketTranslator<ClientboundLoginPacket
|
||||
if (!registeredChannels.isEmpty()) {
|
||||
String channels = registeredChannels
|
||||
.stream()
|
||||
.map(channel -> channel.key() + ":" + channel.channel())
|
||||
.filter(channel -> !channel.isPacket())
|
||||
.map(channel -> channel.identifier().namespace() + ":" + channel.identifier().path())
|
||||
.collect(Collectors.joining("\0"));
|
||||
|
||||
session.sendDownstreamPacket(new ServerboundCustomPayloadPacket(register, channels.getBytes(StandardCharsets.UTF_8)));
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* Copyright (c) 2025 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*
|
||||
* @author GeyserMC
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.network;
|
||||
|
||||
import net.kyori.adventure.key.Key;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.geysermc.geyser.api.network.MessageDirection;
|
||||
import org.geysermc.geyser.api.network.NetworkChannel;
|
||||
import org.geysermc.geyser.api.network.message.DataType;
|
||||
import org.geysermc.geyser.api.network.message.Message;
|
||||
import org.geysermc.geyser.api.network.message.MessageBuffer;
|
||||
import org.geysermc.geyser.api.network.message.MessageHandler;
|
||||
import org.geysermc.geyser.api.network.message.MessagePriority;
|
||||
import org.geysermc.geyser.impl.IdentifierImpl;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
import static org.geysermc.geyser.util.GeyserMockContext.mockContext;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.fail;
|
||||
|
||||
public class MessageRegistrationOrderTest {
|
||||
private final NetworkChannel xuidChannel = new ExternalNetworkChannel(new IdentifierImpl(Key.key("geyser_test", "xuid")), XuidMessage.class);
|
||||
|
||||
@Test
|
||||
void testRegistrationOrder() {
|
||||
mockContext(context -> {
|
||||
GeyserSession session = context.mock(GeyserSession.class);
|
||||
GeyserNetworkManager manager = new GeyserNetworkManager(session);
|
||||
|
||||
AtomicInteger state = new AtomicInteger(0);
|
||||
|
||||
new NetworkDefinitionBuilder<XuidMessage>(registration -> manager.onRegister(this.xuidChannel, XuidMessage::new, registration))
|
||||
.clientbound(MessagePriority.EARLY, message -> {
|
||||
assertEquals(0, state.getAndIncrement());
|
||||
return MessageHandler.State.UNHANDLED;
|
||||
})
|
||||
.register();
|
||||
|
||||
new NetworkDefinitionBuilder<XuidMessage>(registration -> manager.onRegister(this.xuidChannel, XuidMessage::new, registration))
|
||||
.clientbound(MessagePriority.LATE, message -> {
|
||||
assertEquals(2, state.getAndIncrement());
|
||||
return MessageHandler.State.UNHANDLED;
|
||||
})
|
||||
.register();
|
||||
|
||||
new NetworkDefinitionBuilder<XuidMessage>(registration -> manager.onRegister(this.xuidChannel, XuidMessage::new, registration))
|
||||
.clientbound(MessagePriority.NORMAL, message -> {
|
||||
assertEquals(1, state.getAndIncrement());
|
||||
return MessageHandler.State.UNHANDLED;
|
||||
})
|
||||
.register();
|
||||
|
||||
manager.handleMessages(this.xuidChannel, List.of(new XuidMessage("test-xuid")), MessageDirection.CLIENTBOUND);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testPipelineTags() {
|
||||
mockContext(context -> {
|
||||
GeyserSession session = context.mock(GeyserSession.class);
|
||||
GeyserNetworkManager manager = new GeyserNetworkManager(session);
|
||||
|
||||
AtomicInteger state = new AtomicInteger(0);
|
||||
|
||||
new NetworkDefinitionBuilder<XuidMessage>(registration -> manager.onRegister(this.xuidChannel, XuidMessage::new, registration))
|
||||
.clientbound(message -> {
|
||||
assertEquals(2, state.getAndIncrement());
|
||||
return MessageHandler.State.UNHANDLED;
|
||||
})
|
||||
.pipeline(pipeline -> {
|
||||
pipeline.tag("monitor");
|
||||
})
|
||||
.register();
|
||||
|
||||
new NetworkDefinitionBuilder<XuidMessage>(registration -> manager.onRegister(this.xuidChannel, XuidMessage::new, registration))
|
||||
.clientbound(message -> {
|
||||
assertEquals(1, state.getAndIncrement());
|
||||
return MessageHandler.State.UNHANDLED;
|
||||
})
|
||||
.pipeline(pipeline -> {
|
||||
pipeline.tag("initial-handler");
|
||||
pipeline.before("monitor");
|
||||
})
|
||||
.register();
|
||||
|
||||
new NetworkDefinitionBuilder<XuidMessage>(registration -> manager.onRegister(this.xuidChannel, XuidMessage::new, registration))
|
||||
.clientbound(message -> {
|
||||
assertEquals(3, state.getAndIncrement());
|
||||
return MessageHandler.State.UNHANDLED;
|
||||
})
|
||||
.pipeline(pipeline -> {
|
||||
pipeline.tag("tail");
|
||||
pipeline.after("monitor");
|
||||
})
|
||||
.register();
|
||||
|
||||
// No pipeline, so should automatically be added to the tail
|
||||
new NetworkDefinitionBuilder<XuidMessage>(registration -> manager.onRegister(this.xuidChannel, XuidMessage::new, registration))
|
||||
.clientbound(message -> {
|
||||
assertEquals(4, state.getAndIncrement());
|
||||
return MessageHandler.State.UNHANDLED;
|
||||
})
|
||||
.register();
|
||||
|
||||
// Early priority - should come first regardless of tail structure
|
||||
new NetworkDefinitionBuilder<XuidMessage>(registration -> manager.onRegister(this.xuidChannel, XuidMessage::new, registration))
|
||||
.clientbound(MessagePriority.EARLY, message -> {
|
||||
assertEquals(0, state.getAndIncrement());
|
||||
return MessageHandler.State.UNHANDLED;
|
||||
})
|
||||
.register();
|
||||
|
||||
manager.handleMessages(this.xuidChannel, List.of(new XuidMessage("test-xuid")), MessageDirection.CLIENTBOUND);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void testMixedHandlers() {
|
||||
mockContext(context -> {
|
||||
GeyserSession session = context.mock(GeyserSession.class);
|
||||
GeyserNetworkManager manager = new GeyserNetworkManager(session);
|
||||
|
||||
AtomicInteger state = new AtomicInteger(0);
|
||||
|
||||
// Simple early clientbound
|
||||
new NetworkDefinitionBuilder<XuidMessage>(registration -> manager.onRegister(this.xuidChannel, XuidMessage::new, registration))
|
||||
.clientbound(MessagePriority.EARLY, message -> {
|
||||
assertEquals(0, state.getAndIncrement());
|
||||
return MessageHandler.State.UNHANDLED;
|
||||
})
|
||||
.register();
|
||||
|
||||
// Late clientbound but first serverbound
|
||||
new NetworkDefinitionBuilder<XuidMessage>(registration -> manager.onRegister(this.xuidChannel, XuidMessage::new, registration))
|
||||
.clientbound(MessagePriority.LATE, message -> {
|
||||
assertEquals(2, state.getAndIncrement());
|
||||
return MessageHandler.State.UNHANDLED;
|
||||
})
|
||||
.serverbound(MessagePriority.FIRST, message -> {
|
||||
fail("Serverbound handler should not be called in this test");
|
||||
return MessageHandler.State.UNHANDLED;
|
||||
})
|
||||
.register();
|
||||
|
||||
// Normal (default) bidirectional
|
||||
new NetworkDefinitionBuilder<XuidMessage>(registration -> manager.onRegister(this.xuidChannel, XuidMessage::new, registration))
|
||||
.bidirectional((message, direction) -> {
|
||||
if (direction == MessageDirection.SERVERBOUND) {
|
||||
fail("Serverbound handler should not be called in this test");
|
||||
return MessageHandler.State.UNHANDLED;
|
||||
}
|
||||
|
||||
assertEquals(1, state.getAndIncrement());
|
||||
return MessageHandler.State.UNHANDLED;
|
||||
})
|
||||
.register();
|
||||
|
||||
// Serverbound only - should never be called
|
||||
new NetworkDefinitionBuilder<XuidMessage>(registration -> manager.onRegister(this.xuidChannel, XuidMessage::new, registration))
|
||||
.serverbound(MessagePriority.NORMAL, message -> {
|
||||
fail("Serverbound handler should not be called in this test");
|
||||
return MessageHandler.State.UNHANDLED;
|
||||
})
|
||||
.register();
|
||||
|
||||
manager.handleMessages(this.xuidChannel, List.of(new XuidMessage("test-xuid")), MessageDirection.CLIENTBOUND);
|
||||
});
|
||||
}
|
||||
|
||||
public record XuidMessage(String xuid) implements Message.Simple {
|
||||
|
||||
public XuidMessage(MessageBuffer buffer) {
|
||||
this(buffer.read(DataType.STRING));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void encode(@NonNull MessageBuffer buffer) {
|
||||
buffer.write(DataType.STRING, this.xuid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,8 +25,8 @@
|
||||
|
||||
package org.geysermc.geyser.scoreboard.network;
|
||||
|
||||
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNextPacket;
|
||||
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNoNextPacket;
|
||||
import static org.geysermc.geyser.util.AssertUtils.assertNextPacket;
|
||||
import static org.geysermc.geyser.util.AssertUtils.assertNoNextPacket;
|
||||
import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.spawnPlayerSilently;
|
||||
import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.mockContextScoreboard;
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ import java.util.EnumSet;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.*;
|
||||
import static org.geysermc.geyser.util.AssertUtils.*;
|
||||
import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.mockContextScoreboard;
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@@ -25,8 +25,8 @@
|
||||
|
||||
package org.geysermc.geyser.scoreboard.network.belowname;
|
||||
|
||||
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNextPacket;
|
||||
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNoNextPacket;
|
||||
import static org.geysermc.geyser.util.AssertUtils.assertNextPacket;
|
||||
import static org.geysermc.geyser.util.AssertUtils.assertNoNextPacket;
|
||||
import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.spawnPlayerSilently;
|
||||
import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.mockContextScoreboard;
|
||||
|
||||
|
||||
@@ -25,8 +25,8 @@
|
||||
|
||||
package org.geysermc.geyser.scoreboard.network.playerlist;
|
||||
|
||||
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNextPacket;
|
||||
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNoNextPacket;
|
||||
import static org.geysermc.geyser.util.AssertUtils.assertNextPacket;
|
||||
import static org.geysermc.geyser.util.AssertUtils.assertNoNextPacket;
|
||||
import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.mockContextScoreboard;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -25,9 +25,9 @@
|
||||
|
||||
package org.geysermc.geyser.scoreboard.network.server;
|
||||
|
||||
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNextPacket;
|
||||
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNextPacketMatch;
|
||||
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNoNextPacket;
|
||||
import static org.geysermc.geyser.util.AssertUtils.assertNextPacket;
|
||||
import static org.geysermc.geyser.util.AssertUtils.assertNextPacketMatch;
|
||||
import static org.geysermc.geyser.util.AssertUtils.assertNoNextPacket;
|
||||
import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.spawnPlayer;
|
||||
import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.mockContextScoreboard;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
@@ -25,8 +25,8 @@
|
||||
|
||||
package org.geysermc.geyser.scoreboard.network.sidebar;
|
||||
|
||||
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNextPacket;
|
||||
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNoNextPacket;
|
||||
import static org.geysermc.geyser.util.AssertUtils.assertNextPacket;
|
||||
import static org.geysermc.geyser.util.AssertUtils.assertNoNextPacket;
|
||||
import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.mockContextScoreboard;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -25,8 +25,8 @@
|
||||
|
||||
package org.geysermc.geyser.scoreboard.network.sidebar;
|
||||
|
||||
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNextPacket;
|
||||
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNoNextPacket;
|
||||
import static org.geysermc.geyser.util.AssertUtils.assertNextPacket;
|
||||
import static org.geysermc.geyser.util.AssertUtils.assertNoNextPacket;
|
||||
import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.mockContextScoreboard;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -25,8 +25,8 @@
|
||||
|
||||
package org.geysermc.geyser.scoreboard.network.sidebar;
|
||||
|
||||
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNextPacket;
|
||||
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNoNextPacket;
|
||||
import static org.geysermc.geyser.util.AssertUtils.assertNextPacket;
|
||||
import static org.geysermc.geyser.util.AssertUtils.assertNoNextPacket;
|
||||
import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContextScoreboard.mockContextScoreboard;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -25,9 +25,9 @@
|
||||
|
||||
package org.geysermc.geyser.scoreboard.network.util;
|
||||
|
||||
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNextPacketType;
|
||||
import static org.geysermc.geyser.scoreboard.network.util.AssertUtils.assertNoNextPacket;
|
||||
import static org.geysermc.geyser.scoreboard.network.util.GeyserMockContext.mockContext;
|
||||
import static org.geysermc.geyser.util.AssertUtils.assertNextPacketType;
|
||||
import static org.geysermc.geyser.util.AssertUtils.assertNoNextPacket;
|
||||
import static org.geysermc.geyser.util.GeyserMockContext.mockContext;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.spy;
|
||||
@@ -45,6 +45,7 @@ import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.session.cache.EntityCache;
|
||||
import org.geysermc.geyser.session.cache.WorldCache;
|
||||
import org.geysermc.geyser.session.cache.waypoint.WaypointCache;
|
||||
import org.geysermc.geyser.util.GeyserMockContext;
|
||||
import org.mockito.stubbing.Answer;
|
||||
|
||||
public class GeyserMockContextScoreboard {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2024 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2024-2025 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -23,7 +23,7 @@
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.scoreboard.network.util;
|
||||
package org.geysermc.geyser.util;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.function.Consumer;
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2024 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2024-2025 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -23,7 +23,7 @@
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.scoreboard.network.util;
|
||||
package org.geysermc.geyser.util;
|
||||
|
||||
import org.geysermc.geyser.GeyserLogger;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2024 GeyserMC. http://geysermc.org
|
||||
* Copyright (c) 2024-2025 GeyserMC. http://geysermc.org
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -23,22 +23,23 @@
|
||||
* @link https://github.com/GeyserMC/Geyser
|
||||
*/
|
||||
|
||||
package org.geysermc.geyser.scoreboard.network.util;
|
||||
package org.geysermc.geyser.util;
|
||||
|
||||
import static org.mockito.Mockito.mockStatic;
|
||||
import static org.mockito.Mockito.when;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
||||
import org.geysermc.geyser.event.GeyserEventBus;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import org.cloudburstmc.protocol.bedrock.packet.BedrockPacket;
|
||||
import org.geysermc.geyser.GeyserImpl;
|
||||
import org.geysermc.geyser.configuration.GeyserConfiguration;
|
||||
import org.geysermc.geyser.registry.Registries;
|
||||
import org.geysermc.geyser.session.GeyserSession;
|
||||
import org.geysermc.geyser.translator.protocol.PacketTranslator;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
import static org.mockito.Mockito.mockStatic;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class GeyserMockContext {
|
||||
private final List<Object> mocksAndSpies = new ArrayList<>();
|
||||
@@ -58,6 +59,9 @@ public class GeyserMockContext {
|
||||
var logger = context.storeObject(new EmptyGeyserLogger());
|
||||
when(geyserImpl.getLogger()).thenReturn(logger);
|
||||
|
||||
var eventBus = context.mock(GeyserEventBus.class);
|
||||
when(geyserImpl.getEventBus()).thenReturn(eventBus);
|
||||
|
||||
try (var geyserImplMock = mockStatic(GeyserImpl.class)) {
|
||||
geyserImplMock.when(GeyserImpl::getInstance).thenReturn(geyserImpl);
|
||||
|
||||
@@ -113,7 +117,7 @@ public class GeyserMockContext {
|
||||
return mockOrSpy(GeyserSession.class);
|
||||
}
|
||||
|
||||
void addPacket(BedrockPacket packet) {
|
||||
public void addPacket(BedrockPacket packet) {
|
||||
packets.add(packet);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user