Skip to content

WIP - New API #3243

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ dependencies {
}

java {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17

withJavadocJar()
withSourcesJar()
Expand Down
157 changes: 157 additions & 0 deletions src/main/java/dev/protocollib/Example.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package dev.protocollib;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;

import dev.protocollib.api.ProtocolLib;
import dev.protocollib.api.listener.PacketListenerBundleBehavior;
import dev.protocollib.api.listener.PacketListenerPriority;
import dev.protocollib.api.packet.MutablePacketContainer;
import dev.protocollib.api.packet.PacketContainer;
import dev.protocollib.api.packet.PacketTypes;
import dev.protocollib.api.reflect.MutableGenericAccessor;

public class Example {

private static final ExecutorService EXECUTOR = Executors.newCachedThreadPool();
private static ProtocolLib protocolLib;
private static Plugin plugin;

public static class ParticleData {
public String s;
public int i;
}

public static class ParticlePacket {
public int duration;
public String name;
public ParticleData data;
}

static {
MutablePacketContainer packet = null;
Class<?> clazz = MutableGenericAccessor.class;

packet.accessor().update(a -> {
a.update(ParticleData.class, 0, b -> {
b.set(int.class, 0, -1);
});
});

packet.accessor().update(accessor -> {

accessor.setObject(clazz, 1, "");
accessor.set(Integer.class, 1, 1235);
accessor.set(String.class, 1, "world");

accessor.update(Object.class, 1, a -> {
a.update(Object.class, 1, b -> {
b.set(int.class, 2, -1);
});
});

accessor.update(Object.class, 1, mutableAccessor -> {
mutableAccessor.set(int.class, 1, -1);
});

accessor.update(ParticleData.class, 1, (pd) -> {
pd.i++;
return pd;
});

accessor.update(ParticleData.class, 1, (pd) -> {
pd.set(int.class, 1, pd.get(int.class, 1) + 1);
});
});
}

// ========================
// Packet Listeners
// ========================

static void registerListeners() {
// sync read-only listener
protocolLib
.createListener(plugin)
.types(PacketTypes.Game.LEVEL_CHUNK)
.priority(PacketListenerPriority.LOW)
.includeCanceledPackets()
.bundleBehavior(PacketListenerBundleBehavior.SKIP_OUTSIDE_BUNDLE)
.registerSync((packet, context) -> {
Chunk chunk = Chunk.from(packet);
// Sync processing
});

// async modify packet on netty thread
protocolLib
.createListener(plugin)
.types(PacketTypes.Game.LEVEL_CHUNK)
.mutable()
.registerAsync((packet, context) -> {
Chunk chunk = Chunk.from(packet);

// do processing here ...
// write changes to packet ...

context.addTransmissionListener(chunk::markSent);
context.resumeProcessing();
});

// async modify packet on app thread-pool
protocolLib
.createListener(plugin)
.types(PacketTypes.Game.LEVEL_CHUNK)
.mutable()
.registerAsync((packet, context) -> {
Chunk chunk = Chunk.from(packet);

EXECUTOR.execute(() -> {

// do heavy processing here ...
// write changes to packet ...

context.addTransmissionListener(chunk::markSent);
context.resumeProcessing();
});
});
}

// ========================
// Packet Sending
// ========================

static void sendPackets(Player player, Chunk chunk) {
// full packet operation
protocolLib
.connection(player)
.packetOperation()
.skipProcessing()
.postTransmission(chunk::markSent)
.send(chunk.packet());

// connection shortcut
protocolLib
.connection(player)
.sendPacket(chunk.packet());

// direct API shortcut
protocolLib
.sendPacket(player, chunk.packet());
}

// ========================
// Chunk Implementation
// ========================

static class Chunk {
static Chunk from(PacketContainer packet) {
return new Chunk();
}

PacketContainer packet() { return null; }
void markSent() {}
}
}
86 changes: 86 additions & 0 deletions src/main/java/dev/protocollib/api/Connection.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package dev.protocollib.api;

import java.net.InetSocketAddress;
import java.util.Optional;

import org.bukkit.entity.Player;

import dev.protocollib.api.packet.PacketLike;
import dev.protocollib.api.packet.PacketOperationBuilder;

/**
* Represents a connection associated with a player.
*
* <p>This interface provides methods to interact with the player's network connection,
* including retrieving the player's information, connection address, protocol version,
* and current connection state. It also allows for sending and receiving packets
* through the connection.</p>
*/
public interface Connection {


/**
* Retrieves the player associated with the connection, if available.
*
* @return an {@link Optional} containing the player, or empty if the player is not present
*/
Optional<Player> player();

/**
* Retrieves the address of the connection.
*
* @return the remote {@link InetSocketAddress} of the connection
*/
InetSocketAddress address();

/**
* Retrieves the protocol version used by the connection.
*
* @return the protocol version
*/
int protocolVersion();

/**
* Retrieves the current protocol phase of the connection for a given direction.
*
* @param packetDirection the direction of the packet (clientbound or serverbound)
* @return the {@link ProtocolPhase} representing the current phase of the connection
*/
ProtocolPhase protocolPhase(ProtocolDirection packetDirection);

/**
* Checks if the connection is currently open.
*
* @return {@code true} if the connection is open, {@code false} otherwise
*/
boolean isConnected();

/**
* Initiates a packet operation, which can involve sending or receiving a packet.
*
* @return a {@link PacketOperationBuilder} to configure the packet operation
*/
PacketOperationBuilder packetOperation();

/**
* Sends a packet to the client.
*
* @param packet the packet to send
*/
void sendPacket(PacketLike packet);

/**
* Receives a packet as if the client had sent it.
*
* @param packet the received packet
*/
void receivePacket(PacketLike packet);

/**
* Disconnects the connection with the specified reason.
*
* @param reason the reason for disconnecting
*/
void disconnect(String reason);

}
14 changes: 14 additions & 0 deletions src/main/java/dev/protocollib/api/ProtocolDirection.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package dev.protocollib.api;

/**
* Representing the direction of a packet, either sent to or from the server.
*/
public enum ProtocolDirection {

/** Packet sent from the client to the server. */
SERVERBOUND,

/** Packet sent from the server to the client. */
CLIENTBOUND;

}
67 changes: 67 additions & 0 deletions src/main/java/dev/protocollib/api/ProtocolLib.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package dev.protocollib.api;

import org.bukkit.entity.Player;
import org.bukkit.plugin.Plugin;

import dev.protocollib.api.listener.PacketListenerBuilder;
import dev.protocollib.api.packet.BinaryPacket;
import dev.protocollib.api.packet.MutablePacketContainer;
import dev.protocollib.api.packet.PacketLike;
import dev.protocollib.api.packet.PacketType;

/**
* Representing the main entry point for the ProtocolLib API.
*/
public interface ProtocolLib {

/**
* Creates a packet listener for the provided plugin.
*
* @param plugin the plugin registering the packet listener
* @return a builder to configure and register the packet listener
*/
PacketListenerBuilder createListener(Plugin plugin);

/**
* Retrieves the connection associated with a specific player.
*
* @param player the player whose connection is being retrieved
* @return the connection for the specified player
*/
Connection connection(Player player);

/**
* Sends a packet to the player.
*
* @param packet the packet to send
*/
default void sendPacket(Player player, PacketLike packet) {
connection(player).sendPacket(packet);
}

/**
* Receives a packet as if the player had sent it.
*
* @param packet the received packet
*/
default void receivePacket(Player player, PacketLike packet) {
connection(player).receivePacket(packet);
}

/**
* Creates a new binary packet with the given type and payload.
*
* @param packetType the type of the packet to create
* @param payload the binary payload to include in the packet
* @return a new {@link BinaryPacket} instance
*/
BinaryPacket createBinaryPacket(PacketType packetType, byte[] payload);

/**
* Creates a new packet container for the given packet type.
*
* @param packetType the type of the packet to create
* @return a new {@link MutablePacketContainer} instance
*/
MutablePacketContainer createPacket(PacketType packetType);
}
10 changes: 10 additions & 0 deletions src/main/java/dev/protocollib/api/ProtocolPhase.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package dev.protocollib.api;

/**
* Representing the different protocol phases of a minecraft.
*/
public enum ProtocolPhase {

HANDSHAKE, GAME, STATUS, LOGIN, CONFIGURATION, UNKNOWN;

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package dev.protocollib.api.listener;

import org.jetbrains.annotations.NotNull;

import dev.protocollib.api.packet.PacketContainer;

/**
* Functional interface for handling immutable packets.
*
* <p>An immutable packet listener can be executed either synchronously or asynchronously
* depending on how it was registered. If registered asynchronously, it will be executed
* in parallel with other listeners, ensuring non-blocking packet processing.</p>
*/
@FunctionalInterface
public interface ImmutablePacketListener {

/**
* Handles a packet that was sent or received.
*
* @param packet the immutable packet to handle
* @param context the context providing additional information about the packet
*/
void handlePacket(@NotNull PacketContainer packet, @NotNull ImmutablePacketListenerContext context);
}
Loading