Merge branch 'feature/1.19.4'

This commit is contained in:
Camotoy 2023-03-14 07:48:46 -04:00
commit 3bc9926a38
No known key found for this signature in database
GPG key ID: 7EEFB66FE798081F
93 changed files with 4492 additions and 239 deletions

27
Jenkinsfile vendored
View file

@ -23,6 +23,31 @@ pipeline {
when {
branch "master"
}
pipeline {
agent any
tools {
maven 'Maven 3'
jdk 'Java 8'
}
options {
buildDiscarder(logRotator(artifactNumToKeepStr: '20'))
}
stages {
stage ('Build') {
steps {
sh 'mvn clean package'
}
post {
success {
archiveArtifacts artifacts: 'target/*.jar', excludes: 'target/*-sources.jar', fingerprint: true
}
}
}
stage ('Deploy') {
when {
branch "master"
}
steps {
rtMavenDeployer(
@ -50,4 +75,4 @@ pipeline {
}
}
}
}
}

View file

@ -0,0 +1,50 @@
package com.github.steveice10.packetlib.test;
import com.github.steveice10.packetlib.Session;
import com.github.steveice10.packetlib.event.session.ConnectedEvent;
import com.github.steveice10.packetlib.event.session.DisconnectedEvent;
import com.github.steveice10.packetlib.event.session.DisconnectingEvent;
import com.github.steveice10.packetlib.event.session.SessionAdapter;
import com.github.steveice10.packetlib.packet.Packet;
public class ClientSessionListener extends SessionAdapter {
@Override
public void packetReceived(Session session, Packet packet) {
if (packet instanceof PingPacket) {
String id = ((PingPacket) packet).getPingId();
System.out.println("CLIENT Received: " + id);
if (id.equals("hello")) {
session.send(new PingPacket("exit"));
} else if (id.equals("exit")) {
session.disconnect("Finished");
}
}
}
@Override
public void packetSent(Session session, Packet packet) {
if (packet instanceof PingPacket) {
System.out.println("CLIENT Sent: " + ((PingPacket) packet).getPingId());
}
}
@Override
public void connected(ConnectedEvent event) {
System.out.println("CLIENT Connected");
event.getSession().enableEncryption(((TestProtocol) event.getSession().getPacketProtocol()).getEncryption());
event.getSession().send(new PingPacket("hello"));
}
@Override
public void disconnecting(DisconnectingEvent event) {
System.out.println("CLIENT Disconnecting: " + event.getReason());
}
@Override
public void disconnected(DisconnectedEvent event) {
System.out.println("CLIENT Disconnected: " + event.getReason());
}
}

View file

@ -0,0 +1,32 @@
package com.github.steveice10.packetlib.test;
import com.github.steveice10.packetlib.codec.PacketCodecHelper;
import com.github.steveice10.packetlib.packet.Packet;
import java.io.IOException;
public class PingPacket implements Packet {
private final String id;
public PingPacket(ByteBuf buf, PacketCodecHelper codecHelper) throws IOException {
this.id = codecHelper.readString(buf);
}
public PingPacket(String id) {
this.id = id;
}
public String getPingId() {
return this.id;
}
@Override
public void write(ByteBuf buf, PacketCodecHelper codecHelper) throws IOException {
codecHelper.writeString(buf, this.id);
}
@Override
public boolean isPriority() {
return false;
}
}

View file

@ -0,0 +1,31 @@
package com.github.steveice10.packetlib.test;
import com.github.steveice10.packetlib.Server;
import com.github.steveice10.packetlib.Session;
import com.github.steveice10.packetlib.tcp.TcpClientSession;
import com.github.steveice10.packetlib.tcp.TcpServer;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.security.NoSuchAlgorithmException;
public class PingServerTest {
public static void main(String[] args) {
SecretKey key;
try {
KeyGenerator gen = KeyGenerator.getInstance("AES");
gen.init(128);
key = gen.generateKey();
} catch(NoSuchAlgorithmException e) {
System.err.println("AES algorithm not supported, exiting...");
return;
}
Server server = new TcpServer("127.0.0.1", 25565, TestProtocol::new);
server.addListener(new ServerListener(key));
server.bind();
Session client = new TcpClientSession("127.0.0.1", 25565, new TestProtocol(key));
client.connect();
}
}

View file

@ -0,0 +1,46 @@
package com.github.steveice10.packetlib.test;
import com.github.steveice10.packetlib.event.server.ServerAdapter;
import com.github.steveice10.packetlib.event.server.ServerBoundEvent;
import com.github.steveice10.packetlib.event.server.ServerClosedEvent;
import com.github.steveice10.packetlib.event.server.ServerClosingEvent;
import com.github.steveice10.packetlib.event.server.SessionAddedEvent;
import com.github.steveice10.packetlib.event.server.SessionRemovedEvent;
import javax.crypto.SecretKey;
public class ServerListener extends ServerAdapter {
private SecretKey key;
public ServerListener(SecretKey key) {
this.key = key;
}
@Override
public void serverBound(ServerBoundEvent event) {
System.out.println("SERVER Bound: " + event.getServer().getHost() + ":" + event.getServer().getPort());
}
@Override
public void serverClosing(ServerClosingEvent event) {
System.out.println("CLOSING SERVER...");
}
@Override
public void serverClosed(ServerClosedEvent event) {
System.out.println("SERVER CLOSED");
}
@Override
public void sessionAdded(SessionAddedEvent event) {
System.out.println("SERVER Session Added: " + event.getSession().getHost() + ":" + event.getSession().getPort());
((TestProtocol) event.getSession().getPacketProtocol()).setSecretKey(this.key);
event.getSession().enableEncryption(((TestProtocol) event.getSession().getPacketProtocol()).getEncryption());
}
@Override
public void sessionRemoved(SessionRemovedEvent event) {
System.out.println("SERVER Session Removed: " + event.getSession().getHost() + ":" + event.getSession().getPort());
event.getServer().close(false);
}
}

View file

@ -0,0 +1,40 @@
package com.github.steveice10.packetlib.test;
import com.github.steveice10.packetlib.Session;
import com.github.steveice10.packetlib.event.session.ConnectedEvent;
import com.github.steveice10.packetlib.event.session.DisconnectedEvent;
import com.github.steveice10.packetlib.event.session.DisconnectingEvent;
import com.github.steveice10.packetlib.event.session.SessionAdapter;
import com.github.steveice10.packetlib.packet.Packet;
public class ServerSessionListener extends SessionAdapter {
@Override
public void packetReceived(Session session, Packet packet) {
if (packet instanceof PingPacket) {
System.out.println("SERVER Received: " + ((PingPacket) packet).getPingId());
session.send(packet);
}
}
@Override
public void packetSent(Session session, Packet packet) {
if (packet instanceof PingPacket) {
System.out.println("SERVER Sent: " + ((PingPacket) packet).getPingId());
}
}
@Override
public void connected(ConnectedEvent event) {
System.out.println("SERVER Connected");
}
@Override
public void disconnecting(DisconnectingEvent event) {
System.out.println("SERVER Disconnecting: " + event.getReason());
}
@Override
public void disconnected(DisconnectedEvent event) {
System.out.println("SERVER Disconnected: " + event.getReason());
}
}

View file

@ -0,0 +1,62 @@
package com.github.steveice10.packetlib.test;
import com.github.steveice10.packetlib.Server;
import com.github.steveice10.packetlib.Session;
import com.github.steveice10.packetlib.crypt.AESEncryption;
import com.github.steveice10.packetlib.crypt.PacketEncryption;
import com.github.steveice10.packetlib.packet.DefaultPacketHeader;
import com.github.steveice10.packetlib.packet.PacketHeader;
import com.github.steveice10.packetlib.packet.PacketProtocol;
import javax.crypto.SecretKey;
import java.security.GeneralSecurityException;
public class TestProtocol extends PacketProtocol {
private final PacketHeader header = new DefaultPacketHeader();
private AESEncryption encrypt;
@SuppressWarnings("unused")
public TestProtocol() {
}
public TestProtocol(SecretKey key) {
this.setSecretKey(key);
}
public PacketCodecHelper createHelper() {
return new BasePacketCodecHelper();
}
public void setSecretKey(SecretKey key) {
this.register(0, PingPacket.class, PingPacket::new);
try {
this.encrypt = new AESEncryption(key);
} catch(GeneralSecurityException e) {
e.printStackTrace();
}
}
@Override
public String getSRVRecordPrefix() {
return "_test";
}
@Override
public PacketHeader getPacketHeader() {
return this.header;
}
public PacketEncryption getEncryption() {
return this.encrypt;
}
@Override
public void newClientSession(Session session) {
session.addListener(new ClientSessionListener());
}
@Override
public void newServerSession(Server server, Session session) {
session.addListener(new ServerSessionListener());
}
}

39
pom.xml
View file

@ -5,7 +5,7 @@
<groupId>com.github.steveice10</groupId>
<artifactId>mcprotocollib</artifactId>
<version>1.19.3-SNAPSHOT</version>
<version>1.19.4-SNAPSHOT</version>
<packaging>jar</packaging>
<name>MCProtocolLib</name>
@ -87,12 +87,6 @@
<version>1.4</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.github.steveice10</groupId>
<artifactId>packetlib</artifactId>
<version>3.0.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.github.GeyserMC</groupId>
<artifactId>mcauthlib</artifactId>
@ -135,6 +129,37 @@
<version>4.13.1</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.66.Final</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-codec-haproxy</artifactId>
<version>4.1.66.Final</version>
<scope>compile</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>io.netty.incubator</groupId>
<artifactId>netty-incubator-transport-native-io_uring</artifactId>
<version>0.0.8.Final</version>
<classifier>linux-x86_64</classifier>
</dependency>
<dependency>
<groupId>com.github.spotbugs</groupId>
<artifactId>spotbugs-annotations</artifactId>
<version>4.3.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>com.nukkitx.fastutil</groupId>
<artifactId>fastutil-int-object-maps</artifactId>
<version>8.5.2</version>
<scope>compile</scope>
</dependency>
</dependencies>
<build>

View file

@ -86,7 +86,7 @@ public class ClientListener extends SessionAdapter {
} else if (packet instanceof ClientboundGameProfilePacket) {
protocol.setState(ProtocolState.GAME);
} else if (packet instanceof ClientboundLoginDisconnectPacket) {
session.disconnect(((ClientboundLoginDisconnectPacket) packet).getReason().toString());
session.disconnect(((ClientboundLoginDisconnectPacket) packet).getReason());
} else if (packet instanceof ClientboundLoginCompressionPacket) {
session.setCompressionThreshold(((ClientboundLoginCompressionPacket) packet).getThreshold(), false);
}
@ -112,7 +112,7 @@ public class ClientListener extends SessionAdapter {
if (packet instanceof ClientboundKeepAlivePacket && session.getFlag(MinecraftConstants.AUTOMATIC_KEEP_ALIVE_MANAGEMENT, true)) {
session.send(new ServerboundKeepAlivePacket(((ClientboundKeepAlivePacket) packet).getPingId()));
} else if (packet instanceof ClientboundDisconnectPacket) {
session.disconnect(((ClientboundDisconnectPacket) packet).getReason().toString());
session.disconnect(((ClientboundDisconnectPacket) packet).getReason());
}
}
}

View file

@ -30,7 +30,11 @@ import com.github.steveice10.packetlib.packet.Packet;
import net.kyori.adventure.text.Component;
import javax.crypto.SecretKey;
import java.security.*;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.NoSuchAlgorithmException;
import java.security.PrivateKey;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
import java.util.UUID;
@ -125,7 +129,7 @@ public class ServerListener extends SessionAdapter {
if (builder == null) {
builder = $ -> new ServerStatusInfo(
new VersionInfo(protocol.getCodec().getMinecraftVersion(), protocol.getCodec().getProtocolVersion()),
new PlayerInfo(0, 20, new GameProfile[0]),
new PlayerInfo(0, 20, new ArrayList<>()),
Component.text("A Minecraft Server"),
null,
false

View file

@ -13,6 +13,7 @@ import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundCo
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundCustomChatCompletionsPacket;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundCustomPayloadPacket;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundDeleteChatPacket;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundDelimiterPacket;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundDisconnectPacket;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundDisguisedChatPacket;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundKeepAlivePacket;
@ -36,7 +37,9 @@ import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundUp
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundUpdateRecipesPacket;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundUpdateTagsPacket;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.entity.ClientboundAnimatePacket;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.entity.ClientboundDamageEventPacket;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.entity.ClientboundEntityEventPacket;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.entity.ClientboundHurtAnimationPacket;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.entity.ClientboundMoveEntityPosPacket;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.entity.ClientboundMoveEntityPosRotPacket;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.entity.ClientboundMoveEntityRotPacket;
@ -79,6 +82,7 @@ import com.github.steveice10.mc.protocol.packet.ingame.clientbound.level.Clientb
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.level.ClientboundBlockEntityDataPacket;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.level.ClientboundBlockEventPacket;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.level.ClientboundBlockUpdatePacket;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.level.ClientboundChunksBiomesPacket;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.level.ClientboundExplodePacket;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.level.ClientboundForgetLevelChunkPacket;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.level.ClientboundGameEventPacket;
@ -195,9 +199,9 @@ public class MinecraftCodec {
}
public static final PacketCodec CODEC = PacketCodec.builder()
.protocolVersion(761)
.protocolVersion(762)
.helper(() -> new MinecraftCodecHelper(LEVEL_EVENTS, SOUND_NAMES))
.minecraftVersion("1.19.3")
.minecraftVersion("1.19.4")
.state(ProtocolState.HANDSHAKE, PacketStateCodec.builder()
.registerServerboundPacket(0x00, ClientIntentionPacket.class, ClientIntentionPacket::new)
)
@ -216,146 +220,150 @@ public class MinecraftCodec {
.registerServerboundPacket(0x00, ServerboundStatusRequestPacket.class, ServerboundStatusRequestPacket::new)
.registerServerboundPacket(0x01, ServerboundPingRequestPacket.class, ServerboundPingRequestPacket::new)
).state(ProtocolState.GAME, PacketStateCodec.builder()
.registerClientboundPacket(0x00, ClientboundAddEntityPacket.class, ClientboundAddEntityPacket::new)
.registerClientboundPacket(0x01, ClientboundAddExperienceOrbPacket.class, ClientboundAddExperienceOrbPacket::new)
.registerClientboundPacket(0x02, ClientboundAddPlayerPacket.class, ClientboundAddPlayerPacket::new)
.registerClientboundPacket(0x03, ClientboundAnimatePacket.class, ClientboundAnimatePacket::new)
.registerClientboundPacket(0x04, ClientboundAwardStatsPacket.class, ClientboundAwardStatsPacket::new)
.registerClientboundPacket(0x05, ClientboundBlockChangedAckPacket.class, ClientboundBlockChangedAckPacket::new)
.registerClientboundPacket(0x06, ClientboundBlockDestructionPacket.class, ClientboundBlockDestructionPacket::new)
.registerClientboundPacket(0x07, ClientboundBlockEntityDataPacket.class, ClientboundBlockEntityDataPacket::new)
.registerClientboundPacket(0x08, ClientboundBlockEventPacket.class, ClientboundBlockEventPacket::new)
.registerClientboundPacket(0x09, ClientboundBlockUpdatePacket.class, ClientboundBlockUpdatePacket::new)
.registerClientboundPacket(0x0A, ClientboundBossEventPacket.class, ClientboundBossEventPacket::new)
.registerClientboundPacket(0x0B, ClientboundChangeDifficultyPacket.class, ClientboundChangeDifficultyPacket::new)
.registerClientboundPacket(0x0C, ClientboundClearTitlesPacket.class, ClientboundClearTitlesPacket::new)
.registerClientboundPacket(0x0D, ClientboundCommandSuggestionsPacket.class, ClientboundCommandSuggestionsPacket::new)
.registerClientboundPacket(0x0E, ClientboundCommandsPacket.class, ClientboundCommandsPacket::new)
.registerClientboundPacket(0x0F, ClientboundContainerClosePacket.class, ClientboundContainerClosePacket::new)
.registerClientboundPacket(0x10, ClientboundContainerSetContentPacket.class, ClientboundContainerSetContentPacket::new)
.registerClientboundPacket(0x11, ClientboundContainerSetDataPacket.class, ClientboundContainerSetDataPacket::new)
.registerClientboundPacket(0x12, ClientboundContainerSetSlotPacket.class, ClientboundContainerSetSlotPacket::new)
.registerClientboundPacket(0x13, ClientboundCooldownPacket.class, ClientboundCooldownPacket::new)
.registerClientboundPacket(0x14, ClientboundCustomChatCompletionsPacket.class, ClientboundCustomChatCompletionsPacket::new)
.registerClientboundPacket(0x15, ClientboundCustomPayloadPacket.class, ClientboundCustomPayloadPacket::new)
.registerClientboundPacket(0x16, ClientboundDeleteChatPacket.class, ClientboundDeleteChatPacket::new)
.registerClientboundPacket(0x17, ClientboundDisconnectPacket.class, ClientboundDisconnectPacket::new)
.registerClientboundPacket(0x18, ClientboundDisguisedChatPacket.class, ClientboundDisguisedChatPacket::new)
.registerClientboundPacket(0x19, ClientboundEntityEventPacket.class, ClientboundEntityEventPacket::new)
.registerClientboundPacket(0x1A, ClientboundExplodePacket.class, ClientboundExplodePacket::new)
.registerClientboundPacket(0x1B, ClientboundForgetLevelChunkPacket.class, ClientboundForgetLevelChunkPacket::new)
.registerClientboundPacket(0x1C, ClientboundGameEventPacket.class, ClientboundGameEventPacket::new)
.registerClientboundPacket(0x1D, ClientboundHorseScreenOpenPacket.class, ClientboundHorseScreenOpenPacket::new)
.registerClientboundPacket(0x1E, ClientboundInitializeBorderPacket.class, ClientboundInitializeBorderPacket::new)
.registerClientboundPacket(0x1F, ClientboundKeepAlivePacket.class, ClientboundKeepAlivePacket::new)
.registerClientboundPacket(0x20, ClientboundLevelChunkWithLightPacket.class, ClientboundLevelChunkWithLightPacket::new)
.registerClientboundPacket(0x21, ClientboundLevelEventPacket.class, ClientboundLevelEventPacket::new)
.registerClientboundPacket(0x22, ClientboundLevelParticlesPacket.class, ClientboundLevelParticlesPacket::new)
.registerClientboundPacket(0x23, ClientboundLightUpdatePacket.class, ClientboundLightUpdatePacket::new)
.registerClientboundPacket(0x24, ClientboundLoginPacket.class, ClientboundLoginPacket::new)
.registerClientboundPacket(0x25, ClientboundMapItemDataPacket.class, ClientboundMapItemDataPacket::new)
.registerClientboundPacket(0x26, ClientboundMerchantOffersPacket.class, ClientboundMerchantOffersPacket::new)
.registerClientboundPacket(0x27, ClientboundMoveEntityPosPacket.class, ClientboundMoveEntityPosPacket::new)
.registerClientboundPacket(0x28, ClientboundMoveEntityPosRotPacket.class, ClientboundMoveEntityPosRotPacket::new)
.registerClientboundPacket(0x29, ClientboundMoveEntityRotPacket.class, ClientboundMoveEntityRotPacket::new)
.registerClientboundPacket(0x2A, ClientboundMoveVehiclePacket.class, ClientboundMoveVehiclePacket::new)
.registerClientboundPacket(0x2B, ClientboundOpenBookPacket.class, ClientboundOpenBookPacket::new)
.registerClientboundPacket(0x2C, ClientboundOpenScreenPacket.class, ClientboundOpenScreenPacket::new)
.registerClientboundPacket(0x2D, ClientboundOpenSignEditorPacket.class, ClientboundOpenSignEditorPacket::new)
.registerClientboundPacket(0x2E, ClientboundPingPacket.class, ClientboundPingPacket::new)
.registerClientboundPacket(0x2F, ClientboundPlaceGhostRecipePacket.class, ClientboundPlaceGhostRecipePacket::new)
.registerClientboundPacket(0x30, ClientboundPlayerAbilitiesPacket.class, ClientboundPlayerAbilitiesPacket::new)
.registerClientboundPacket(0x31, ClientboundPlayerChatPacket.class, ClientboundPlayerChatPacket::new)
.registerClientboundPacket(0x32, ClientboundPlayerCombatEndPacket.class, ClientboundPlayerCombatEndPacket::new)
.registerClientboundPacket(0x33, ClientboundPlayerCombatEnterPacket.class, ClientboundPlayerCombatEnterPacket::new)
.registerClientboundPacket(0x34, ClientboundPlayerCombatKillPacket.class, ClientboundPlayerCombatKillPacket::new)
.registerClientboundPacket(0x35, ClientboundPlayerInfoRemovePacket.class, ClientboundPlayerInfoRemovePacket::new)
.registerClientboundPacket(0x36, ClientboundPlayerInfoUpdatePacket.class, ClientboundPlayerInfoUpdatePacket::new)
.registerClientboundPacket(0x37, ClientboundPlayerLookAtPacket.class, ClientboundPlayerLookAtPacket::new)
.registerClientboundPacket(0x38, ClientboundPlayerPositionPacket.class, ClientboundPlayerPositionPacket::new)
.registerClientboundPacket(0x39, ClientboundRecipePacket.class, ClientboundRecipePacket::new)
.registerClientboundPacket(0x3A, ClientboundRemoveEntitiesPacket.class, ClientboundRemoveEntitiesPacket::new)
.registerClientboundPacket(0x3B, ClientboundRemoveMobEffectPacket.class, ClientboundRemoveMobEffectPacket::new)
.registerClientboundPacket(0x3C, ClientboundResourcePackPacket.class, ClientboundResourcePackPacket::new)
.registerClientboundPacket(0x3D, ClientboundRespawnPacket.class, ClientboundRespawnPacket::new)
.registerClientboundPacket(0x3E, ClientboundRotateHeadPacket.class, ClientboundRotateHeadPacket::new)
.registerClientboundPacket(0x3F, ClientboundSectionBlocksUpdatePacket.class, ClientboundSectionBlocksUpdatePacket::new)
.registerClientboundPacket(0x40, ClientboundSelectAdvancementsTabPacket.class, ClientboundSelectAdvancementsTabPacket::new)
.registerClientboundPacket(0x41, ClientboundServerDataPacket.class, ClientboundServerDataPacket::new)
.registerClientboundPacket(0x42, ClientboundSetActionBarTextPacket.class, ClientboundSetActionBarTextPacket::new)
.registerClientboundPacket(0x43, ClientboundSetBorderCenterPacket.class, ClientboundSetBorderCenterPacket::new)
.registerClientboundPacket(0x44, ClientboundSetBorderLerpSizePacket.class, ClientboundSetBorderLerpSizePacket::new)
.registerClientboundPacket(0x45, ClientboundSetBorderSizePacket.class, ClientboundSetBorderSizePacket::new)
.registerClientboundPacket(0x46, ClientboundSetBorderWarningDelayPacket.class, ClientboundSetBorderWarningDelayPacket::new)
.registerClientboundPacket(0x47, ClientboundSetBorderWarningDistancePacket.class, ClientboundSetBorderWarningDistancePacket::new)
.registerClientboundPacket(0x48, ClientboundSetCameraPacket.class, ClientboundSetCameraPacket::new)
.registerClientboundPacket(0x49, ClientboundSetCarriedItemPacket.class, ClientboundSetCarriedItemPacket::new)
.registerClientboundPacket(0x4A, ClientboundSetChunkCacheCenterPacket.class, ClientboundSetChunkCacheCenterPacket::new)
.registerClientboundPacket(0x4B, ClientboundSetChunkCacheRadiusPacket.class, ClientboundSetChunkCacheRadiusPacket::new)
.registerClientboundPacket(0x4C, ClientboundSetDefaultSpawnPositionPacket.class, ClientboundSetDefaultSpawnPositionPacket::new)
.registerClientboundPacket(0x4D, ClientboundSetDisplayObjectivePacket.class, ClientboundSetDisplayObjectivePacket::new)
.registerClientboundPacket(0x4E, ClientboundSetEntityDataPacket.class, ClientboundSetEntityDataPacket::new)
.registerClientboundPacket(0x4F, ClientboundSetEntityLinkPacket.class, ClientboundSetEntityLinkPacket::new)
.registerClientboundPacket(0x50, ClientboundSetEntityMotionPacket.class, ClientboundSetEntityMotionPacket::new)
.registerClientboundPacket(0x51, ClientboundSetEquipmentPacket.class, ClientboundSetEquipmentPacket::new)
.registerClientboundPacket(0x52, ClientboundSetExperiencePacket.class, ClientboundSetExperiencePacket::new)
.registerClientboundPacket(0x53, ClientboundSetHealthPacket.class, ClientboundSetHealthPacket::new)
.registerClientboundPacket(0x54, ClientboundSetObjectivePacket.class, ClientboundSetObjectivePacket::new)
.registerClientboundPacket(0x55, ClientboundSetPassengersPacket.class, ClientboundSetPassengersPacket::new)
.registerClientboundPacket(0x56, ClientboundSetPlayerTeamPacket.class, ClientboundSetPlayerTeamPacket::new)
.registerClientboundPacket(0x57, ClientboundSetScorePacket.class, ClientboundSetScorePacket::new)
.registerClientboundPacket(0x58, ClientboundSetSimulationDistancePacket.class, ClientboundSetSimulationDistancePacket::new)
.registerClientboundPacket(0x59, ClientboundSetSubtitleTextPacket.class, ClientboundSetSubtitleTextPacket::new)
.registerClientboundPacket(0x5A, ClientboundSetTimePacket.class, ClientboundSetTimePacket::new)
.registerClientboundPacket(0x5B, ClientboundSetTitleTextPacket.class, ClientboundSetTitleTextPacket::new)
.registerClientboundPacket(0x5C, ClientboundSetTitlesAnimationPacket.class, ClientboundSetTitlesAnimationPacket::new)
.registerClientboundPacket(0x5D, ClientboundSoundEntityPacket.class, ClientboundSoundEntityPacket::new)
.registerClientboundPacket(0x5E, ClientboundSoundPacket.class, ClientboundSoundPacket::new)
.registerClientboundPacket(0x5F, ClientboundStopSoundPacket.class, ClientboundStopSoundPacket::new)
.registerClientboundPacket(0x60, ClientboundSystemChatPacket.class, ClientboundSystemChatPacket::new)
.registerClientboundPacket(0x61, ClientboundTabListPacket.class, ClientboundTabListPacket::new)
.registerClientboundPacket(0x62, ClientboundTagQueryPacket.class, ClientboundTagQueryPacket::new)
.registerClientboundPacket(0x63, ClientboundTakeItemEntityPacket.class, ClientboundTakeItemEntityPacket::new)
.registerClientboundPacket(0x64, ClientboundTeleportEntityPacket.class, ClientboundTeleportEntityPacket::new)
.registerClientboundPacket(0x65, ClientboundUpdateAdvancementsPacket.class, ClientboundUpdateAdvancementsPacket::new)
.registerClientboundPacket(0x66, ClientboundUpdateAttributesPacket.class, ClientboundUpdateAttributesPacket::new)
.registerClientboundPacket(0x67, ClientboundUpdateEnabledFeaturesPacket.class, ClientboundUpdateEnabledFeaturesPacket::new)
.registerClientboundPacket(0x68, ClientboundUpdateMobEffectPacket.class, ClientboundUpdateMobEffectPacket::new)
.registerClientboundPacket(0x69, ClientboundUpdateRecipesPacket.class, ClientboundUpdateRecipesPacket::new)
.registerClientboundPacket(0x6A, ClientboundUpdateTagsPacket.class, ClientboundUpdateTagsPacket::new)
.registerClientboundPacket(0x00, ClientboundDelimiterPacket.class, ClientboundDelimiterPacket::new)
.registerClientboundPacket(0x01, ClientboundAddEntityPacket.class, ClientboundAddEntityPacket::new)
.registerClientboundPacket(0x02, ClientboundAddExperienceOrbPacket.class, ClientboundAddExperienceOrbPacket::new)
.registerClientboundPacket(0x03, ClientboundAddPlayerPacket.class, ClientboundAddPlayerPacket::new)
.registerClientboundPacket(0x04, ClientboundAnimatePacket.class, ClientboundAnimatePacket::new)
.registerClientboundPacket(0x05, ClientboundAwardStatsPacket.class, ClientboundAwardStatsPacket::new)
.registerClientboundPacket(0x06, ClientboundBlockChangedAckPacket.class, ClientboundBlockChangedAckPacket::new)
.registerClientboundPacket(0x07, ClientboundBlockDestructionPacket.class, ClientboundBlockDestructionPacket::new)
.registerClientboundPacket(0x08, ClientboundBlockEntityDataPacket.class, ClientboundBlockEntityDataPacket::new)
.registerClientboundPacket(0x09, ClientboundBlockEventPacket.class, ClientboundBlockEventPacket::new)
.registerClientboundPacket(0x0A, ClientboundBlockUpdatePacket.class, ClientboundBlockUpdatePacket::new)
.registerClientboundPacket(0x0B, ClientboundBossEventPacket.class, ClientboundBossEventPacket::new)
.registerClientboundPacket(0x0C, ClientboundChangeDifficultyPacket.class, ClientboundChangeDifficultyPacket::new)
.registerClientboundPacket(0x0D, ClientboundChunksBiomesPacket.class, ClientboundChunksBiomesPacket::new)
.registerClientboundPacket(0x0E, ClientboundClearTitlesPacket.class, ClientboundClearTitlesPacket::new)
.registerClientboundPacket(0x0F, ClientboundCommandSuggestionsPacket.class, ClientboundCommandSuggestionsPacket::new)
.registerClientboundPacket(0x10, ClientboundCommandsPacket.class, ClientboundCommandsPacket::new)
.registerClientboundPacket(0x11, ClientboundContainerClosePacket.class, ClientboundContainerClosePacket::new)
.registerClientboundPacket(0x12, ClientboundContainerSetContentPacket.class, ClientboundContainerSetContentPacket::new)
.registerClientboundPacket(0x13, ClientboundContainerSetDataPacket.class, ClientboundContainerSetDataPacket::new)
.registerClientboundPacket(0x14, ClientboundContainerSetSlotPacket.class, ClientboundContainerSetSlotPacket::new)
.registerClientboundPacket(0x15, ClientboundCooldownPacket.class, ClientboundCooldownPacket::new)
.registerClientboundPacket(0x16, ClientboundCustomChatCompletionsPacket.class, ClientboundCustomChatCompletionsPacket::new)
.registerClientboundPacket(0x17, ClientboundCustomPayloadPacket.class, ClientboundCustomPayloadPacket::new)
.registerClientboundPacket(0x18, ClientboundDamageEventPacket.class, ClientboundDamageEventPacket::new)
.registerClientboundPacket(0x19, ClientboundDeleteChatPacket.class, ClientboundDeleteChatPacket::new)
.registerClientboundPacket(0x1A, ClientboundDisconnectPacket.class, ClientboundDisconnectPacket::new)
.registerClientboundPacket(0x1B, ClientboundDisguisedChatPacket.class, ClientboundDisguisedChatPacket::new)
.registerClientboundPacket(0x1C, ClientboundEntityEventPacket.class, ClientboundEntityEventPacket::new)
.registerClientboundPacket(0x1D, ClientboundExplodePacket.class, ClientboundExplodePacket::new)
.registerClientboundPacket(0x1E, ClientboundForgetLevelChunkPacket.class, ClientboundForgetLevelChunkPacket::new)
.registerClientboundPacket(0x1F, ClientboundGameEventPacket.class, ClientboundGameEventPacket::new)
.registerClientboundPacket(0x20, ClientboundHorseScreenOpenPacket.class, ClientboundHorseScreenOpenPacket::new)
.registerClientboundPacket(0x21, ClientboundHurtAnimationPacket.class, ClientboundHurtAnimationPacket::new)
.registerClientboundPacket(0x22, ClientboundInitializeBorderPacket.class, ClientboundInitializeBorderPacket::new)
.registerClientboundPacket(0x23, ClientboundKeepAlivePacket.class, ClientboundKeepAlivePacket::new)
.registerClientboundPacket(0x24, ClientboundLevelChunkWithLightPacket.class, ClientboundLevelChunkWithLightPacket::new)
.registerClientboundPacket(0x25, ClientboundLevelEventPacket.class, ClientboundLevelEventPacket::new)
.registerClientboundPacket(0x26, ClientboundLevelParticlesPacket.class, ClientboundLevelParticlesPacket::new)
.registerClientboundPacket(0x27, ClientboundLightUpdatePacket.class, ClientboundLightUpdatePacket::new)
.registerClientboundPacket(0x28, ClientboundLoginPacket.class, ClientboundLoginPacket::new)
.registerClientboundPacket(0x29, ClientboundMapItemDataPacket.class, ClientboundMapItemDataPacket::new)
.registerClientboundPacket(0x2A, ClientboundMerchantOffersPacket.class, ClientboundMerchantOffersPacket::new)
.registerClientboundPacket(0x2B, ClientboundMoveEntityPosPacket.class, ClientboundMoveEntityPosPacket::new)
.registerClientboundPacket(0x2C, ClientboundMoveEntityPosRotPacket.class, ClientboundMoveEntityPosRotPacket::new)
.registerClientboundPacket(0x2D, ClientboundMoveEntityRotPacket.class, ClientboundMoveEntityRotPacket::new)
.registerClientboundPacket(0x2E, ClientboundMoveVehiclePacket.class, ClientboundMoveVehiclePacket::new)
.registerClientboundPacket(0x2F, ClientboundOpenBookPacket.class, ClientboundOpenBookPacket::new)
.registerClientboundPacket(0x30, ClientboundOpenScreenPacket.class, ClientboundOpenScreenPacket::new)
.registerClientboundPacket(0x31, ClientboundOpenSignEditorPacket.class, ClientboundOpenSignEditorPacket::new)
.registerClientboundPacket(0x32, ClientboundPingPacket.class, ClientboundPingPacket::new)
.registerClientboundPacket(0x33, ClientboundPlaceGhostRecipePacket.class, ClientboundPlaceGhostRecipePacket::new)
.registerClientboundPacket(0x34, ClientboundPlayerAbilitiesPacket.class, ClientboundPlayerAbilitiesPacket::new)
.registerClientboundPacket(0x35, ClientboundPlayerChatPacket.class, ClientboundPlayerChatPacket::new)
.registerClientboundPacket(0x36, ClientboundPlayerCombatEndPacket.class, ClientboundPlayerCombatEndPacket::new)
.registerClientboundPacket(0x37, ClientboundPlayerCombatEnterPacket.class, ClientboundPlayerCombatEnterPacket::new)
.registerClientboundPacket(0x38, ClientboundPlayerCombatKillPacket.class, ClientboundPlayerCombatKillPacket::new)
.registerClientboundPacket(0x39, ClientboundPlayerInfoRemovePacket.class, ClientboundPlayerInfoRemovePacket::new)
.registerClientboundPacket(0x3A, ClientboundPlayerInfoUpdatePacket.class, ClientboundPlayerInfoUpdatePacket::new)
.registerClientboundPacket(0x3B, ClientboundPlayerLookAtPacket.class, ClientboundPlayerLookAtPacket::new)
.registerClientboundPacket(0x3C, ClientboundPlayerPositionPacket.class, ClientboundPlayerPositionPacket::new)
.registerClientboundPacket(0x3D, ClientboundRecipePacket.class, ClientboundRecipePacket::new)
.registerClientboundPacket(0x3E, ClientboundRemoveEntitiesPacket.class, ClientboundRemoveEntitiesPacket::new)
.registerClientboundPacket(0x3F, ClientboundRemoveMobEffectPacket.class, ClientboundRemoveMobEffectPacket::new)
.registerClientboundPacket(0x40, ClientboundResourcePackPacket.class, ClientboundResourcePackPacket::new)
.registerClientboundPacket(0x41, ClientboundRespawnPacket.class, ClientboundRespawnPacket::new)
.registerClientboundPacket(0x42, ClientboundRotateHeadPacket.class, ClientboundRotateHeadPacket::new)
.registerClientboundPacket(0x43, ClientboundSectionBlocksUpdatePacket.class, ClientboundSectionBlocksUpdatePacket::new)
.registerClientboundPacket(0x44, ClientboundSelectAdvancementsTabPacket.class, ClientboundSelectAdvancementsTabPacket::new)
.registerClientboundPacket(0x45, ClientboundServerDataPacket.class, ClientboundServerDataPacket::new)
.registerClientboundPacket(0x46, ClientboundSetActionBarTextPacket.class, ClientboundSetActionBarTextPacket::new)
.registerClientboundPacket(0x47, ClientboundSetBorderCenterPacket.class, ClientboundSetBorderCenterPacket::new)
.registerClientboundPacket(0x48, ClientboundSetBorderLerpSizePacket.class, ClientboundSetBorderLerpSizePacket::new)
.registerClientboundPacket(0x49, ClientboundSetBorderSizePacket.class, ClientboundSetBorderSizePacket::new)
.registerClientboundPacket(0x4A, ClientboundSetBorderWarningDelayPacket.class, ClientboundSetBorderWarningDelayPacket::new)
.registerClientboundPacket(0x4B, ClientboundSetBorderWarningDistancePacket.class, ClientboundSetBorderWarningDistancePacket::new)
.registerClientboundPacket(0x4C, ClientboundSetCameraPacket.class, ClientboundSetCameraPacket::new)
.registerClientboundPacket(0x4D, ClientboundSetCarriedItemPacket.class, ClientboundSetCarriedItemPacket::new)
.registerClientboundPacket(0x4E, ClientboundSetChunkCacheCenterPacket.class, ClientboundSetChunkCacheCenterPacket::new)
.registerClientboundPacket(0x4F, ClientboundSetChunkCacheRadiusPacket.class, ClientboundSetChunkCacheRadiusPacket::new)
.registerClientboundPacket(0x50, ClientboundSetDefaultSpawnPositionPacket.class, ClientboundSetDefaultSpawnPositionPacket::new)
.registerClientboundPacket(0x51, ClientboundSetDisplayObjectivePacket.class, ClientboundSetDisplayObjectivePacket::new)
.registerClientboundPacket(0x52, ClientboundSetEntityDataPacket.class, ClientboundSetEntityDataPacket::new)
.registerClientboundPacket(0x53, ClientboundSetEntityLinkPacket.class, ClientboundSetEntityLinkPacket::new)
.registerClientboundPacket(0x54, ClientboundSetEntityMotionPacket.class, ClientboundSetEntityMotionPacket::new)
.registerClientboundPacket(0x55, ClientboundSetEquipmentPacket.class, ClientboundSetEquipmentPacket::new)
.registerClientboundPacket(0x56, ClientboundSetExperiencePacket.class, ClientboundSetExperiencePacket::new)
.registerClientboundPacket(0x57, ClientboundSetHealthPacket.class, ClientboundSetHealthPacket::new)
.registerClientboundPacket(0x58, ClientboundSetObjectivePacket.class, ClientboundSetObjectivePacket::new)
.registerClientboundPacket(0x59, ClientboundSetPassengersPacket.class, ClientboundSetPassengersPacket::new)
.registerClientboundPacket(0x5A, ClientboundSetPlayerTeamPacket.class, ClientboundSetPlayerTeamPacket::new)
.registerClientboundPacket(0x5B, ClientboundSetScorePacket.class, ClientboundSetScorePacket::new)
.registerClientboundPacket(0x5C, ClientboundSetSimulationDistancePacket.class, ClientboundSetSimulationDistancePacket::new)
.registerClientboundPacket(0x5D, ClientboundSetSubtitleTextPacket.class, ClientboundSetSubtitleTextPacket::new)
.registerClientboundPacket(0x5E, ClientboundSetTimePacket.class, ClientboundSetTimePacket::new)
.registerClientboundPacket(0x5F, ClientboundSetTitleTextPacket.class, ClientboundSetTitleTextPacket::new)
.registerClientboundPacket(0x60, ClientboundSetTitlesAnimationPacket.class, ClientboundSetTitlesAnimationPacket::new)
.registerClientboundPacket(0x61, ClientboundSoundEntityPacket.class, ClientboundSoundEntityPacket::new)
.registerClientboundPacket(0x62, ClientboundSoundPacket.class, ClientboundSoundPacket::new)
.registerClientboundPacket(0x63, ClientboundStopSoundPacket.class, ClientboundStopSoundPacket::new)
.registerClientboundPacket(0x64, ClientboundSystemChatPacket.class, ClientboundSystemChatPacket::new)
.registerClientboundPacket(0x65, ClientboundTabListPacket.class, ClientboundTabListPacket::new)
.registerClientboundPacket(0x66, ClientboundTagQueryPacket.class, ClientboundTagQueryPacket::new)
.registerClientboundPacket(0x67, ClientboundTakeItemEntityPacket.class, ClientboundTakeItemEntityPacket::new)
.registerClientboundPacket(0x68, ClientboundTeleportEntityPacket.class, ClientboundTeleportEntityPacket::new)
.registerClientboundPacket(0x69, ClientboundUpdateAdvancementsPacket.class, ClientboundUpdateAdvancementsPacket::new)
.registerClientboundPacket(0x6A, ClientboundUpdateAttributesPacket.class, ClientboundUpdateAttributesPacket::new)
.registerClientboundPacket(0x6B, ClientboundUpdateEnabledFeaturesPacket.class, ClientboundUpdateEnabledFeaturesPacket::new)
.registerClientboundPacket(0x6C, ClientboundUpdateMobEffectPacket.class, ClientboundUpdateMobEffectPacket::new)
.registerClientboundPacket(0x6D, ClientboundUpdateRecipesPacket.class, ClientboundUpdateRecipesPacket::new)
.registerClientboundPacket(0x6E, ClientboundUpdateTagsPacket.class, ClientboundUpdateTagsPacket::new)
.registerServerboundPacket(0x00, ServerboundAcceptTeleportationPacket.class, ServerboundAcceptTeleportationPacket::new)
.registerServerboundPacket(0x01, ServerboundBlockEntityTagQuery.class, ServerboundBlockEntityTagQuery::new)
.registerServerboundPacket(0x02, ServerboundChangeDifficultyPacket.class, ServerboundChangeDifficultyPacket::new)
.registerServerboundPacket(0x03, ServerboundChatAckPacket.class, ServerboundChatAckPacket::new)
.registerServerboundPacket(0x04, ServerboundChatCommandPacket.class, ServerboundChatCommandPacket::new)
.registerServerboundPacket(0x05, ServerboundChatPacket.class, ServerboundChatPacket::new)
.registerServerboundPacket(0x06, ServerboundClientCommandPacket.class, ServerboundClientCommandPacket::new)
.registerServerboundPacket(0x07, ServerboundClientInformationPacket.class, ServerboundClientInformationPacket::new)
.registerServerboundPacket(0x08, ServerboundCommandSuggestionPacket.class, ServerboundCommandSuggestionPacket::new)
.registerServerboundPacket(0x09, ServerboundContainerButtonClickPacket.class, ServerboundContainerButtonClickPacket::new)
.registerServerboundPacket(0x0A, ServerboundContainerClickPacket.class, ServerboundContainerClickPacket::new)
.registerServerboundPacket(0x0B, ServerboundContainerClosePacket.class, ServerboundContainerClosePacket::new)
.registerServerboundPacket(0x0C, ServerboundCustomPayloadPacket.class, ServerboundCustomPayloadPacket::new)
.registerServerboundPacket(0x0D, ServerboundEditBookPacket.class, ServerboundEditBookPacket::new)
.registerServerboundPacket(0x0E, ServerboundEntityTagQuery.class, ServerboundEntityTagQuery::new)
.registerServerboundPacket(0x0F, ServerboundInteractPacket.class, ServerboundInteractPacket::new)
.registerServerboundPacket(0x10, ServerboundJigsawGeneratePacket.class, ServerboundJigsawGeneratePacket::new)
.registerServerboundPacket(0x11, ServerboundKeepAlivePacket.class, ServerboundKeepAlivePacket::new)
.registerServerboundPacket(0x12, ServerboundLockDifficultyPacket.class, ServerboundLockDifficultyPacket::new)
.registerServerboundPacket(0x13, ServerboundMovePlayerPosPacket.class, ServerboundMovePlayerPosPacket::new)
.registerServerboundPacket(0x14, ServerboundMovePlayerPosRotPacket.class, ServerboundMovePlayerPosRotPacket::new)
.registerServerboundPacket(0x15, ServerboundMovePlayerRotPacket.class, ServerboundMovePlayerRotPacket::new)
.registerServerboundPacket(0x16, ServerboundMovePlayerStatusOnlyPacket.class, ServerboundMovePlayerStatusOnlyPacket::new)
.registerServerboundPacket(0x17, ServerboundMoveVehiclePacket.class, ServerboundMoveVehiclePacket::new)
.registerServerboundPacket(0x18, ServerboundPaddleBoatPacket.class, ServerboundPaddleBoatPacket::new)
.registerServerboundPacket(0x19, ServerboundPickItemPacket.class, ServerboundPickItemPacket::new)
.registerServerboundPacket(0x1A, ServerboundPlaceRecipePacket.class, ServerboundPlaceRecipePacket::new)
.registerServerboundPacket(0x1B, ServerboundPlayerAbilitiesPacket.class, ServerboundPlayerAbilitiesPacket::new)
.registerServerboundPacket(0x1C, ServerboundPlayerActionPacket.class, ServerboundPlayerActionPacket::new)
.registerServerboundPacket(0x1D, ServerboundPlayerCommandPacket.class, ServerboundPlayerCommandPacket::new)
.registerServerboundPacket(0x1E, ServerboundPlayerInputPacket.class, ServerboundPlayerInputPacket::new)
.registerServerboundPacket(0x1F, ServerboundPongPacket.class, ServerboundPongPacket::new)
.registerServerboundPacket(0x20, ServerboundChatSessionUpdatePacket.class, ServerboundChatSessionUpdatePacket::new)
.registerServerboundPacket(0x06, ServerboundChatSessionUpdatePacket.class, ServerboundChatSessionUpdatePacket::new)
.registerServerboundPacket(0x07, ServerboundClientCommandPacket.class, ServerboundClientCommandPacket::new)
.registerServerboundPacket(0x08, ServerboundClientInformationPacket.class, ServerboundClientInformationPacket::new)
.registerServerboundPacket(0x09, ServerboundCommandSuggestionPacket.class, ServerboundCommandSuggestionPacket::new)
.registerServerboundPacket(0x0A, ServerboundContainerButtonClickPacket.class, ServerboundContainerButtonClickPacket::new)
.registerServerboundPacket(0x0B, ServerboundContainerClickPacket.class, ServerboundContainerClickPacket::new)
.registerServerboundPacket(0x0C, ServerboundContainerClosePacket.class, ServerboundContainerClosePacket::new)
.registerServerboundPacket(0x0D, ServerboundCustomPayloadPacket.class, ServerboundCustomPayloadPacket::new)
.registerServerboundPacket(0x0E, ServerboundEditBookPacket.class, ServerboundEditBookPacket::new)
.registerServerboundPacket(0x0F, ServerboundEntityTagQuery.class, ServerboundEntityTagQuery::new)
.registerServerboundPacket(0x10, ServerboundInteractPacket.class, ServerboundInteractPacket::new)
.registerServerboundPacket(0x11, ServerboundJigsawGeneratePacket.class, ServerboundJigsawGeneratePacket::new)
.registerServerboundPacket(0x12, ServerboundKeepAlivePacket.class, ServerboundKeepAlivePacket::new)
.registerServerboundPacket(0x13, ServerboundLockDifficultyPacket.class, ServerboundLockDifficultyPacket::new)
.registerServerboundPacket(0x14, ServerboundMovePlayerPosPacket.class, ServerboundMovePlayerPosPacket::new)
.registerServerboundPacket(0x15, ServerboundMovePlayerPosRotPacket.class, ServerboundMovePlayerPosRotPacket::new)
.registerServerboundPacket(0x16, ServerboundMovePlayerRotPacket.class, ServerboundMovePlayerRotPacket::new)
.registerServerboundPacket(0x17, ServerboundMovePlayerStatusOnlyPacket.class, ServerboundMovePlayerStatusOnlyPacket::new)
.registerServerboundPacket(0x18, ServerboundMoveVehiclePacket.class, ServerboundMoveVehiclePacket::new)
.registerServerboundPacket(0x19, ServerboundPaddleBoatPacket.class, ServerboundPaddleBoatPacket::new)
.registerServerboundPacket(0x1A, ServerboundPickItemPacket.class, ServerboundPickItemPacket::new)
.registerServerboundPacket(0x1B, ServerboundPlaceRecipePacket.class, ServerboundPlaceRecipePacket::new)
.registerServerboundPacket(0x1C, ServerboundPlayerAbilitiesPacket.class, ServerboundPlayerAbilitiesPacket::new)
.registerServerboundPacket(0x1D, ServerboundPlayerActionPacket.class, ServerboundPlayerActionPacket::new)
.registerServerboundPacket(0x1E, ServerboundPlayerCommandPacket.class, ServerboundPlayerCommandPacket::new)
.registerServerboundPacket(0x1F, ServerboundPlayerInputPacket.class, ServerboundPlayerInputPacket::new)
.registerServerboundPacket(0x20, ServerboundPongPacket.class, ServerboundPongPacket::new)
.registerServerboundPacket(0x21, ServerboundRecipeBookChangeSettingsPacket.class, ServerboundRecipeBookChangeSettingsPacket::new)
.registerServerboundPacket(0x22, ServerboundRecipeBookSeenRecipePacket.class, ServerboundRecipeBookSeenRecipePacket::new)
.registerServerboundPacket(0x23, ServerboundRenameItemPacket.class, ServerboundRenameItemPacket::new)

View file

@ -21,6 +21,7 @@ import com.github.steveice10.mc.protocol.data.game.entity.metadata.GlobalPos;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.MetadataType;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.Pose;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.SnifferState;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.VillagerData;
import com.github.steveice10.mc.protocol.data.game.entity.object.Direction;
import com.github.steveice10.mc.protocol.data.game.entity.player.BlockBreakStage;
@ -56,6 +57,7 @@ import com.github.steveice10.opennbt.tag.builtin.Tag;
import com.github.steveice10.packetlib.codec.BasePacketCodecHelper;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.math.vector.Vector4f;
import io.netty.buffer.ByteBuf;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import lombok.RequiredArgsConstructor;
@ -292,6 +294,22 @@ public class MinecraftCodecHelper extends BasePacketCodecHelper {
buf.writeFloat(rot.getZ());
}
public Vector4f readQuaternion(ByteBuf buf) {
float x = buf.readFloat();
float y = buf.readFloat();
float z = buf.readFloat();
float w = buf.readFloat();
return Vector4f.from(x, y, z, w);
}
public void writeQuaternion(ByteBuf buf, Vector4f vec4) {
buf.writeFloat(vec4.getX());
buf.writeFloat(vec4.getY());
buf.writeFloat(vec4.getZ());
buf.writeFloat(vec4.getW());
}
public Direction readDirection(ByteBuf buf) {
return Direction.from(this.readVarInt(buf));
}
@ -316,6 +334,14 @@ public class MinecraftCodecHelper extends BasePacketCodecHelper {
this.writeEnum(buf, type);
}
public SnifferState readSnifferState(ByteBuf buf) {
return SnifferState.from(this.readVarInt(buf));
}
public void writeSnifferState(ByteBuf buf, SnifferState state) {
this.writeEnum(buf, state);
}
private void writeEnum(ByteBuf buf, Enum<?> e) {
this.writeVarInt(buf, e.ordinal());
}

View file

@ -0,0 +1,12 @@
package com.github.steveice10.mc.protocol.data.game.chunk;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class ChunkBiomeData {
private final int x;
private final int z;
private final byte[] buffer;
}

View file

@ -48,6 +48,7 @@ public enum CommandParser {
RESOURCE_KEY,
TEMPLATE_MIRROR,
TEMPLATE_ROTATION,
HEIGHTMAP,
UUID;
private static final CommandParser[] VALUES = values();

View file

@ -0,0 +1,10 @@
package com.github.steveice10.mc.protocol.data.game.command.properties;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class TimeProperties implements CommandProperties {
private final int min;
}

View file

@ -63,7 +63,8 @@ public enum EntityEvent {
GOAT_STOP_LOWERING_HEAD,
MAKE_POOF_PARTICLES,
WARDEN_RECEIVE_SIGNAL,
WARDEN_SONIC_BOOM;
WARDEN_SONIC_BOOM,
SNIFFER_MAKE_SOUND;
private static final EntityEvent[] VALUES = values();

View file

@ -1,13 +1,19 @@
package com.github.steveice10.mc.protocol.data.game.entity.metadata;
import com.github.steveice10.mc.protocol.codec.MinecraftCodecHelper;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.*;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.FloatEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.LongEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ObjectEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.object.Direction;
import com.github.steveice10.mc.protocol.data.game.entity.type.PaintingType;
import com.github.steveice10.mc.protocol.data.game.level.particle.Particle;
import com.github.steveice10.opennbt.tag.builtin.CompoundTag;
import com.nukkitx.math.vector.Vector3f;
import com.nukkitx.math.vector.Vector3i;
import com.nukkitx.math.vector.Vector4f;
import io.netty.buffer.ByteBuf;
import lombok.Getter;
import net.kyori.adventure.text.Component;
@ -16,6 +22,7 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.OptionalInt;
import java.util.UUID;
@Getter
@ -36,7 +43,8 @@ public class MetadataType<T> {
public static final MetadataType<Optional<Vector3i>> OPTIONAL_POSITION = new MetadataType<>(optionalReader(MinecraftCodecHelper::readPosition), optionalWriter(MinecraftCodecHelper::writePosition), ObjectEntityMetadata::new);
public static final MetadataType<Direction> DIRECTION = new MetadataType<>(MinecraftCodecHelper::readDirection, MinecraftCodecHelper::writeDirection, ObjectEntityMetadata::new);
public static final MetadataType<Optional<UUID>> OPTIONAL_UUID = new MetadataType<>(optionalReader(MinecraftCodecHelper::readUUID), optionalWriter(MinecraftCodecHelper::writeUUID), ObjectEntityMetadata::new);
public static final OptionalIntMetadataType BLOCK_STATE = new OptionalIntMetadataType(ObjectEntityMetadata::new);
public static final IntMetadataType BLOCK_STATE = new IntMetadataType(MinecraftCodecHelper::readVarInt, MinecraftCodecHelper::writeVarInt, IntEntityMetadata::new);
public static final IntMetadataType OPTIONAL_BLOCK_STATE = new IntMetadataType(MinecraftCodecHelper::readVarInt, MinecraftCodecHelper::writeVarInt, IntEntityMetadata::new);
public static final MetadataType<CompoundTag> NBT_TAG = new MetadataType<>(MinecraftCodecHelper::readTag, MinecraftCodecHelper::writeTag, ObjectEntityMetadata::new);
public static final MetadataType<Particle> PARTICLE = new MetadataType<>(MinecraftCodecHelper::readParticle, MinecraftCodecHelper::writeParticle, ObjectEntityMetadata::new);
public static final MetadataType<VillagerData> VILLAGER_DATA = new MetadataType<>(MinecraftCodecHelper::readVillagerData, MinecraftCodecHelper::writeVillagerData, ObjectEntityMetadata::new);
@ -46,6 +54,9 @@ public class MetadataType<T> {
public static final IntMetadataType FROG_VARIANT = new IntMetadataType(MinecraftCodecHelper::readVarInt, MinecraftCodecHelper::writeVarInt, IntEntityMetadata::new);
public static final MetadataType<Optional<GlobalPos>> OPTIONAL_GLOBAL_POS = new MetadataType<>(optionalReader(MinecraftCodecHelper::readGlobalPos), optionalWriter(MinecraftCodecHelper::writeGlobalPos), ObjectEntityMetadata::new);
public static final MetadataType<PaintingType> PAINTING_VARIANT = new MetadataType<>(MinecraftCodecHelper::readPaintingType, MinecraftCodecHelper::writePaintingType, ObjectEntityMetadata::new);
public static final MetadataType<SnifferState> SNIFFER_STATE = new MetadataType<>(MinecraftCodecHelper::readSnifferState, MinecraftCodecHelper::writeSnifferState, ObjectEntityMetadata::new);
public static final MetadataType<Vector3f> VECTOR3 = new MetadataType<>(MinecraftCodecHelper::readRotation, MinecraftCodecHelper::writeRotation, ObjectEntityMetadata::new);
public static final MetadataType<Vector4f> QUATERNION = new MetadataType<>(MinecraftCodecHelper::readQuaternion, MinecraftCodecHelper::writeQuaternion, ObjectEntityMetadata::new);
protected final int id;
protected final Reader<T> reader;

View file

@ -0,0 +1,17 @@
package com.github.steveice10.mc.protocol.data.game.entity.metadata;
public enum SnifferState {
IDLING,
FEELING_HAPPY,
SCENTING,
SNIFFING,
SEARCHING,
DIGGING,
RISING;
private static final SnifferState[] VALUES = values();
public static SnifferState from(int id) {
return VALUES[id];
}
}

View file

@ -1,16 +1,34 @@
package com.github.steveice10.mc.protocol.data.game.entity.player;
public enum Animation {
SWING_ARM,
DAMAGE,
LEAVE_BED,
SWING_OFFHAND,
CRITICAL_HIT,
ENCHANTMENT_CRITICAL_HIT;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
private static final Animation[] VALUES = values();
public enum Animation {
SWING_ARM(0),
LEAVE_BED(2),
SWING_OFFHAND(3),
CRITICAL_HIT(4),
ENCHANTMENT_CRITICAL_HIT(5);
private final int id;
Animation(int id) {
this.id = id;
}
public int getId() {
return id;
}
private static Int2ObjectMap<Animation> VALUES = new Int2ObjectOpenHashMap<>();
public static Animation from(int id) {
return VALUES[id];
return VALUES.get(id);
}
static {
for (Animation animation : values()) {
VALUES.put(animation.id, animation);
}
}
}

View file

@ -9,32 +9,39 @@ public enum EntityType {
BAT,
BEE,
BLAZE,
BLOCK_DISPLAY,
BOAT,
CHEST_BOAT,
CAT,
CAMEL,
CAT,
CAVE_SPIDER,
CHEST_BOAT,
CHEST_MINECART,
CHICKEN,
COD,
COMMAND_BLOCK_MINECART,
COW,
CREEPER,
DOLPHIN,
DONKEY,
DRAGON_FIREBALL,
DROWNED,
EGG,
ELDER_GUARDIAN,
END_CRYSTAL,
ENDER_DRAGON,
ENDER_PEARL,
ENDERMAN,
ENDERMITE,
EVOKER,
EVOKER_FANGS,
EXPERIENCE_BOTTLE,
EXPERIENCE_ORB,
EYE_OF_ENDER,
FALLING_BLOCK,
FIREWORK_ROCKET,
FOX,
FROG,
FURNACE_MINECART,
GHAST,
GIANT,
GLOW_ITEM_FRAME,
@ -42,11 +49,14 @@ public enum EntityType {
GOAT,
GUARDIAN,
HOGLIN,
HOPPER_MINECART,
HORSE,
HUSK,
ILLUSIONER,
INTERACTION,
IRON_GOLEM,
ITEM,
ITEM_DISPLAY,
ITEM_FRAME,
FIREBALL,
LEASH_KNOT,
@ -56,14 +66,8 @@ public enum EntityType {
MAGMA_CUBE,
MARKER,
MINECART,
CHEST_MINECART,
COMMAND_BLOCK_MINECART,
FURNACE_MINECART,
HOPPER_MINECART,
SPAWNER_MINECART,
TNT_MINECART,
MULE,
MOOSHROOM,
MULE,
OCELOT,
PAINTING,
PANDA,
@ -74,7 +78,7 @@ public enum EntityType {
PIGLIN_BRUTE,
PILLAGER,
POLAR_BEAR,
TNT,
POTION,
PUFFERFISH,
RABBIT,
RAVAGER,
@ -87,20 +91,21 @@ public enum EntityType {
SKELETON_HORSE,
SLIME,
SMALL_FIREBALL,
SNIFFER,
SNOW_GOLEM,
SNOWBALL,
SPAWNER_MINECART,
SPECTRAL_ARROW,
SPIDER,
SQUID,
STRAY,
STRIDER,
TADPOLE,
EGG,
ENDER_PEARL,
EXPERIENCE_BOTTLE,
POTION,
TRIDENT,
TEXT_DISPLAY,
TNT,
TNT_MINECART,
TRADER_LLAMA,
TRIDENT,
TROPICAL_FISH,
TURTLE,
VEX,

View file

@ -21,6 +21,7 @@ public enum ContainerType {
LOOM,
MERCHANT,
SHULKER_BOX,
LEGACY_SMITHING,
SMITHING,
SMOKER,
CARTOGRAPHY,

View file

@ -38,7 +38,9 @@ public enum BlockEntityType {
SCULK_SENSOR,
SCULK_CATALYST,
SCULK_SHRIEKER,
CHISELED_BOOKSHELF;
CHISELED_BOOKSHELF,
SUSPICIOUS_SAND,
DECORATED_POT;
private static final BlockEntityType[] VALUES = values();

View file

@ -0,0 +1,4 @@
package com.github.steveice10.mc.protocol.data.game.level.block.value;
public class NoteBlockValue implements BlockValue {
}

View file

@ -0,0 +1,11 @@
package com.github.steveice10.mc.protocol.data.game.level.block.value;
public enum NoteBlockValueType implements BlockValueType {
PLAY_NOTE;
private static final NoteBlockValueType[] VALUES = values();
public static NoteBlockValueType from(int id) {
return VALUES[id];
}
}

View file

@ -30,6 +30,9 @@ public enum ParticleType {
FIREWORK,
FISHING,
FLAME,
DRIPPING_CHERRY_LEAVES,
FALLING_CHERRY_LEAVES,
LANDING_CHERRY_LEAVES,
SCULK_SOUL,
SCULK_CHARGE,
SCULK_CHARGE_POP,

View file

@ -173,6 +173,8 @@ public enum BuiltinSound implements Sound {
ITEM_BOTTLE_FILL("item.bottle.fill"),
ITEM_BOTTLE_FILL_DRAGONBREATH("item.bottle.fill_dragonbreath"),
BLOCK_BREWING_STAND_BREW("block.brewing_stand.brew"),
ITEM_BRUSH_BRUSHING("item.brush.brushing"),
ITEM_BRUSH_BRUSH_SAND_COMPLETED("item.brush.brush_sand_completed"),
BLOCK_BUBBLE_COLUMN_BUBBLE_POP("block.bubble_column.bubble_pop"),
BLOCK_BUBBLE_COLUMN_UPWARDS_AMBIENT("block.bubble_column.upwards_ambient"),
BLOCK_BUBBLE_COLUMN_UPWARDS_INSIDE("block.bubble_column.upwards_inside"),
@ -238,6 +240,36 @@ public enum BuiltinSound implements Sound {
BLOCK_CHAIN_HIT("block.chain.hit"),
BLOCK_CHAIN_PLACE("block.chain.place"),
BLOCK_CHAIN_STEP("block.chain.step"),
BLOCK_CHERRY_WOOD_BREAK("block.cherry_wood.break"),
BLOCK_CHERRY_WOOD_FALL("block.cherry_wood.fall"),
BLOCK_CHERRY_WOOD_HIT("block.cherry_wood.hit"),
BLOCK_CHERRY_WOOD_PLACE("block.cherry_wood.place"),
BLOCK_CHERRY_WOOD_STEP("block.cherry_wood.step"),
BLOCK_CHERRY_SAPLING_BREAK("block.cherry_sapling.break"),
BLOCK_CHERRY_SAPLING_FALL("block.cherry_sapling.fall"),
BLOCK_CHERRY_SAPLING_HIT("block.cherry_sapling.hit"),
BLOCK_CHERRY_SAPLING_PLACE("block.cherry_sapling.place"),
BLOCK_CHERRY_SAPLING_STEP("block.cherry_sapling.step"),
BLOCK_CHERRY_LEAVES_BREAK("block.cherry_leaves.break"),
BLOCK_CHERRY_LEAVES_FALL("block.cherry_leaves.fall"),
BLOCK_CHERRY_LEAVES_HIT("block.cherry_leaves.hit"),
BLOCK_CHERRY_LEAVES_PLACE("block.cherry_leaves.place"),
BLOCK_CHERRY_LEAVES_STEP("block.cherry_leaves.step"),
BLOCK_CHERRY_WOOD_HANGING_SIGN_STEP("block.cherry_wood_hanging_sign.step"),
BLOCK_CHERRY_WOOD_HANGING_SIGN_BREAK("block.cherry_wood_hanging_sign.break"),
BLOCK_CHERRY_WOOD_HANGING_SIGN_FALL("block.cherry_wood_hanging_sign.fall"),
BLOCK_CHERRY_WOOD_HANGING_SIGN_HIT("block.cherry_wood_hanging_sign.hit"),
BLOCK_CHERRY_WOOD_HANGING_SIGN_PLACE("block.cherry_wood_hanging_sign.place"),
BLOCK_CHERRY_WOOD_DOOR_CLOSE("block.cherry_wood_door.close"),
BLOCK_CHERRY_WOOD_DOOR_OPEN("block.cherry_wood_door.open"),
BLOCK_CHERRY_WOOD_TRAPDOOR_CLOSE("block.cherry_wood_trapdoor.close"),
BLOCK_CHERRY_WOOD_TRAPDOOR_OPEN("block.cherry_wood_trapdoor.open"),
BLOCK_CHERRY_WOOD_BUTTON_CLICK_OFF("block.cherry_wood_button.click_off"),
BLOCK_CHERRY_WOOD_BUTTON_CLICK_ON("block.cherry_wood_button.click_on"),
BLOCK_CHERRY_WOOD_PRESSURE_PLATE_CLICK_OFF("block.cherry_wood_pressure_plate.click_off"),
BLOCK_CHERRY_WOOD_PRESSURE_PLATE_CLICK_ON("block.cherry_wood_pressure_plate.click_on"),
BLOCK_CHERRY_WOOD_FENCE_GATE_CLOSE("block.cherry_wood_fence_gate.close"),
BLOCK_CHERRY_WOOD_FENCE_GATE_OPEN("block.cherry_wood_fence_gate.open"),
BLOCK_CHEST_CLOSE("block.chest.close"),
BLOCK_CHEST_LOCKED("block.chest.locked"),
BLOCK_CHEST_OPEN("block.chest.open"),
@ -300,6 +332,12 @@ public enum BuiltinSound implements Sound {
ITEM_CROSSBOW_QUICK_CHARGE_2("item.crossbow.quick_charge_2"),
ITEM_CROSSBOW_QUICK_CHARGE_3("item.crossbow.quick_charge_3"),
ITEM_CROSSBOW_SHOOT("item.crossbow.shoot"),
BLOCK_DECORATED_POT_BREAK("block.decorated_pot.break"),
BLOCK_DECORATED_POT_FALL("block.decorated_pot.fall"),
BLOCK_DECORATED_POT_HIT("block.decorated_pot.hit"),
BLOCK_DECORATED_POT_STEP("block.decorated_pot.step"),
BLOCK_DECORATED_POT_PLACE("block.decorated_pot.place"),
BLOCK_DECORATED_POT_SHATTER("block.decorated_pot.shatter"),
BLOCK_DEEPSLATE_BRICKS_BREAK("block.deepslate_bricks.break"),
BLOCK_DEEPSLATE_BRICKS_FALL("block.deepslate_bricks.fall"),
BLOCK_DEEPSLATE_BRICKS_HIT("block.deepslate_bricks.hit"),
@ -442,6 +480,11 @@ public enum BuiltinSound implements Sound {
ENTITY_FOX_SNIFF("entity.fox.sniff"),
ENTITY_FOX_SPIT("entity.fox.spit"),
ENTITY_FOX_TELEPORT("entity.fox.teleport"),
BLOCK_SUSPICIOUS_SAND_BREAK("block.suspicious_sand.break"),
BLOCK_SUSPICIOUS_SAND_STEP("block.suspicious_sand.step"),
BLOCK_SUSPICIOUS_SAND_PLACE("block.suspicious_sand.place"),
BLOCK_SUSPICIOUS_SAND_HIT("block.suspicious_sand.hit"),
BLOCK_SUSPICIOUS_SAND_FALL("block.suspicious_sand.fall"),
BLOCK_FROGLIGHT_BREAK("block.froglight.break"),
BLOCK_FROGLIGHT_FALL("block.froglight.fall"),
BLOCK_FROGLIGHT_HIT("block.froglight.hit"),
@ -708,6 +751,11 @@ public enum BuiltinSound implements Sound {
BLOCK_MOSS_CARPET_HIT("block.moss_carpet.hit"),
BLOCK_MOSS_CARPET_PLACE("block.moss_carpet.place"),
BLOCK_MOSS_CARPET_STEP("block.moss_carpet.step"),
BLOCK_PINK_PETALS_BREAK("block.pink_petals.break"),
BLOCK_PINK_PETALS_FALL("block.pink_petals.fall"),
BLOCK_PINK_PETALS_HIT("block.pink_petals.hit"),
BLOCK_PINK_PETALS_PLACE("block.pink_petals.place"),
BLOCK_PINK_PETALS_STEP("block.pink_petals.step"),
BLOCK_MOSS_BREAK("block.moss.break"),
BLOCK_MOSS_FALL("block.moss.fall"),
BLOCK_MOSS_HIT("block.moss.hit"),
@ -766,6 +814,7 @@ public enum BuiltinSound implements Sound {
MUSIC_OVERWORLD_JUNGLE_AND_FOREST("music.overworld.jungle_and_forest"),
MUSIC_OVERWORLD_OLD_GROWTH_TAIGA("music.overworld.old_growth_taiga"),
MUSIC_OVERWORLD_MEADOW("music.overworld.meadow"),
MUSIC_OVERWORLD_CHERRY_GROVE("music.overworld.cherry_grove"),
MUSIC_NETHER_NETHER_WASTES("music.nether.nether_wastes"),
MUSIC_OVERWORLD_FROZEN_PEAKS("music.overworld.frozen_peaks"),
MUSIC_OVERWORLD_SNOWY_SLOPES("music.overworld.snowy_slopes"),
@ -795,6 +844,7 @@ public enum BuiltinSound implements Sound {
BLOCK_NETHER_WOOD_PRESSURE_PLATE_CLICK_ON("block.nether_wood_pressure_plate.click_on"),
BLOCK_NETHER_WOOD_FENCE_GATE_CLOSE("block.nether_wood_fence_gate.close"),
BLOCK_NETHER_WOOD_FENCE_GATE_OPEN("block.nether_wood_fence_gate.open"),
INTENTIONALLY_EMPTY("intentionally_empty"),
BLOCK_PACKED_MUD_BREAK("block.packed_mud.break"),
BLOCK_PACKED_MUD_FALL("block.packed_mud.fall"),
BLOCK_PACKED_MUD_HIT("block.packed_mud.hit"),
@ -1167,6 +1217,18 @@ public enum BuiltinSound implements Sound {
ENTITY_SLIME_SQUISH_SMALL("entity.slime.squish_small"),
BLOCK_SMITHING_TABLE_USE("block.smithing_table.use"),
BLOCK_SMOKER_SMOKE("block.smoker.smoke"),
ENTITY_SNIFFER_STEP("entity.sniffer.step"),
ENTITY_SNIFFER_EAT("entity.sniffer.eat"),
ENTITY_SNIFFER_IDLE("entity.sniffer.idle"),
ENTITY_SNIFFER_HURT("entity.sniffer.hurt"),
ENTITY_SNIFFER_DEATH("entity.sniffer.death"),
ENTITY_SNIFFER_DROP_SEED("entity.sniffer.drop_seed"),
ENTITY_SNIFFER_SCENTING("entity.sniffer.scenting"),
ENTITY_SNIFFER_SNIFFING("entity.sniffer.sniffing"),
ENTITY_SNIFFER_SEARCHING("entity.sniffer.searching"),
ENTITY_SNIFFER_DIGGING("entity.sniffer.digging"),
ENTITY_SNIFFER_DIGGING_STOP("entity.sniffer.digging_stop"),
ENTITY_SNIFFER_HAPPY("entity.sniffer.happy"),
ENTITY_SNOWBALL_THROW("entity.snowball.throw"),
BLOCK_SNOW_BREAK("block.snow.break"),
BLOCK_SNOW_FALL("block.snow.fall"),
@ -1356,14 +1418,14 @@ public enum BuiltinSound implements Sound {
BLOCK_WOODEN_DOOR_OPEN("block.wooden_door.open"),
BLOCK_WOODEN_TRAPDOOR_CLOSE("block.wooden_trapdoor.close"),
BLOCK_WOODEN_TRAPDOOR_OPEN("block.wooden_trapdoor.open"),
BLOCK_WOOD_BREAK("block.wood.break"),
BLOCK_WOODEN_BUTTON_CLICK_OFF("block.wooden_button.click_off"),
BLOCK_WOODEN_BUTTON_CLICK_ON("block.wooden_button.click_on"),
BLOCK_WOODEN_PRESSURE_PLATE_CLICK_OFF("block.wooden_pressure_plate.click_off"),
BLOCK_WOODEN_PRESSURE_PLATE_CLICK_ON("block.wooden_pressure_plate.click_on"),
BLOCK_WOOD_BREAK("block.wood.break"),
BLOCK_WOOD_FALL("block.wood.fall"),
BLOCK_WOOD_HIT("block.wood.hit"),
BLOCK_WOOD_PLACE("block.wood.place"),
BLOCK_WOODEN_PRESSURE_PLATE_CLICK_OFF("block.wooden_pressure_plate.click_off"),
BLOCK_WOODEN_PRESSURE_PLATE_CLICK_ON("block.wooden_pressure_plate.click_on"),
BLOCK_WOOD_STEP("block.wood.step"),
BLOCK_WOOL_BREAK("block.wool.break"),
BLOCK_WOOL_FALL("block.wool.fall"),

View file

@ -27,7 +27,10 @@ public enum RecipeType {
SMOKING,
CAMPFIRE_COOKING,
STONECUTTING,
SMITHING;
SMITHING,
SMITHING_TRANSFORM,
SMITHING_TRIM,
CRAFTING_DECORATED_POT;
private final String resourceLocation;

View file

@ -8,7 +8,7 @@ import lombok.NonNull;
@Data
@AllArgsConstructor
public class SmithingRecipeData implements RecipeData {
public class LegacyUpgradeRecipeData implements RecipeData {
private final @NonNull Ingredient base;
private final @NonNull Ingredient addition;
private final ItemStack result;

View file

@ -0,0 +1,16 @@
package com.github.steveice10.mc.protocol.data.game.recipe.data;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.ItemStack;
import com.github.steveice10.mc.protocol.data.game.recipe.Ingredient;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NonNull;
@Data
@AllArgsConstructor
public class SmithingTransformRecipeData implements RecipeData {
private final @NonNull Ingredient template;
private final @NonNull Ingredient base;
private final @NonNull Ingredient addition;
private final ItemStack result;
}

View file

@ -0,0 +1,14 @@
package com.github.steveice10.mc.protocol.data.game.recipe.data;
import com.github.steveice10.mc.protocol.data.game.recipe.Ingredient;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NonNull;
@Data
@AllArgsConstructor
public class SmithingTrimRecipeData implements RecipeData {
private final @NonNull Ingredient template;
private final @NonNull Ingredient base;
private final @NonNull Ingredient addition;
}

View file

@ -7,11 +7,13 @@ import lombok.Data;
import lombok.NonNull;
import lombok.Setter;
import java.util.List;
@Data
@Setter(AccessLevel.NONE)
@AllArgsConstructor
public class PlayerInfo {
private int maxPlayers;
private int onlinePlayers;
private @NonNull GameProfile[] players;
private @NonNull List<GameProfile> players;
}

View file

@ -0,0 +1,20 @@
package com.github.steveice10.mc.protocol.packet.ingame.clientbound;
import com.github.steveice10.mc.protocol.codec.MinecraftCodecHelper;
import com.github.steveice10.mc.protocol.codec.MinecraftPacket;
import io.netty.buffer.ByteBuf;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.io.IOException;
import java.util.List;
@Data
@AllArgsConstructor
public class ClientboundBundlePacket implements MinecraftPacket {
private final List<MinecraftPacket> packets;
@Override
public void serialize(ByteBuf buf, MinecraftCodecHelper helper) throws IOException {
}
}

View file

@ -130,6 +130,9 @@ public class ClientboundCommandsPacket implements MinecraftPacket {
case SCORE_HOLDER:
properties = new ScoreHolderProperties(in.readBoolean());
break;
case TIME:
properties = new TimeProperties(in.readInt());
break;
case RESOURCE_OR_TAG:
case RESOURCE_OR_TAG_KEY:
case RESOURCE:
@ -298,6 +301,9 @@ public class ClientboundCommandsPacket implements MinecraftPacket {
case SCORE_HOLDER:
out.writeBoolean(((ScoreHolderProperties) node.getProperties()).isAllowMultiple());
break;
case TIME:
out.writeInt(((TimeProperties) node.getProperties()).getMin());
break;
case RESOURCE:
case RESOURCE_OR_TAG:
helper.writeString(out, ((ResourceProperties) node.getProperties()).getRegistryKey());

View file

@ -0,0 +1,14 @@
package com.github.steveice10.mc.protocol.packet.ingame.clientbound;
import com.github.steveice10.mc.protocol.codec.MinecraftCodecHelper;
import com.github.steveice10.mc.protocol.codec.MinecraftPacket;
import io.netty.buffer.ByteBuf;
public class ClientboundDelimiterPacket implements MinecraftPacket {
public ClientboundDelimiterPacket(ByteBuf in, MinecraftCodecHelper helper) {
}
@Override
public void serialize(ByteBuf out, MinecraftCodecHelper helper) {
}
}

View file

@ -2,7 +2,6 @@ package com.github.steveice10.mc.protocol.packet.ingame.clientbound;
import com.github.steveice10.mc.protocol.codec.MinecraftCodecHelper;
import com.github.steveice10.mc.protocol.codec.MinecraftPacket;
import com.github.steveice10.mc.protocol.data.DefaultComponentSerializer;
import io.netty.buffer.ByteBuf;
import lombok.AllArgsConstructor;
import lombok.Data;
@ -16,38 +15,20 @@ import java.io.IOException;
@With
@AllArgsConstructor
public class ClientboundServerDataPacket implements MinecraftPacket {
private final @Nullable Component motd;
private final @Nullable String iconBase64;
private final Component motd;
private final byte @Nullable[] iconBytes;
private final boolean enforcesSecureChat;
public ClientboundServerDataPacket(ByteBuf in, MinecraftCodecHelper helper) throws IOException {
if (in.readBoolean()) {
this.motd = helper.readComponent(in);
} else {
this.motd = null;
}
if (in.readBoolean()) {
this.iconBase64 = helper.readString(in);
} else {
this.iconBase64 = null;
}
this.motd = helper.readComponent(in);
this.iconBytes = helper.readNullable(in, helper::readByteArray);
this.enforcesSecureChat = in.readBoolean();
}
@Override
public void serialize(ByteBuf out, MinecraftCodecHelper helper) throws IOException {
out.writeBoolean(this.motd != null);
if (this.motd != null) {
helper.writeComponent(out, this.motd);
}
out.writeBoolean(this.iconBase64 != null);
if (this.iconBase64 != null) {
helper.writeString(out, this.iconBase64);
}
helper.writeComponent(out, this.motd);
helper.writeNullable(out, this.iconBytes, helper::writeByteArray);
out.writeBoolean(this.enforcesSecureChat);
}
}

View file

@ -85,7 +85,24 @@ public class ClientboundUpdateRecipesPacket implements MinecraftPacket {
Ingredient addition = helper.readRecipeIngredient(in);
ItemStack result = helper.readItemStack(in);
data = new SmithingRecipeData(base, addition, result);
data = new LegacyUpgradeRecipeData(base, addition, result);
break;
}
case SMITHING_TRANSFORM: {
Ingredient template = helper.readRecipeIngredient(in);
Ingredient base = helper.readRecipeIngredient(in);
Ingredient addition = helper.readRecipeIngredient(in);
ItemStack result = helper.readItemStack(in);
data = new SmithingTransformRecipeData(template, base, addition, result);
break;
}
case SMITHING_TRIM: {
Ingredient template = helper.readRecipeIngredient(in);
Ingredient base = helper.readRecipeIngredient(in);
Ingredient addition = helper.readRecipeIngredient(in);
data = new SmithingTrimRecipeData(template, base, addition);
break;
}
default: {
@ -160,13 +177,30 @@ public class ClientboundUpdateRecipesPacket implements MinecraftPacket {
break;
}
case SMITHING: {
SmithingRecipeData data = (SmithingRecipeData) recipe.getData();
LegacyUpgradeRecipeData data = (LegacyUpgradeRecipeData) recipe.getData();
helper.writeRecipeIngredient(out, data.getBase());
helper.writeRecipeIngredient(out, data.getAddition());
helper.writeItemStack(out, data.getResult());
break;
}
case SMITHING_TRANSFORM: {
SmithingTransformRecipeData data = (SmithingTransformRecipeData) recipe.getData();
helper.writeRecipeIngredient(out, data.getTemplate());
helper.writeRecipeIngredient(out, data.getBase());
helper.writeRecipeIngredient(out, data.getAddition());
helper.writeItemStack(out, data.getResult());
break;
}
case SMITHING_TRIM: {
SmithingTrimRecipeData data = (SmithingTrimRecipeData) recipe.getData();
helper.writeRecipeIngredient(out, data.getTemplate());
helper.writeRecipeIngredient(out, data.getBase());
helper.writeRecipeIngredient(out, data.getAddition());
break;
}
default: {
SimpleCraftingRecipeData data = (SimpleCraftingRecipeData) recipe.getData();

View file

@ -26,6 +26,6 @@ public class ClientboundAnimatePacket implements MinecraftPacket {
@Override
public void serialize(ByteBuf out, MinecraftCodecHelper helper) throws IOException {
helper.writeVarInt(out, this.entityId);
out.writeByte(this.animation.ordinal());
out.writeByte(this.animation.getId());
}
}

View file

@ -0,0 +1,44 @@
package com.github.steveice10.mc.protocol.packet.ingame.clientbound.entity;
import com.github.steveice10.mc.protocol.codec.MinecraftCodecHelper;
import com.github.steveice10.mc.protocol.codec.MinecraftPacket;
import com.nukkitx.math.vector.Vector3d;
import io.netty.buffer.ByteBuf;
import lombok.AllArgsConstructor;
import lombok.Data;
import org.jetbrains.annotations.Nullable;
@Data
@AllArgsConstructor
public class ClientboundDamageEventPacket implements MinecraftPacket {
private final int entityId;
private final int sourceTypeId;
private final int sourceCauseId;
private final int sourceDirectId;
private final @Nullable Vector3d sourcePosition;
public ClientboundDamageEventPacket(ByteBuf in, MinecraftCodecHelper helper) {
this.entityId = helper.readVarInt(in);
this.sourceTypeId = helper.readVarInt(in);
this.sourceCauseId = helper.readVarInt(in) - 1;
this.sourceDirectId = helper.readVarInt(in) - 1;
this.sourcePosition = in.readBoolean() ? Vector3d.from(in.readDouble(), in.readDouble(), in.readDouble()) : null;
}
@Override
public void serialize(ByteBuf out, MinecraftCodecHelper helper) {
helper.writeVarInt(out, this.entityId);
helper.writeVarInt(out, this.sourceTypeId);
helper.writeVarInt(out, this.sourceCauseId + 1);
helper.writeVarInt(out, this.sourceDirectId + 1);
if (this.sourcePosition != null) {
out.writeBoolean(true);
out.writeDouble(this.sourcePosition.getX());
out.writeDouble(this.sourcePosition.getY());
out.writeDouble(this.sourcePosition.getZ());
} else {
out.writeBoolean(false);
}
}
}

View file

@ -0,0 +1,27 @@
package com.github.steveice10.mc.protocol.packet.ingame.clientbound.entity;
import com.github.steveice10.mc.protocol.codec.MinecraftCodecHelper;
import com.github.steveice10.mc.protocol.codec.MinecraftPacket;
import io.netty.buffer.ByteBuf;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.With;
@Data
@With
@AllArgsConstructor
public class ClientboundHurtAnimationPacket implements MinecraftPacket {
private final int id;
private final float yaw;
public ClientboundHurtAnimationPacket(ByteBuf in, MinecraftCodecHelper helper) {
this.id = helper.readVarInt(in);
this.yaw = in.readFloat();
}
@Override
public void serialize(ByteBuf out, MinecraftCodecHelper helper) {
helper.writeVarInt(out, this.id);
out.writeFloat(this.yaw);
}
}

View file

@ -24,11 +24,10 @@ public class ClientboundPlayerPositionPacket implements MinecraftPacket {
private final float yaw;
private final float pitch;
private final int teleportId;
private final boolean dismountVehicle;
private final @NonNull List<PositionElement> relative;
public ClientboundPlayerPositionPacket(double x, double y, double z, float yaw, float pitch, int teleportId, boolean dismountVehicle, PositionElement... relative) {
this(x, y, z, yaw, pitch, teleportId, dismountVehicle, Arrays.asList(relative != null ? relative : new PositionElement[0]));
public ClientboundPlayerPositionPacket(double x, double y, double z, float yaw, float pitch, int teleportId, PositionElement... relative) {
this(x, y, z, yaw, pitch, teleportId, Arrays.asList(relative != null ? relative : new PositionElement[0]));
}
public ClientboundPlayerPositionPacket(ByteBuf in, MinecraftCodecHelper helper) throws IOException {
@ -48,7 +47,6 @@ public class ClientboundPlayerPositionPacket implements MinecraftPacket {
}
this.teleportId = helper.readVarInt(in);
this.dismountVehicle = in.readBoolean();
}
@Override
@ -67,6 +65,5 @@ public class ClientboundPlayerPositionPacket implements MinecraftPacket {
out.writeByte(flags);
helper.writeVarInt(out, this.teleportId);
out.writeBoolean(this.dismountVehicle);
}
}

View file

@ -40,7 +40,10 @@ public class ClientboundBlockEventPacket implements MinecraftPacket {
this.blockId = helper.readVarInt(in);
// TODO: Handle this in MinecraftCodecHelper
if (this.blockId == STICKY_PISTON || this.blockId == PISTON) {
if (this.blockId == NOTE_BLOCK) {
this.type = NoteBlockValueType.from(type);
this.value = new NoteBlockValue();
} else if (this.blockId == STICKY_PISTON || this.blockId == PISTON) {
this.type = PistonValueType.from(type);
this.value = new PistonValue(Direction.from(Math.abs((value & 7) % 6)));
} else if (this.blockId == MOB_SPAWNER) {

View file

@ -0,0 +1,37 @@
package com.github.steveice10.mc.protocol.packet.ingame.clientbound.level;
import com.github.steveice10.mc.protocol.codec.MinecraftCodecHelper;
import com.github.steveice10.mc.protocol.codec.MinecraftPacket;
import com.github.steveice10.mc.protocol.data.game.chunk.ChunkBiomeData;
import io.netty.buffer.ByteBuf;
import lombok.AllArgsConstructor;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
@Data
@AllArgsConstructor
public class ClientboundChunksBiomesPacket implements MinecraftPacket {
private final List<ChunkBiomeData> chunkBiomeData;
public ClientboundChunksBiomesPacket(ByteBuf in, MinecraftCodecHelper helper) {
this.chunkBiomeData = new ArrayList<>();
int length = helper.readVarInt(in);
for (int i = 0; i < length; i++) {
long raw = in.readLong();
this.chunkBiomeData.add(new ChunkBiomeData((int)raw, (int)(raw >> 32), helper.readByteArray(in)));
}
}
@Override
public void serialize(ByteBuf out, MinecraftCodecHelper helper) {
helper.writeVarInt(out, this.chunkBiomeData.size());
for (ChunkBiomeData entry : this.chunkBiomeData) {
long raw = (long)entry.getX() & 0xFFFFFFFFL | ((long)entry.getZ() & 0xFFFFFFFFL) << 32;
out.writeLong(raw);
helper.writeByteArray(out, entry.getBuffer());
}
}
}

View file

@ -62,7 +62,7 @@ public class ClientboundSectionBlocksUpdatePacket implements MinecraftPacket {
helper.writeVarInt(out, this.entries.length);
for (BlockChangeEntry entry : this.entries) {
short position = (short) ((entry.getPosition().getX() - (this.chunkX << 4)) << 8 | (entry.getPosition().getZ() - (this.chunkZ << 4)) << 4 | (entry.getPosition().getY() - (this.chunkY << 4)));
helper.writeVarLong(out, (long) entry.getBlock() << 12 | position);
helper.writeVarLong(out, (long) entry.getBlock() << 12 | (long) position);
}
}
}

View file

@ -2,7 +2,6 @@ package com.github.steveice10.mc.protocol.packet.ingame.clientbound.scoreboard;
import com.github.steveice10.mc.protocol.codec.MinecraftCodecHelper;
import com.github.steveice10.mc.protocol.codec.MinecraftPacket;
import com.github.steveice10.mc.protocol.data.DefaultComponentSerializer;
import com.github.steveice10.mc.protocol.data.game.scoreboard.CollisionRule;
import com.github.steveice10.mc.protocol.data.game.scoreboard.NameTagVisibility;
import com.github.steveice10.mc.protocol.data.game.scoreboard.TeamAction;
@ -11,6 +10,7 @@ import io.netty.buffer.ByteBuf;
import lombok.*;
import net.kyori.adventure.text.Component;
import javax.annotation.Nullable;
import java.io.IOException;
import java.util.Arrays;
@ -26,8 +26,8 @@ public class ClientboundSetPlayerTeamPacket implements MinecraftPacket {
private final Component suffix;
private final boolean friendlyFire;
private final boolean seeFriendlyInvisibles;
private final NameTagVisibility nameTagVisibility;
private final CollisionRule collisionRule;
private final @Nullable NameTagVisibility nameTagVisibility;
private final @Nullable CollisionRule collisionRule;
private final TeamColor color;
private final String[] players;

View file

@ -21,6 +21,8 @@ import net.kyori.adventure.text.Component;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
@Data
@With
@ -30,24 +32,23 @@ public class ClientboundStatusResponsePacket implements MinecraftPacket {
public ClientboundStatusResponsePacket(ByteBuf in, MinecraftCodecHelper helper) throws IOException {
JsonObject obj = new Gson().fromJson(helper.readString(in), JsonObject.class);
JsonObject ver = obj.get("version").getAsJsonObject();
VersionInfo version = new VersionInfo(ver.get("name").getAsString(), ver.get("protocol").getAsInt());
JsonElement desc = obj.get("description");
Component description = DefaultComponentSerializer.get().serializer().fromJson(desc, Component.class);
JsonObject plrs = obj.get("players").getAsJsonObject();
GameProfile[] profiles = new GameProfile[0];
List<GameProfile> profiles = new ArrayList<>();
if (plrs.has("sample")) {
JsonArray prof = plrs.get("sample").getAsJsonArray();
if (prof.size() > 0) {
profiles = new GameProfile[prof.size()];
for (int index = 0; index < prof.size(); index++) {
JsonObject o = prof.get(index).getAsJsonObject();
profiles[index] = new GameProfile(o.get("id").getAsString(), o.get("name").getAsString());
profiles.add(new GameProfile(o.get("id").getAsString(), o.get("name").getAsString()));
}
}
}
PlayerInfo players = new PlayerInfo(plrs.get("max").getAsInt(), plrs.get("online").getAsInt(), profiles);
JsonElement desc = obj.get("description");
Component description = DefaultComponentSerializer.get().serializer().fromJson(desc, Component.class);
JsonObject ver = obj.get("version").getAsJsonObject();
VersionInfo version = new VersionInfo(ver.get("name").getAsString(), ver.get("protocol").getAsInt());
byte[] icon = null;
if (obj.has("favicon")) {
icon = this.stringToIcon(obj.get("favicon").getAsString());
@ -66,7 +67,7 @@ public class ClientboundStatusResponsePacket implements MinecraftPacket {
JsonObject plrs = new JsonObject();
plrs.addProperty("max", this.info.getPlayerInfo().getMaxPlayers());
plrs.addProperty("online", this.info.getPlayerInfo().getOnlinePlayers());
if (this.info.getPlayerInfo().getPlayers().length > 0) {
if (this.info.getPlayerInfo().getPlayers().size() > 0) {
JsonArray array = new JsonArray();
for (GameProfile profile : this.info.getPlayerInfo().getPlayers()) {
JsonObject o = new JsonObject();
@ -78,9 +79,9 @@ public class ClientboundStatusResponsePacket implements MinecraftPacket {
plrs.add("sample", array);
}
obj.add("version", ver);
obj.add("players", plrs);
obj.add("description", new Gson().fromJson(DefaultComponentSerializer.get().serialize(this.info.getDescription()), JsonElement.class));
obj.add("players", plrs);
obj.add("version", ver);
if (this.info.getIconPng() != null) {
obj.addProperty("favicon", this.iconToString(this.info.getIconPng()));
}

View file

@ -0,0 +1,171 @@
package com.github.steveice10.packetlib;
import com.github.steveice10.packetlib.event.server.*;
import com.github.steveice10.packetlib.packet.PacketProtocol;
import java.util.*;
import java.util.function.Supplier;
public abstract class AbstractServer implements Server {
private final String host;
private final int port;
private final Supplier<? extends PacketProtocol> protocolSupplier;
private final List<Session> sessions = new ArrayList<>();
private final Map<String, Object> flags = new HashMap<>();
private final List<ServerListener> listeners = new ArrayList<>();
public AbstractServer(String host, int port, Supplier<? extends PacketProtocol> protocolSupplier) {
this.host = host;
this.port = port;
this.protocolSupplier = protocolSupplier;
}
@Override
public String getHost() {
return this.host;
}
@Override
public int getPort() {
return this.port;
}
@Override
public Supplier<? extends PacketProtocol> getPacketProtocol() {
return this.protocolSupplier;
}
protected PacketProtocol createPacketProtocol() {
return this.protocolSupplier.get();
}
@Override
public Map<String, Object> getGlobalFlags() {
return Collections.unmodifiableMap(this.flags);
}
@Override
public boolean hasGlobalFlag(String key) {
return this.flags.containsKey(key);
}
@Override
public <T> T getGlobalFlag(String key) {
return this.getGlobalFlag(key, null);
}
@SuppressWarnings("unchecked")
@Override
public <T> T getGlobalFlag(String key, T def) {
Object value = this.flags.get(key);
if(value == null) {
return def;
}
try {
return (T) value;
} catch(ClassCastException e) {
throw new IllegalStateException("Tried to get flag \"" + key + "\" as the wrong type. Actual type: " + value.getClass().getName());
}
}
@Override
public void setGlobalFlag(String key, Object value) {
this.flags.put(key, value);
}
@Override
public List<ServerListener> getListeners() {
return Collections.unmodifiableList(this.listeners);
}
@Override
public void addListener(ServerListener listener) {
this.listeners.add(listener);
}
@Override
public void removeListener(ServerListener listener) {
this.listeners.remove(listener);
}
protected void callEvent(ServerEvent event) {
for(ServerListener listener : this.listeners) {
event.call(listener);
}
}
@Override
public List<Session> getSessions() {
return new ArrayList<>(this.sessions);
}
public void addSession(Session session) {
this.sessions.add(session);
this.callEvent(new SessionAddedEvent(this, session));
}
public void removeSession(Session session) {
this.sessions.remove(session);
if(session.isConnected()) {
session.disconnect("Connection closed.");
}
this.callEvent(new SessionRemovedEvent(this, session));
}
@Override
public AbstractServer bind() {
return this.bind(true);
}
@Override
public AbstractServer bind(boolean wait) {
return this.bind(wait, null);
}
@Override
public AbstractServer bind(boolean wait, Runnable callback) {
this.bindImpl(wait, () -> {
callEvent(new ServerBoundEvent(AbstractServer.this));
if(callback != null) {
callback.run();
}
});
return this;
}
protected abstract void bindImpl(boolean wait, Runnable callback);
@Override
public void close() {
this.close(true);
}
@Override
public void close(boolean wait) {
this.close(wait, null);
}
@Override
public void close(boolean wait, Runnable callback) {
this.callEvent(new ServerClosingEvent(this));
for(Session session : this.getSessions()) {
if(session.isConnected()) {
session.disconnect("Server closed.");
}
}
this.closeImpl(wait, () -> {
callEvent(new ServerClosedEvent(AbstractServer.this));
if(callback != null) {
callback.run();
}
});
}
protected abstract void closeImpl(boolean wait, Runnable callback);
}

View file

@ -0,0 +1,23 @@
package com.github.steveice10.packetlib;
/**
* Built-in PacketLib session flags.
*/
public class BuiltinFlags {
/**
* When set to true, enables printing internal debug messages.
*/
public static final String PRINT_DEBUG = "print-packetlib-debug";
public static final String ENABLE_CLIENT_PROXY_PROTOCOL = "enable-client-proxy-protocol";
public static final String CLIENT_PROXIED_ADDRESS = "client-proxied-address";
/**
* When set to false, an SRV record resolve is not attempted.
*/
public static final String ATTEMPT_SRV_RESOLVE = "attempt-srv-resolve";
private BuiltinFlags() {
}
}

View file

@ -0,0 +1,106 @@
package com.github.steveice10.packetlib;
import java.net.SocketAddress;
/**
* Information describing a network proxy.
*/
public class ProxyInfo {
private Type type;
private SocketAddress address;
private boolean authenticated;
private String username;
private String password;
/**
* Creates a new unauthenticated ProxyInfo instance.
*
* @param type Type of proxy.
* @param address Network address of the proxy.
*/
public ProxyInfo(Type type, SocketAddress address) {
this.type = type;
this.address = address;
this.authenticated = false;
}
/**
* Creates a new authenticated ProxyInfo instance.
*
* @param type Type of proxy.
* @param address Network address of the proxy.
* @param username Username to authenticate with.
* @param password Password to authenticate with.
*/
public ProxyInfo(Type type, SocketAddress address, String username, String password) {
this(type, address);
this.authenticated = true;
this.username = username;
this.password = password;
}
/**
* Gets the proxy's type.
*
* @return The proxy's type.
*/
public Type getType() {
return this.type;
}
/**
* Gets the proxy's network address.
*
* @return The proxy's network address.
*/
public SocketAddress getAddress() {
return this.address;
}
/**
* Gets whether the proxy is authenticated with.
*
* @return Whether to authenticate with the proxy.
*/
public boolean isAuthenticated() {
return this.authenticated;
}
/**
* Gets the proxy's authentication username.
*
* @return The username to authenticate with.
*/
public String getUsername() {
return this.username;
}
/**
* Gets the proxy's authentication password.
*
* @return The password to authenticate with.
*/
public String getPassword() {
return this.password;
}
/**
* Supported proxy types.
*/
public enum Type {
/**
* HTTP proxy.
*/
HTTP,
/**
* SOCKS4 proxy.
*/
SOCKS4,
/**
* SOCKS5 proxy.
*/
SOCKS5;
}
}

View file

@ -0,0 +1,156 @@
package com.github.steveice10.packetlib;
import com.github.steveice10.packetlib.event.server.ServerListener;
import com.github.steveice10.packetlib.packet.PacketProtocol;
import java.util.List;
import java.util.Map;
import java.util.function.Supplier;
/**
* Listens for new sessions to connect.
*/
public interface Server {
/**
* Gets the host the session is listening on.
*
* @return The listening host.
*/
String getHost();
/**
* Gets the port the session is listening on.
*
* @return The listening port.
*/
int getPort();
/**
* Gets the packet protocol of the server.
*
* @return The server's packet protocol.
*/
Supplier<? extends PacketProtocol> getPacketProtocol();
/**
* Returns true if the listener is listening.
*
* @return True if the listener is listening.
*/
boolean isListening();
/**
* Gets this server's set flags.
*
* @return This server's flags.
*/
Map<String, Object> getGlobalFlags();
/**
* Checks whether this server has a flag set.
*
* @param key Key of the flag to check for.
* @return Whether this server has a flag set.
*/
boolean hasGlobalFlag(String key);
/**
* Gets the value of the given flag as an instance of the given type.
*
* @param <T> Type of the flag.
* @param key Key of the flag.
* @return Value of the flag.
* @throws IllegalStateException If the flag's value isn't of the required type.
*/
<T> T getGlobalFlag(String key);
/**
* Gets the value of the given flag as an instance of the given type.
* If the flag is not set, the specified default value will be returned.
*
* @param <T> Type of the flag.
* @param key Key of the flag.
* @param def Default value of the flag.
* @return Value of the flag.
* @throws IllegalStateException If the flag's value isn't of the required type.
*/
@SuppressWarnings("unchecked")
<T> T getGlobalFlag(String key, T def);
/**
* Sets the value of a flag. The flag will be used in sessions if a session does
* not contain a value for the flag.
*
* @param key Key of the flag.
* @param value Value to set the flag to.
*/
void setGlobalFlag(String key, Object value);
/**
* Gets the listeners listening on this session.
*
* @return This server's listeners.
*/
List<ServerListener> getListeners();
/**
* Adds a listener to this server.
*
* @param listener Listener to add.
*/
void addListener(ServerListener listener);
/**
* Removes a listener from this server.
*
* @param listener Listener to remove.
*/
void removeListener(ServerListener listener);
/**
* Gets all sessions belonging to this server.
*
* @return Sessions belonging to this server.
*/
List<Session> getSessions();
/**
* Binds the listener to its host and port.
*/
AbstractServer bind();
/**
* Binds the listener to its host and port.
*
* @param wait Whether to wait for the listener to finish binding.
*/
AbstractServer bind(boolean wait);
/**
* Binds the listener to its host and port.
*
* @param wait Whether to wait for the listener to finish binding.
* @param callback Callback to call when the listener has finished binding.
*/
AbstractServer bind(boolean wait, Runnable callback);
/**
* Closes the listener.
*/
void close();
/**
* Closes the listener.
*
* @param wait Whether to wait for the listener to finish closing.
*/
void close(boolean wait);
/**
* Closes the listener.
*
* @param wait Whether to wait for the listener to finish closing.
* @param callback Callback to call when the listener has finished closing.
*/
void close(boolean wait, Runnable callback);
}

View file

@ -0,0 +1,275 @@
package com.github.steveice10.packetlib;
import com.github.steveice10.packetlib.codec.PacketCodecHelper;
import com.github.steveice10.packetlib.crypt.PacketEncryption;
import com.github.steveice10.packetlib.event.session.SessionEvent;
import com.github.steveice10.packetlib.event.session.SessionListener;
import com.github.steveice10.packetlib.packet.Packet;
import com.github.steveice10.packetlib.packet.PacketProtocol;
import net.kyori.adventure.text.Component;
import org.jetbrains.annotations.Nullable;
import java.net.SocketAddress;
import java.util.List;
import java.util.Map;
/**
* A network session.
*/
public interface Session {
/**
* Connects this session to its host and port.
*/
public void connect();
/**
* Connects this session to its host and port.
*
* @param wait Whether to wait for the connection to be established before returning.
*/
public void connect(boolean wait);
/**
* Gets the host the session is connected to.
*
* @return The connected host.
*/
public String getHost();
/**
* Gets the port the session is connected to.
*
* @return The connected port.
*/
public int getPort();
/**
* Gets the local address of the session.
*
* @return The local address, or null if the session is not connected.
*/
public SocketAddress getLocalAddress();
/**
* Gets the remote address of the session.
*
* @return The remote address, or null if the session is not connected.
*/
public SocketAddress getRemoteAddress();
/**
* Gets the packet protocol of the session.
*
* @return The session's packet protocol.
*/
public PacketProtocol getPacketProtocol();
/**
* Gets the session's {@link PacketCodecHelper}.
*
* @return The session's packet codec helper.
*/
PacketCodecHelper getCodecHelper();
/**
* Gets this session's set flags. If this session belongs to a server, the server's
* flags will be included in the results.
*
* @return This session's flags.
*/
public Map<String, Object> getFlags();
/**
* Checks whether this session has a flag set. If this session belongs to a server,
* the server's flags will also be checked.
*
* @param key Key of the flag to check for.
* @return Whether this session has a flag set.
*/
public boolean hasFlag(String key);
/**
* Gets the value of the given flag as an instance of the given type. If this
* session belongs to a server, the server's flags will be checked for the flag
* as well.
*
* @param <T> Type of the flag.
* @param key Key of the flag.
* @return Value of the flag.
* @throws IllegalStateException If the flag's value isn't of the required type.
*/
public <T> T getFlag(String key);
/**
* Gets the value of the given flag as an instance of the given type. If this
* session belongs to a server, the server's flags will be checked for the flag
* as well. If the flag is not set, the specified default value will be returned.
*
* @param <T> Type of the flag.
* @param key Key of the flag.
* @param def Default value of the flag.
* @return Value of the flag.
* @throws IllegalStateException If the flag's value isn't of the required type.
*/
public <T> T getFlag(String key, T def);
/**
* Sets the value of a flag. This does not change a server's flags if this session
* belongs to a server.
*
* @param key Key of the flag.
* @param value Value to set the flag to.
*/
public void setFlag(String key, Object value);
/**
* Gets the listeners listening on this session.
*
* @return This session's listeners.
*/
public List<SessionListener> getListeners();
/**
* Adds a listener to this session.
*
* @param listener Listener to add.
*/
public void addListener(SessionListener listener);
/**
* Removes a listener from this session.
*
* @param listener Listener to remove.
*/
public void removeListener(SessionListener listener);
/**
* Calls an event on the listeners of this session.
*
* @param event Event to call.
*/
void callEvent(SessionEvent event);
/**
* Notifies all listeners that a packet was just received.
*
* @param packet Packet to notify.
*/
void callPacketReceived(Packet packet);
/**
* Notifies all listeners that a packet was just sent.
*
* @param packet Packet to notify.
*/
void callPacketSent(Packet packet);
/**
* Gets the compression packet length threshold for this session (-1 = disabled).
*
* @return This session's compression threshold.
*/
int getCompressionThreshold();
/**
* Sets the compression packet length threshold for this session (-1 = disabled).
*
* @param threshold The new compression threshold.
* @param validateDecompression whether to validate that the decompression fits within size checks.
*/
void setCompressionThreshold(int threshold, boolean validateDecompression);
/**
* Enables encryption for this session.
*
* @param encryption the encryption to encrypt with
*/
void enableEncryption(PacketEncryption encryption);
/**
* Gets the connect timeout for this session in seconds.
*
* @return The session's connect timeout.
*/
public int getConnectTimeout();
/**
* Sets the connect timeout for this session in seconds.
*
* @param timeout Connect timeout to set.
*/
public void setConnectTimeout(int timeout);
/**
* Gets the read timeout for this session in seconds.
*
* @return The session's read timeout.
*/
public int getReadTimeout();
/**
* Sets the read timeout for this session in seconds.
*
* @param timeout Read timeout to set.
*/
public void setReadTimeout(int timeout);
/**
* Gets the write timeout for this session in seconds.
*
* @return The session's write timeout.
*/
public int getWriteTimeout();
/**
* Sets the write timeout for this session in seconds.
*
* @param timeout Write timeout to set.
*/
public void setWriteTimeout(int timeout);
/**
* Returns true if the session is connected.
*
* @return True if the session is connected.
*/
public boolean isConnected();
/**
* Sends a packet.
*
* @param packet Packet to send.
*/
public void send(Packet packet);
/**
* Disconnects the session.
*
* @param reason Reason for disconnecting.
*/
void disconnect(@Nullable String reason);
/**
* Disconnects the session.
*
* @param reason Reason for disconnecting.
* @param cause Throwable responsible for disconnecting.
*/
void disconnect(@Nullable String reason, Throwable cause);
/**
* Disconnects the session.
*
* @param reason Reason for disconnecting.
*/
void disconnect(@Nullable Component reason);
/**
* Disconnects the session.
*
* @param reason Reason for disconnecting.
* @param cause Throwable responsible for disconnecting.
*/
void disconnect(@Nullable Component reason, Throwable cause);
}

View file

@ -0,0 +1,166 @@
package com.github.steveice10.packetlib.codec;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import java.nio.charset.StandardCharsets;
public class BasePacketCodecHelper implements PacketCodecHelper {
@Override
public void writeVarInt(ByteBuf buf, int value) {
this.writeVarLong(buf, value & 0xFFFFFFFFL);
}
@Override
public int readVarInt(ByteBuf buf) {
int value = 0;
int size = 0;
int b;
while (((b = buf.readByte()) & 0x80) == 0x80) {
value |= (b & 0x7F) << (size++ * 7);
if (size > 5) {
throw new IllegalArgumentException("VarInt too long (length must be <= 5)");
}
}
return value | ((b & 0x7F) << (size * 7));
}
// Based off of Andrew Steinborn's blog post:
// https://steinborn.me/posts/performance/how-fast-can-you-write-a-varint/
@Override
public void writeVarLong(ByteBuf buf, long value) {
// Peel the one and two byte count cases explicitly as they are the most common VarInt sizes
// that the server will write, to improve inlining.
if ((value & ~0x7FL) == 0) {
buf.writeByte((byte) value);
} else if ((value & ~0x3FFFL) == 0) {
int w = (int) ((value & 0x7FL | 0x80L) << 8 |
(value >>> 7));
buf.writeShort(w);
} else {
writeVarLongFull(buf, value);
}
}
private static void writeVarLongFull(ByteBuf buf, long value) {
if ((value & ~0x7FL) == 0) {
buf.writeByte((byte) value);
} else if ((value & ~0x3FFFL) == 0) {
int w = (int) ((value & 0x7FL | 0x80L) << 8 |
(value >>> 7));
buf.writeShort(w);
} else if ((value & ~0x1FFFFFL) == 0) {
int w = (int) ((value & 0x7FL | 0x80L) << 16 |
((value >>> 7) & 0x7FL | 0x80L) << 8 |
(value >>> 14));
buf.writeMedium(w);
} else if ((value & ~0xFFFFFFFL) == 0) {
int w = (int) ((value & 0x7F | 0x80) << 24 |
(((value >>> 7) & 0x7F | 0x80) << 16) |
((value >>> 14) & 0x7F | 0x80) << 8 |
(value >>> 21));
buf.writeInt(w);
} else if ((value & ~0x7FFFFFFFFL) == 0) {
int w = (int) ((value & 0x7F | 0x80) << 24 |
((value >>> 7) & 0x7F | 0x80) << 16 |
((value >>> 14) & 0x7F | 0x80) << 8 |
((value >>> 21) & 0x7F | 0x80));
buf.writeInt(w);
buf.writeByte((int) (value >>> 28));
} else if ((value & ~0x3FFFFFFFFFFL) == 0) {
int w = (int) ((value & 0x7F | 0x80) << 24 |
((value >>> 7) & 0x7F | 0x80) << 16 |
((value >>> 14) & 0x7F | 0x80) << 8 |
((value >>> 21) & 0x7F | 0x80));
int w2 = (int) (((value >>> 28) & 0x7FL | 0x80L) << 8 |
(value >>> 35));
buf.writeInt(w);
buf.writeShort(w2);
} else if ((value & ~0x1FFFFFFFFFFFFL) == 0) {
int w = (int) ((value & 0x7F | 0x80) << 24 |
((value >>> 7) & 0x7F | 0x80) << 16 |
((value >>> 14) & 0x7F | 0x80) << 8 |
((value >>> 21) & 0x7F | 0x80));
int w2 = (int) ((((value >>> 28) & 0x7FL | 0x80L) << 16 |
((value >>> 35) & 0x7FL | 0x80L) << 8) |
(value >>> 42));
buf.writeInt(w);
buf.writeMedium(w2);
} else if ((value & ~0xFFFFFFFFFFFFFFL) == 0) {
long w = (value & 0x7F | 0x80) << 56 |
((value >>> 7) & 0x7F | 0x80) << 48 |
((value >>> 14) & 0x7F | 0x80) << 40 |
((value >>> 21) & 0x7F | 0x80) << 32 |
((value >>> 28) & 0x7FL | 0x80L) << 24 |
((value >>> 35) & 0x7FL | 0x80L) << 16 |
((value >>> 42) & 0x7FL | 0x80L) << 8 |
(value >>> 49);
buf.writeLong(w);
} else if ((value & ~0x7FFFFFFFFFFFFFFFL) == 0) {
long w = (value & 0x7F | 0x80) << 56 |
((value >>> 7) & 0x7F | 0x80) << 48 |
((value >>> 14) & 0x7F | 0x80) << 40 |
((value >>> 21) & 0x7F | 0x80) << 32 |
((value >>> 28) & 0x7FL | 0x80L) << 24 |
((value >>> 35) & 0x7FL | 0x80L) << 16 |
((value >>> 42) & 0x7FL | 0x80L) << 8 |
(value >>> 49);
buf.writeLong(w);
buf.writeByte((byte) (value >>> 56));
} else {
long w = (value & 0x7F | 0x80) << 56 |
((value >>> 7) & 0x7F | 0x80) << 48 |
((value >>> 14) & 0x7F | 0x80) << 40 |
((value >>> 21) & 0x7F | 0x80) << 32 |
((value >>> 28) & 0x7FL | 0x80L) << 24 |
((value >>> 35) & 0x7FL | 0x80L) << 16 |
((value >>> 42) & 0x7FL | 0x80L) << 8 |
(value >>> 49);
int w2 = (int) (((value >>> 56) & 0x7FL | 0x80L) << 8 |
(value >>> 63));
buf.writeLong(w);
buf.writeShort(w2);
}
}
@Override
public long readVarLong(ByteBuf buf) {
int value = 0;
int size = 0;
int b;
while (((b = buf.readByte()) & 0x80) == 0x80) {
value |= (b & 0x7F) << (size++ * 7);
if (size > 10) {
throw new IllegalArgumentException("VarLong too long (length must be <= 10)");
}
}
return value | ((b & 0x7FL) << (size * 7));
}
public String readString(ByteBuf buf) {
return this.readString(buf, Short.MAX_VALUE);
}
@Override
public String readString(ByteBuf buf, int maxLength) {
int length = this.readVarInt(buf);
if (length > maxLength * 3) {
throw new IllegalArgumentException("String buffer is longer than maximum allowed length");
}
String string = (String) buf.readCharSequence(length, StandardCharsets.UTF_8);
if (string.length() > maxLength) {
throw new IllegalArgumentException("String is longer than maximum allowed length");
}
return string;
}
@Override
public void writeString(ByteBuf buf, String value) {
this.writeVarInt(buf, ByteBufUtil.utf8Bytes(value));
buf.writeCharSequence(value, StandardCharsets.UTF_8);
}
}

View file

@ -0,0 +1,20 @@
package com.github.steveice10.packetlib.codec;
import io.netty.buffer.ByteBuf;
public interface PacketCodecHelper {
void writeVarInt(ByteBuf buf, int value);
int readVarInt(ByteBuf buf);
void writeVarLong(ByteBuf buf, long value);
long readVarLong(ByteBuf buf);
String readString(ByteBuf buf);
String readString(ByteBuf buf, int maxLength);
void writeString(ByteBuf buf, String value);
}

View file

@ -0,0 +1,56 @@
package com.github.steveice10.packetlib.codec;
import com.github.steveice10.packetlib.packet.Packet;
import io.netty.buffer.ByteBuf;
import java.io.IOException;
/**
* Represents a definition of a packet with various
* information about it, such as it's id, class and
* factory for construction.
*
* @param <T> the packet type
*/
public class PacketDefinition<T extends Packet, H extends PacketCodecHelper> {
private final int id;
private final Class<T> packetClass;
private final PacketSerializer<T, H> serializer;
public PacketDefinition(final int id, final Class<T> packetClass, final PacketSerializer<T, H> serializer) {
this.id = id;
this.packetClass = packetClass;
this.serializer = serializer;
}
/**
* Returns the id of the packet.
*
* @return the id of the packet
*/
public int getId() {
return this.id;
}
/**
* Returns the class of the packet.
*
* @return the class of the packet
*/
public Class<T> getPacketClass() {
return this.packetClass;
}
/**
* Returns the {@link PacketSerializer} of the packet.
*
* @return the packet serializer of the packet
*/
public PacketSerializer<T, H> getSerializer() {
return this.serializer;
}
public T newInstance(ByteBuf buf, H helper) throws IOException {
return this.serializer.deserialize(buf, helper, this);
}
}

View file

@ -0,0 +1,13 @@
package com.github.steveice10.packetlib.codec;
import com.github.steveice10.packetlib.packet.Packet;
import io.netty.buffer.ByteBuf;
import java.io.IOException;
public interface PacketSerializer<T extends Packet, H extends PacketCodecHelper> {
void serialize(ByteBuf buf, H helper, T packet) throws IOException;
T deserialize(ByteBuf buf, H helper, PacketDefinition<T, H> definition) throws IOException;
}

View file

@ -0,0 +1,47 @@
package com.github.steveice10.packetlib.crypt;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import java.security.GeneralSecurityException;
import java.security.Key;
/**
* An encryption implementation using "AES/CFB8/NoPadding" encryption.
*/
public class AESEncryption implements PacketEncryption {
private Cipher inCipher;
private Cipher outCipher;
/**
* Creates a new AESEncryption instance.
*
* @param key Key to use when encrypting/decrypting data.
* @throws GeneralSecurityException If a security error occurs.
*/
public AESEncryption(Key key) throws GeneralSecurityException {
this.inCipher = Cipher.getInstance("AES/CFB8/NoPadding");
this.inCipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(key.getEncoded()));
this.outCipher = Cipher.getInstance("AES/CFB8/NoPadding");
this.outCipher.init(Cipher.ENCRYPT_MODE, key, new IvParameterSpec(key.getEncoded()));
}
@Override
public int getDecryptOutputSize(int length) {
return this.inCipher.getOutputSize(length);
}
@Override
public int getEncryptOutputSize(int length) {
return this.outCipher.getOutputSize(length);
}
@Override
public int decrypt(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset) throws Exception {
return this.inCipher.update(input, inputOffset, inputLength, output, outputOffset);
}
@Override
public int encrypt(byte[] input, int inputOffset, int inputLength, byte[] output, int outputOffset) throws Exception {
return this.outCipher.update(input, inputOffset, inputLength, output, outputOffset);
}
}

View file

@ -0,0 +1,48 @@
package com.github.steveice10.packetlib.crypt;
/**
* An interface for encrypting packets.
*/
public interface PacketEncryption {
/**
* Gets the output size from decrypting.
*
* @param length Length of the data being decrypted.
* @return The output size from decrypting.
*/
public int getDecryptOutputSize(int length);
/**
* Gets the output size from encrypting.
*
* @param length Length of the data being encrypted.
* @return The output size from encrypting.
*/
public int getEncryptOutputSize(int length);
/**
* Decrypts the given data.
*
* @param input Input data to decrypt.
* @param inputOffset Offset of the data to start decrypting at.
* @param inputLength Length of the data to be decrypted.
* @param output Array to output decrypted data to.
* @param outputOffset Offset of the output array to start at.
* @return The number of bytes stored in the output array.
* @throws Exception If an error occurs.
*/
public int decrypt(byte input[], int inputOffset, int inputLength, byte output[], int outputOffset) throws Exception;
/**
* Encrypts the given data.
*
* @param input Input data to encrypt.
* @param inputOffset Offset of the data to start encrypting at.
* @param inputLength Length of the data to be encrypted.
* @param output Array to output encrypted data to.
* @param outputOffset Offset of the output array to start at.
* @return The number of bytes stored in the output array.
* @throws Exception If an error occurs.
*/
public int encrypt(byte input[], int inputOffset, int inputLength, byte output[], int outputOffset) throws Exception;
}

View file

@ -0,0 +1,26 @@
package com.github.steveice10.packetlib.event.server;
/**
* An adapter for picking server events to listen for.
*/
public class ServerAdapter implements ServerListener {
@Override
public void serverBound(ServerBoundEvent event) {
}
@Override
public void serverClosing(ServerClosingEvent event) {
}
@Override
public void serverClosed(ServerClosedEvent event) {
}
@Override
public void sessionAdded(SessionAddedEvent event) {
}
@Override
public void sessionRemoved(SessionRemovedEvent event) {
}
}

View file

@ -0,0 +1,33 @@
package com.github.steveice10.packetlib.event.server;
import com.github.steveice10.packetlib.Server;
/**
* Called when the server is bound to its host and port.
*/
public class ServerBoundEvent implements ServerEvent {
private Server server;
/**
* Creates a new ServerBoundEvent instance.
*
* @param server Server being bound.
*/
public ServerBoundEvent(Server server) {
this.server = server;
}
/**
* Gets the server involved in this event.
*
* @return The event's server.
*/
public Server getServer() {
return this.server;
}
@Override
public void call(ServerListener listener) {
listener.serverBound(this);
}
}

View file

@ -0,0 +1,33 @@
package com.github.steveice10.packetlib.event.server;
import com.github.steveice10.packetlib.Server;
/**
* Called when the server is closed.
*/
public class ServerClosedEvent implements ServerEvent {
private Server server;
/**
* Creates a new ServerClosedEvent instance.
*
* @param server Server being closed.
*/
public ServerClosedEvent(Server server) {
this.server = server;
}
/**
* Gets the server involved in this event.
*
* @return The event's server.
*/
public Server getServer() {
return this.server;
}
@Override
public void call(ServerListener listener) {
listener.serverClosed(this);
}
}

View file

@ -0,0 +1,33 @@
package com.github.steveice10.packetlib.event.server;
import com.github.steveice10.packetlib.Server;
/**
* Called when the server is about to close.
*/
public class ServerClosingEvent implements ServerEvent {
private Server server;
/**
* Creates a new ServerClosingEvent instance.
*
* @param server Server being closed.
*/
public ServerClosingEvent(Server server) {
this.server = server;
}
/**
* Gets the server involved in this event.
*
* @return The event's server.
*/
public Server getServer() {
return this.server;
}
@Override
public void call(ServerListener listener) {
listener.serverClosing(this);
}
}

View file

@ -0,0 +1,13 @@
package com.github.steveice10.packetlib.event.server;
/**
* An event relating to servers.
*/
public interface ServerEvent {
/**
* Calls the event.
*
* @param listener Listener to call the event on.
*/
public void call(ServerListener listener);
}

View file

@ -0,0 +1,41 @@
package com.github.steveice10.packetlib.event.server;
/**
* A listener for listening to server events.
*/
public interface ServerListener {
/**
* Called when a server is bound to its host and port.
*
* @param event Data relating to the event.
*/
public void serverBound(ServerBoundEvent event);
/**
* Called when a server is about to close.
*
* @param event Data relating to the event.
*/
public void serverClosing(ServerClosingEvent event);
/**
* Called when a server is closed.
*
* @param event Data relating to the event.
*/
public void serverClosed(ServerClosedEvent event);
/**
* Called when a session is added to the server.
*
* @param event Data relating to the event.
*/
public void sessionAdded(SessionAddedEvent event);
/**
* Called when a session is removed and disconnected from the server.
*
* @param event Data relating to the event.
*/
public void sessionRemoved(SessionRemovedEvent event);
}

View file

@ -0,0 +1,46 @@
package com.github.steveice10.packetlib.event.server;
import com.github.steveice10.packetlib.Server;
import com.github.steveice10.packetlib.Session;
/**
* Called when a session is added to the server.
*/
public class SessionAddedEvent implements ServerEvent {
private Server server;
private Session session;
/**
* Creates a new SessionAddedEvent instance.
*
* @param server Server the session is being added to.
* @param session Session being added.
*/
public SessionAddedEvent(Server server, Session session) {
this.server = server;
this.session = session;
}
/**
* Gets the server involved in this event.
*
* @return The event's server.
*/
public Server getServer() {
return this.server;
}
/**
* Gets the session involved in this event.
*
* @return The event's session.
*/
public Session getSession() {
return this.session;
}
@Override
public void call(ServerListener listener) {
listener.sessionAdded(this);
}
}

View file

@ -0,0 +1,46 @@
package com.github.steveice10.packetlib.event.server;
import com.github.steveice10.packetlib.Server;
import com.github.steveice10.packetlib.Session;
/**
* Called when a session is removed and disconnected from the server.
*/
public class SessionRemovedEvent implements ServerEvent {
private Server server;
private Session session;
/**
* Creates a new SessionRemovedEvent instance.
*
* @param server Server the session is being removed from.
* @param session Session being removed.
*/
public SessionRemovedEvent(Server server, Session session) {
this.server = server;
this.session = session;
}
/**
* Gets the server involved in this event.
*
* @return The event's server.
*/
public Server getServer() {
return this.server;
}
/**
* Gets the session involved in this event.
*
* @return The event's session.
*/
public Session getSession() {
return this.session;
}
@Override
public void call(ServerListener listener) {
listener.sessionRemoved(this);
}
}

View file

@ -0,0 +1,33 @@
package com.github.steveice10.packetlib.event.session;
import com.github.steveice10.packetlib.Session;
/**
* Called when the session connects.
*/
public class ConnectedEvent implements SessionEvent {
private Session session;
/**
* Creates a new ConnectedEvent instance.
*
* @param session Session being connected.
*/
public ConnectedEvent(Session session) {
this.session = session;
}
/**
* Gets the session involved in this event.
*
* @return The event's session.
*/
public Session getSession() {
return this.session;
}
@Override
public void call(SessionListener listener) {
listener.connected(this);
}
}

View file

@ -0,0 +1,58 @@
package com.github.steveice10.packetlib.event.session;
import com.github.steveice10.packetlib.Session;
import net.kyori.adventure.text.Component;
/**
* Called when the session is disconnected.
*/
public class DisconnectedEvent implements SessionEvent {
private final Session session;
private final Component reason;
private final Throwable cause;
/**
* Creates a new DisconnectedEvent instance.
*
* @param session Session being disconnected.
* @param reason Reason for the session to disconnect.
* @param cause Throwable that caused the disconnect.
*/
public DisconnectedEvent(Session session, Component reason, Throwable cause) {
this.session = session;
this.reason = reason;
this.cause = cause;
}
/**
* Gets the session involved in this event.
*
* @return The event's session.
*/
public Session getSession() {
return this.session;
}
/**
* Gets the reason given for the session disconnecting.
*
* @return The event's reason.
*/
public Component getReason() {
return this.reason;
}
/**
* Gets the Throwable responsible for the session disconnecting.
*
* @return The Throwable responsible for the disconnect, or null if the disconnect was not caused by a Throwable.
*/
public Throwable getCause() {
return this.cause;
}
@Override
public void call(SessionListener listener) {
listener.disconnected(this);
}
}

View file

@ -0,0 +1,58 @@
package com.github.steveice10.packetlib.event.session;
import com.github.steveice10.packetlib.Session;
import net.kyori.adventure.text.Component;
/**
* Called when the session is about to disconnect.
*/
public class DisconnectingEvent implements SessionEvent {
private final Session session;
private final Component reason;
private final Throwable cause;
/**
* Creates a new DisconnectingEvent instance.
*
* @param session Session being disconnected.
* @param reason Reason for the session to disconnect.
* @param cause Throwable that caused the disconnect.
*/
public DisconnectingEvent(Session session, Component reason, Throwable cause) {
this.session = session;
this.reason = reason;
this.cause = cause;
}
/**
* Gets the session involved in this event.
*
* @return The event's session.
*/
public Session getSession() {
return this.session;
}
/**
* Gets the reason given for the session disconnecting.
*
* @return The event's reason.
*/
public Component getReason() {
return this.reason;
}
/**
* Gets the Throwable responsible for the session disconnecting.
*
* @return The Throwable responsible for the disconnect, or null if the disconnect was not caused by a Throwable.
*/
public Throwable getCause() {
return this.cause;
}
@Override
public void call(SessionListener listener) {
listener.disconnecting(this);
}
}

View file

@ -0,0 +1,68 @@
package com.github.steveice10.packetlib.event.session;
import com.github.steveice10.packetlib.Session;
/**
* Called when a session encounters an error while reading or writing packet data.
*/
public class PacketErrorEvent implements SessionEvent {
private Session session;
private Throwable cause;
private boolean suppress = false;
/**
* Creates a new SessionErrorEvent instance.
*
* @param session Session that the error came from.
* @param cause Cause of the error.
*/
public PacketErrorEvent(Session session, Throwable cause) {
this.session = session;
this.cause = cause;
}
/**
* Gets the session involved in this event.
*
* @return The event's session.
*/
public Session getSession() {
return this.session;
}
/**
* Gets the Throwable responsible for the error.
*
* @return The Throwable responsible for the error.
*/
public Throwable getCause() {
return this.cause;
}
/**
* Gets whether the error should be suppressed. If the error is not suppressed,
* it will be passed on through internal error handling and disconnect the session.
*
* The default value is false.
*
* @return Whether the error should be suppressed.
*/
public boolean shouldSuppress() {
return this.suppress;
}
/**
* Sets whether the error should be suppressed. If the error is not suppressed,
* it will be passed on through internal error handling and disconnect the session.
*
* @param suppress Whether the error should be suppressed.
*/
public void setSuppress(boolean suppress) {
this.suppress = suppress;
}
@Override
public void call(SessionListener listener) {
listener.packetError(this);
}
}

View file

@ -0,0 +1,81 @@
package com.github.steveice10.packetlib.event.session;
import com.github.steveice10.packetlib.Session;
import com.github.steveice10.packetlib.packet.Packet;
/**
* Called when the session is sending a packet.
*/
public class PacketSendingEvent implements SessionEvent {
private Session session;
private Packet packet;
private boolean cancelled = false;
/**
* Creates a new PacketSendingEvent instance.
*
* @param session Session sending the packet.
* @param packet Packet being sent.
*/
public PacketSendingEvent(Session session, Packet packet) {
this.session = session;
this.packet = packet;
}
/**
* Gets the session involved in this event.
*
* @return The event's session.
*/
public Session getSession() {
return this.session;
}
/**
* Gets the packet involved in this event as the required type.
*
* @param <T> Type of the packet.
* @return The event's packet as the required type.
* @throws IllegalStateException If the packet's value isn't of the required type.
*/
@SuppressWarnings("unchecked")
public <T extends Packet> T getPacket() {
try {
return (T) this.packet;
} catch(ClassCastException e) {
throw new IllegalStateException("Tried to get packet as the wrong type. Actual type: " + this.packet.getClass().getName());
}
}
/**
* Sets the packet that should be sent as a result of this event.
*
* @param packet The packet to send.
*/
public void setPacket(Packet packet) {
this.packet = packet;
}
/**
* Gets whether the event has been cancelled.
*
* @return Whether the event has been cancelled.
*/
public boolean isCancelled() {
return this.cancelled;
}
/**
* Sets whether the event should be cancelled.
*
* @param cancelled Whether the event should be cancelled.
*/
public void setCancelled(boolean cancelled) {
this.cancelled = cancelled;
}
@Override
public void call(SessionListener listener) {
listener.packetSending(this);
}
}

View file

@ -0,0 +1,37 @@
package com.github.steveice10.packetlib.event.session;
import com.github.steveice10.packetlib.Session;
import com.github.steveice10.packetlib.packet.Packet;
/**
* An adapter for picking session events to listen for.
*/
public class SessionAdapter implements SessionListener {
@Override
public void packetReceived(Session session, Packet packet) {
}
@Override
public void packetSending(PacketSendingEvent event) {
}
@Override
public void packetSent(Session session, Packet packet) {
}
@Override
public void packetError(PacketErrorEvent event) {
}
@Override
public void connected(ConnectedEvent event) {
}
@Override
public void disconnecting(DisconnectingEvent event) {
}
@Override
public void disconnected(DisconnectedEvent event) {
}
}

View file

@ -0,0 +1,13 @@
package com.github.steveice10.packetlib.event.session;
/**
* An event relating to sessions.
*/
public interface SessionEvent {
/**
* Calls the event.
*
* @param listener Listener to call the event on.
*/
public void call(SessionListener listener);
}

View file

@ -0,0 +1,58 @@
package com.github.steveice10.packetlib.event.session;
import com.github.steveice10.packetlib.Session;
import com.github.steveice10.packetlib.packet.Packet;
/**
* A listener for listening to session events.
*/
public interface SessionListener {
/**
* Called when a session receives a packet.
*
* @param packet the packet that was just received.
*/
void packetReceived(Session session, Packet packet);
/**
* Called when a session is sending a packet.
*
* @param event Data relating to the event.
*/
public void packetSending(PacketSendingEvent event);
/**
* Called when a session sends a packet.
*
* @param packet Packet just sent.
*/
void packetSent(Session session, Packet packet);
/**
* Called when a session encounters an error while reading or writing packet data.
*
* @param event Data relating to the event.
*/
public void packetError(PacketErrorEvent event);
/**
* Called when a session connects.
*
* @param event Data relating to the event.
*/
public void connected(ConnectedEvent event);
/**
* Called when a session is about to disconnect.
*
* @param event Data relating to the event.
*/
public void disconnecting(DisconnectingEvent event);
/**
* Called when a session is disconnected.
*
* @param event Data relating to the event.
*/
public void disconnected(DisconnectedEvent event);
}

View file

@ -0,0 +1,30 @@
package com.github.steveice10.packetlib.helper;
import io.netty.channel.epoll.Epoll;
import io.netty.channel.kqueue.KQueue;
import io.netty.incubator.channel.uring.IOUring;
public class TransportHelper {
public enum TransportMethod {
NIO, EPOLL, KQUEUE, IO_URING
}
public static TransportMethod determineTransportMethod() {
if (isClassAvailable("io.netty.incubator.channel.uring.IOUring") && IOUring.isAvailable()) return TransportMethod.IO_URING;
if (isClassAvailable("io.netty.channel.epoll.Epoll") && Epoll.isAvailable()) return TransportMethod.EPOLL;
if (isClassAvailable("io.netty.channel.kqueue.KQueue") && KQueue.isAvailable()) return TransportMethod.KQUEUE;
return TransportMethod.NIO;
}
/**
* Used so implementations can opt to remove these dependencies if so desired
*/
private static boolean isClassAvailable(String className) {
try {
Class.forName(className);
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
}

View file

@ -0,0 +1,38 @@
package com.github.steveice10.packetlib.packet;
import com.github.steveice10.packetlib.codec.PacketCodecHelper;
import com.github.steveice10.packetlib.codec.PacketDefinition;
import com.github.steveice10.packetlib.codec.PacketSerializer;
import io.netty.buffer.ByteBuf;
public class BufferedPacket implements Packet, PacketSerializer<BufferedPacket, PacketCodecHelper> {
private final Class<? extends Packet> packetClass;
private final byte[] buf;
public BufferedPacket(Class<? extends Packet> packetClass, byte[] buf) {
this.packetClass = packetClass;
this.buf = buf;
}
public Class<? extends Packet> getPacketClass() {
return packetClass;
}
@Override
public boolean isPriority() {
return true;
}
@Override
public void serialize(ByteBuf buf, PacketCodecHelper helper, BufferedPacket packet) {
buf.writeBytes(this.buf);
}
@Override
public BufferedPacket deserialize(ByteBuf buf, PacketCodecHelper helper, PacketDefinition<BufferedPacket, PacketCodecHelper> definition) {
byte[] array = new byte[buf.readableBytes()];
buf.readBytes(array);
return new BufferedPacket(definition.getPacketClass(), array);
}
}

View file

@ -0,0 +1,56 @@
package com.github.steveice10.packetlib.packet;
import com.github.steveice10.packetlib.codec.PacketCodecHelper;
import io.netty.buffer.ByteBuf;
import java.io.IOException;
/**
* The default packet header, using a varint packet length and id.
*/
public class DefaultPacketHeader implements PacketHeader {
@Override
public boolean isLengthVariable() {
return true;
}
@Override
public int getLengthSize() {
return 5;
}
@Override
public int getLengthSize(int length) {
if((length & -128) == 0) {
return 1;
} else if((length & -16384) == 0) {
return 2;
} else if((length & -2097152) == 0) {
return 3;
} else if((length & -268435456) == 0) {
return 4;
} else {
return 5;
}
}
@Override
public int readLength(ByteBuf buf, PacketCodecHelper codecHelper, int available) throws IOException {
return codecHelper.readVarInt(buf);
}
@Override
public void writeLength(ByteBuf buf, PacketCodecHelper codecHelper, int length) throws IOException {
codecHelper.writeVarInt(buf, length);
}
@Override
public int readPacketId(ByteBuf buf, PacketCodecHelper codecHelper) throws IOException {
return codecHelper.readVarInt(buf);
}
@Override
public void writePacketId(ByteBuf buf, PacketCodecHelper codecHelper, int packetId) throws IOException {
codecHelper.writeVarInt(buf, packetId);
}
}

View file

@ -0,0 +1,20 @@
package com.github.steveice10.packetlib.packet;
import io.netty.buffer.ByteBuf;
/**
* A network packet. Any given packet must have a constructor that takes in a {@link ByteBuf}.
*/
public interface Packet {
/**
* Gets whether the packet has handling priority.
* If the result is true, the packet will be handled immediately after being
* decoded.
*
* @return Whether the packet has priority.
*/
default boolean isPriority() {
return false;
}
}

View file

@ -0,0 +1,74 @@
package com.github.steveice10.packetlib.packet;
import com.github.steveice10.packetlib.codec.PacketCodecHelper;
import io.netty.buffer.ByteBuf;
import java.io.IOException;
/**
* The header of a protocol's packets.
*/
public interface PacketHeader {
/**
* Gets whether the header's length value can vary in size.
*
* @return Whether the header's length value can vary in size.
*/
public boolean isLengthVariable();
/**
* Gets the size of the header's length value.
*
* @return The length value's size.
*/
public int getLengthSize();
/**
* Gets the size of the header's length value.
*
* @param length Length value to get the size of.
* @return The length value's size.
*/
public int getLengthSize(int length);
/**
* Reads the length of a packet from the given input.
*
* @param buf Buffer to read from.
* @param codecHelper The codec helper.
* @param available Number of packet bytes available after the length.
* @return The resulting packet length.
* @throws java.io.IOException If an I/O error occurs.
*/
public int readLength(ByteBuf buf, PacketCodecHelper codecHelper, int available) throws IOException;
/**
* Writes the length of a packet to the given output.
*
* @param buf Buffer to write to.
* @param codecHelper The codec helper.
* @param length Length to write.
* @throws java.io.IOException If an I/O error occurs.
*/
public void writeLength(ByteBuf buf, PacketCodecHelper codecHelper, int length) throws IOException;
/**
* Reads the ID of a packet from the given input.
*
* @param buf Buffer to read from.
* @param codecHelper The codec helper.
* @return The resulting packet ID, or -1 if the packet should not be read yet.
* @throws java.io.IOException If an I/O error occurs.
*/
public int readPacketId(ByteBuf buf, PacketCodecHelper codecHelper) throws IOException;
/**
* Writes the ID of a packet to the given output.
*
* @param buf Buffer to write to.
* @param codecHelper The codec helper.
* @param packetId Packet ID to write.
* @throws java.io.IOException If an I/O error occurs.
*/
public void writePacketId(ByteBuf buf, PacketCodecHelper codecHelper, int packetId) throws IOException;
}

View file

@ -0,0 +1,303 @@
package com.github.steveice10.packetlib.packet;
import com.github.steveice10.packetlib.Server;
import com.github.steveice10.packetlib.Session;
import com.github.steveice10.packetlib.codec.PacketCodecHelper;
import com.github.steveice10.packetlib.codec.PacketDefinition;
import com.github.steveice10.packetlib.codec.PacketSerializer;
import io.netty.buffer.ByteBuf;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import java.io.IOException;
import java.util.HashMap;
import java.util.IdentityHashMap;
import java.util.Map;
/**
* A protocol for packet sending and receiving.
* All implementations must have a constructor that takes in a {@link ByteBuf}.
*/
public abstract class PacketProtocol {
private final Int2ObjectMap<PacketDefinition<? extends Packet, ?>> serverbound = new Int2ObjectOpenHashMap<>();
private final Int2ObjectMap<PacketDefinition<? extends Packet, ?>> clientbound = new Int2ObjectOpenHashMap<>();
private final Map<Class<? extends Packet>, Integer> clientboundIds = new IdentityHashMap<>();
private final Map<Class<? extends Packet>, Integer> serverboundIds = new IdentityHashMap<>();
/**
* Gets the prefix used when locating SRV records for this protocol.
*
* @return The protocol's SRV record prefix.
*/
public abstract String getSRVRecordPrefix();
/**
* Gets the packet header of this protocol.
*
* @return The protocol's packet header.
*/
public abstract PacketHeader getPacketHeader();
/**
* Creates a new {@link PacketCodecHelper} that can be used
* for each session.
*
* @return A new {@link PacketCodecHelper}.
*/
public abstract PacketCodecHelper createHelper();
/**
* Called when a client session is created with this protocol.
*
* @param session The created session.
*/
public abstract void newClientSession(Session session);
/**
* Called when a server session is created with this protocol.
*
* @param server The server that the session belongs to.
* @param session The created session.
*/
public abstract void newServerSession(Server server, Session session);
/**
* Clears all currently registered packets.
*/
public final void clearPackets() {
this.serverbound.clear();
this.clientbound.clear();
this.clientboundIds.clear();
this.serverboundIds.clear();
}
/**
* Registers a packet to this protocol as both serverbound and clientbound.
*
* @param id Id to register the packet to.
* @param packet Packet to register.
* @param serializer The packet serializer.
* @throws IllegalArgumentException If the packet fails a test creation when being registered as serverbound.
*/
public final <T extends Packet, H extends PacketCodecHelper> void register(int id, Class<T> packet, PacketSerializer<T, H> serializer) {
this.registerServerbound(id, packet, serializer);
this.registerClientbound(id, packet, serializer);
}
/**
* Registers a packet to this protocol as both serverbound and clientbound.
*
* @param definition The packet definition.
* @throws IllegalArgumentException If the packet fails a test creation when being registered as serverbound.
*/
public final void register(PacketDefinition<? extends Packet, ?> definition) {
this.registerServerbound(definition);
this.registerClientbound(definition);
}
/**
* Registers a serverbound packet to this protocol.
*
* @param id Id to register the packet to.
* @param packet Packet to register.
* @param serializer The packet serializer.
* @throws IllegalArgumentException If the packet fails a test creation.
*/
public final <T extends Packet, H extends PacketCodecHelper> void registerServerbound(int id, Class<T> packet, PacketSerializer<T, H> serializer) {
this.registerServerbound(new PacketDefinition<>(id, packet, serializer));
}
/**
* Registers a serverbound packet to this protocol.
*
* @param definition The packet definition.
*/
public final void registerServerbound(PacketDefinition<? extends Packet, ?> definition) {
this.serverbound.put(definition.getId(), definition);
this.serverboundIds.put(definition.getPacketClass(), definition.getId());
}
/**
* Registers a clientbound packet to this protocol.
*
* @param id Id to register the packet to.
* @param packet Packet to register.
* @param serializer The packet serializer.
*/
public final <T extends Packet, H extends PacketCodecHelper> void registerClientbound(int id, Class<T> packet, PacketSerializer<T, H> serializer) {
this.registerClientbound(new PacketDefinition<>(id, packet, serializer));
}
/**
* Registers a clientbound packet to this protocol.
*
* @param definition The packet definition.
*/
public final void registerClientbound(PacketDefinition<? extends Packet, ?> definition) {
this.clientbound.put(definition.getId(), definition);
this.clientboundIds.put(definition.getPacketClass(), definition.getId());
}
/**
* Creates a new instance of a clientbound packet with the given id and read the clientbound input.
*
* @param id Id of the packet to create.
* @param buf The buffer to read the packet from.
* @param codecHelper The codec helper.
* @return The created packet.
* @throws IOException if there was an IO error whilst reading the packet.
* @throws IllegalArgumentException If the packet ID is not registered.
*/
@SuppressWarnings("unchecked")
public <H extends PacketCodecHelper> Packet createClientboundPacket(int id, ByteBuf buf, H codecHelper) throws IOException {
PacketDefinition<?, H> definition = (PacketDefinition<?, H>) this.clientbound.get(id);
if (definition == null) {
throw new IllegalArgumentException("Invalid packet id: " + id);
}
return definition.newInstance(buf, codecHelper);
}
/**
* Gets the registered id of a clientbound packet class.
*
* @param packetClass Class of the packet to get the id for.
* @return The packet's registered id.
* @throws IllegalArgumentException If the packet is not registered.
*/
public int getClientboundId(Class<? extends Packet> packetClass) {
Integer packetId = this.clientboundIds.get(packetClass);
if(packetId == null) {
throw new IllegalArgumentException("Unregistered clientbound packet class: " + packetClass.getName());
}
return packetId;
}
/**
* Gets the registered id of a clientbound {@link Packet} instance.
*
* @param packet Instance of {@link Packet} to get the id for.
* @return The packet's registered id.
* @throws IllegalArgumentException If the packet is not registered.
*/
public int getClientboundId(Packet packet) {
if (packet instanceof BufferedPacket) {
return getClientboundId(((BufferedPacket) packet).getPacketClass());
}
return getClientboundId(packet.getClass());
}
/**
* Gets the packet class for a packet id.
* @param id The packet id.
* @return The registered packet's class
* @throws IllegalArgumentException If the packet ID is not registered.
*/
public Class<? extends Packet> getClientboundClass(int id) {
PacketDefinition<?, ?> definition = this.clientbound.get(id);
if (definition == null) {
throw new IllegalArgumentException("Invalid packet id: " + id);
}
return definition.getPacketClass();
}
/**
* Creates a new instance of a serverbound packet with the given id and read the serverbound input.
*
* @param id Id of the packet to create.
* @param buf The buffer to read the packet from.
* @param codecHelper The codec helper.
* @return The created packet.
* @throws IOException if there was an IO error whilst reading the packet.
* @throws IllegalArgumentException If the packet ID is not registered.
*/
@SuppressWarnings("unchecked")
public <H extends PacketCodecHelper> Packet createServerboundPacket(int id, ByteBuf buf, H codecHelper) throws IOException {
PacketDefinition<?, H> definition = (PacketDefinition<?, H>) this.serverbound.get(id);
if (definition == null) {
throw new IllegalArgumentException("Invalid packet id: " + id);
}
return definition.newInstance(buf, codecHelper);
}
/**
* Gets the registered id of a serverbound packet class.
*
* @param packetClass Class of the packet to get the id for.
* @return The packet's registered id.
* @throws IllegalArgumentException If the packet is not registered.
*/
public int getServerboundId(Class<? extends Packet> packetClass) {
Integer packetId = this.serverboundIds.get(packetClass);
if(packetId == null) {
throw new IllegalArgumentException("Unregistered serverbound packet class: " + packetClass.getName());
}
return packetId;
}
/**
* Gets the registered id of a serverbound {@link Packet} instance.
*
* @param packet Instance of {@link Packet} to get the id for.
* @return The packet's registered id.
* @throws IllegalArgumentException If the packet is not registered.
*/
public int getServerboundId(Packet packet) {
if (packet instanceof BufferedPacket) {
return getServerboundId(((BufferedPacket) packet).getPacketClass());
}
return getServerboundId(packet.getClass());
}
/**
* Gets the packet class for a packet id.
* @param id The packet id.
* @return The registered packet's class
* @throws IllegalArgumentException If the packet ID is not registered.
*/
public Class<? extends Packet> getServerboundClass(int id) {
PacketDefinition<?, ?> definition = this.serverbound.get(id);
if (definition == null) {
throw new IllegalArgumentException("Invalid packet id: " + id);
}
return definition.getPacketClass();
}
/**
* Gets the serverbound packet definition for the given packet id.
*
* @param id The packet id.
* @return The registered packet's class
*/
public PacketDefinition<?, ?> getServerboundDefinition(int id) {
PacketDefinition<?, ?> definition = this.serverbound.get(id);
if (definition == null) {
throw new IllegalArgumentException("Invalid packet id: " + id);
}
return definition;
}
/**
* Gets the clientbound packet definition for the given packet id.
*
* @param id The packet id.
* @return The registered packet's class
*/
public PacketDefinition<?, ?> getClientboundDefinition(int id) {
PacketDefinition<?, ?> definition = this.clientbound.get(id);
if (definition == null) {
throw new IllegalArgumentException("Invalid packet id: " + id);
}
return definition;
}
}

View file

@ -0,0 +1,30 @@
package com.github.steveice10.packetlib.tcp;
import com.github.steveice10.mc.protocol.codec.MinecraftPacket;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundBundlePacket;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.ClientboundDelimiterPacket;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageDecoder;
import java.util.ArrayList;
import java.util.List;
public class TcpBundlerUnpacker extends MessageToMessageDecoder<MinecraftPacket> {
private List<MinecraftPacket> currentPackets;
@Override
protected void decode(ChannelHandlerContext ctx, MinecraftPacket packet, List<Object> out) throws Exception {
if (currentPackets != null) {
if (packet.getClass() == ClientboundDelimiterPacket.class) {
out.add(new ClientboundBundlePacket(currentPackets));
currentPackets = null;
} else {
currentPackets.add(packet);
}
} else {
if (packet.getClass() == ClientboundDelimiterPacket.class) {
currentPackets = new ArrayList<>(2);
}
}
}
}

View file

@ -0,0 +1,321 @@
package com.github.steveice10.packetlib.tcp;
import com.github.steveice10.packetlib.BuiltinFlags;
import com.github.steveice10.packetlib.ProxyInfo;
import com.github.steveice10.packetlib.codec.PacketCodecHelper;
import com.github.steveice10.packetlib.helper.TransportHelper;
import com.github.steveice10.packetlib.packet.PacketProtocol;
import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.AddressedEnvelope;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.epoll.EpollDatagramChannel;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.DatagramChannel;
import io.netty.channel.*;
import io.netty.channel.kqueue.KQueueDatagramChannel;
import io.netty.channel.kqueue.KQueueEventLoopGroup;
import io.netty.channel.kqueue.KQueueSocketChannel;
import io.netty.channel.socket.nio.NioDatagramChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.dns.DefaultDnsQuestion;
import io.netty.handler.codec.dns.DefaultDnsRawRecord;
import io.netty.handler.codec.dns.DefaultDnsRecordDecoder;
import io.netty.handler.codec.dns.DnsRecordType;
import io.netty.handler.codec.dns.DnsResponse;
import io.netty.handler.codec.dns.DnsSection;
import io.netty.handler.codec.haproxy.HAProxyCommand;
import io.netty.handler.codec.haproxy.HAProxyMessage;
import io.netty.handler.codec.haproxy.HAProxyMessageEncoder;
import io.netty.handler.codec.haproxy.HAProxyProtocolVersion;
import io.netty.handler.codec.haproxy.HAProxyProxiedProtocol;
import io.netty.handler.proxy.HttpProxyHandler;
import io.netty.handler.proxy.Socks4ProxyHandler;
import io.netty.handler.proxy.Socks5ProxyHandler;
import io.netty.incubator.channel.uring.IOUringDatagramChannel;
import io.netty.incubator.channel.uring.IOUringEventLoopGroup;
import io.netty.incubator.channel.uring.IOUringSocketChannel;
import io.netty.resolver.dns.DnsNameResolver;
import io.netty.resolver.dns.DnsNameResolverBuilder;
import java.net.*;
public class TcpClientSession extends TcpSession {
private static final String IP_REGEX = "\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\b";
private static Class<? extends Channel> CHANNEL_CLASS;
private static Class<? extends DatagramChannel> DATAGRAM_CHANNEL_CLASS;
private static EventLoopGroup EVENT_LOOP_GROUP;
private final String bindAddress;
private final int bindPort;
private final ProxyInfo proxy;
private final PacketCodecHelper codecHelper;
public TcpClientSession(String host, int port, PacketProtocol protocol) {
this(host, port, protocol, null);
}
public TcpClientSession(String host, int port, PacketProtocol protocol, ProxyInfo proxy) {
this(host, port, "0.0.0.0", 0, protocol, proxy);
}
public TcpClientSession(String host, int port, String bindAddress, int bindPort, PacketProtocol protocol) {
this(host, port, bindAddress, bindPort, protocol, null);
}
public TcpClientSession(String host, int port, String bindAddress, int bindPort, PacketProtocol protocol, ProxyInfo proxy) {
super(host, port, protocol);
this.bindAddress = bindAddress;
this.bindPort = bindPort;
this.proxy = proxy;
this.codecHelper = protocol.createHelper();
}
@Override
public void connect(boolean wait) {
if(this.disconnected) {
throw new IllegalStateException("Session has already been disconnected.");
}
boolean debug = getFlag(BuiltinFlags.PRINT_DEBUG, false);
if (CHANNEL_CLASS == null) {
createTcpEventLoopGroup();
}
try {
final Bootstrap bootstrap = new Bootstrap();
bootstrap.channel(CHANNEL_CLASS);
bootstrap.handler(new ChannelInitializer<Channel>() {
@Override
public void initChannel(Channel channel) {
PacketProtocol protocol = getPacketProtocol();
protocol.newClientSession(TcpClientSession.this);
channel.config().setOption(ChannelOption.IP_TOS, 0x18);
try {
channel.config().setOption(ChannelOption.TCP_NODELAY, true);
} catch (ChannelException e) {
if(debug) {
System.out.println("Exception while trying to set TCP_NODELAY");
e.printStackTrace();
}
}
ChannelPipeline pipeline = channel.pipeline();
refreshReadTimeoutHandler(channel);
refreshWriteTimeoutHandler(channel);
addProxy(pipeline);
int size = protocol.getPacketHeader().getLengthSize();
if (size > 0) {
pipeline.addLast("sizer", new TcpPacketSizer(TcpClientSession.this, size));
}
pipeline.addLast("codec", new TcpPacketCodec(TcpClientSession.this, true));
pipeline.addLast("manager", TcpClientSession.this);
addHAProxySupport(pipeline);
}
}).group(EVENT_LOOP_GROUP).option(ChannelOption.CONNECT_TIMEOUT_MILLIS, getConnectTimeout() * 1000);
InetSocketAddress remoteAddress = resolveAddress();
bootstrap.remoteAddress(remoteAddress);
bootstrap.localAddress(bindAddress, bindPort);
ChannelFuture future = bootstrap.connect();
if (wait) {
future.sync();
}
future.addListener((futureListener) -> {
if (!futureListener.isSuccess()) {
exceptionCaught(null, futureListener.cause());
}
});
} catch(Throwable t) {
exceptionCaught(null, t);
}
}
@Override
public PacketCodecHelper getCodecHelper() {
return this.codecHelper;
}
private InetSocketAddress resolveAddress() {
boolean debug = getFlag(BuiltinFlags.PRINT_DEBUG, false);
String name = this.getPacketProtocol().getSRVRecordPrefix() + "._tcp." + this.getHost();
if (debug) {
System.out.println("[PacketLib] Attempting SRV lookup for \"" + name + "\".");
}
if(getFlag(BuiltinFlags.ATTEMPT_SRV_RESOLVE, true) && (!this.host.matches(IP_REGEX) && !this.host.equalsIgnoreCase("localhost"))) {
DnsNameResolver resolver = null;
AddressedEnvelope<DnsResponse, InetSocketAddress> envelope = null;
try {
resolver = new DnsNameResolverBuilder(EVENT_LOOP_GROUP.next())
.channelType(DATAGRAM_CHANNEL_CLASS)
.build();
envelope = resolver.query(new DefaultDnsQuestion(name, DnsRecordType.SRV)).get();
DnsResponse response = envelope.content();
if (response.count(DnsSection.ANSWER) > 0) {
DefaultDnsRawRecord record = response.recordAt(DnsSection.ANSWER, 0);
if (record.type() == DnsRecordType.SRV) {
ByteBuf buf = record.content();
buf.skipBytes(4); // Skip priority and weight.
int port = buf.readUnsignedShort();
String host = DefaultDnsRecordDecoder.decodeName(buf);
if (host.endsWith(".")) {
host = host.substring(0, host.length() - 1);
}
if(debug) {
System.out.println("[PacketLib] Found SRV record containing \"" + host + ":" + port + "\".");
}
this.host = host;
this.port = port;
} else if (debug) {
System.out.println("[PacketLib] Received non-SRV record in response.");
}
} else if (debug) {
System.out.println("[PacketLib] No SRV record found.");
}
} catch(Exception e) {
if (debug) {
System.out.println("[PacketLib] Failed to resolve SRV record.");
e.printStackTrace();
}
} finally {
if (envelope != null) {
envelope.release();
}
if (resolver != null) {
resolver.close();
}
}
} else if(debug) {
System.out.println("[PacketLib] Not resolving SRV record for " + this.host);
}
// Resolve host here
try {
InetAddress resolved = InetAddress.getByName(getHost());
if (debug) {
System.out.printf("[PacketLib] Resolved %s -> %s%n", getHost(), resolved.getHostAddress());
}
return new InetSocketAddress(resolved, getPort());
} catch (UnknownHostException e) {
if (debug) {
System.out.println("[PacketLib] Failed to resolve host, letting Netty do it instead.");
e.printStackTrace();
}
return InetSocketAddress.createUnresolved(getHost(), getPort());
}
}
private void addProxy(ChannelPipeline pipeline) {
if(proxy != null) {
switch(proxy.getType()) {
case HTTP:
if (proxy.isAuthenticated()) {
pipeline.addFirst("proxy", new HttpProxyHandler(proxy.getAddress(), proxy.getUsername(), proxy.getPassword()));
} else {
pipeline.addFirst("proxy", new HttpProxyHandler(proxy.getAddress()));
}
break;
case SOCKS4:
if (proxy.isAuthenticated()) {
pipeline.addFirst("proxy", new Socks4ProxyHandler(proxy.getAddress(), proxy.getUsername()));
} else {
pipeline.addFirst("proxy", new Socks4ProxyHandler(proxy.getAddress()));
}
break;
case SOCKS5:
if (proxy.isAuthenticated()) {
pipeline.addFirst("proxy", new Socks5ProxyHandler(proxy.getAddress(), proxy.getUsername(), proxy.getPassword()));
} else {
pipeline.addFirst("proxy", new Socks5ProxyHandler(proxy.getAddress()));
}
break;
default:
throw new UnsupportedOperationException("Unsupported proxy type: " + proxy.getType());
}
}
}
private void addHAProxySupport(ChannelPipeline pipeline) {
InetSocketAddress clientAddress = getFlag(BuiltinFlags.CLIENT_PROXIED_ADDRESS);
if (getFlag(BuiltinFlags.ENABLE_CLIENT_PROXY_PROTOCOL, false) && clientAddress != null) {
pipeline.addFirst("proxy-protocol-packet-sender", new ChannelInboundHandlerAdapter() {
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
HAProxyProxiedProtocol proxiedProtocol = clientAddress.getAddress() instanceof Inet4Address ? HAProxyProxiedProtocol.TCP4 : HAProxyProxiedProtocol.TCP6;
InetSocketAddress remoteAddress = (InetSocketAddress) ctx.channel().remoteAddress();
ctx.channel().writeAndFlush(new HAProxyMessage(
HAProxyProtocolVersion.V2, HAProxyCommand.PROXY, proxiedProtocol,
clientAddress.getAddress().getHostAddress(), remoteAddress.getAddress().getHostAddress(),
clientAddress.getPort(), remoteAddress.getPort()
));
ctx.pipeline().remove(this);
ctx.pipeline().remove("proxy-protocol-encoder");
super.channelActive(ctx);
}
});
pipeline.addFirst("proxy-protocol-encoder", HAProxyMessageEncoder.INSTANCE);
}
}
@Override
public void disconnect(String reason, Throwable cause) {
super.disconnect(reason, cause);
}
private static void createTcpEventLoopGroup() {
if (CHANNEL_CLASS != null) {
return;
}
switch (TransportHelper.determineTransportMethod()) {
case IO_URING:
EVENT_LOOP_GROUP = new IOUringEventLoopGroup();
CHANNEL_CLASS = IOUringSocketChannel.class;
DATAGRAM_CHANNEL_CLASS = IOUringDatagramChannel.class;
break;
case EPOLL:
EVENT_LOOP_GROUP = new EpollEventLoopGroup();
CHANNEL_CLASS = EpollSocketChannel.class;
DATAGRAM_CHANNEL_CLASS = EpollDatagramChannel.class;
break;
case KQUEUE:
EVENT_LOOP_GROUP = new KQueueEventLoopGroup();
CHANNEL_CLASS = KQueueSocketChannel.class;
DATAGRAM_CHANNEL_CLASS = KQueueDatagramChannel.class;
break;
case NIO:
EVENT_LOOP_GROUP = new NioEventLoopGroup();
CHANNEL_CLASS = NioSocketChannel.class;
DATAGRAM_CHANNEL_CLASS = NioDatagramChannel.class;
break;
}
Runtime.getRuntime().addShutdownHook(new Thread(() -> EVENT_LOOP_GROUP.shutdownGracefully()));
}
}

View file

@ -0,0 +1,80 @@
package com.github.steveice10.packetlib.tcp;
import com.github.steveice10.packetlib.Session;
import com.github.steveice10.packetlib.codec.PacketCodecHelper;
import com.github.steveice10.packetlib.codec.PacketDefinition;
import com.github.steveice10.packetlib.event.session.PacketErrorEvent;
import com.github.steveice10.packetlib.packet.Packet;
import com.github.steveice10.packetlib.packet.PacketProtocol;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageCodec;
import java.util.List;
public class TcpPacketCodec extends ByteToMessageCodec<Packet> {
private final Session session;
private final boolean client;
public TcpPacketCodec(Session session, boolean client) {
this.session = session;
this.client = client;
}
@SuppressWarnings({"rawtypes", "unchecked"})
@Override
public void encode(ChannelHandlerContext ctx, Packet packet, ByteBuf buf) throws Exception {
int initial = buf.writerIndex();
PacketProtocol packetProtocol = this.session.getPacketProtocol();
PacketCodecHelper codecHelper = this.session.getCodecHelper();
try {
int packetId = this.client ? packetProtocol.getServerboundId(packet) : packetProtocol.getClientboundId(packet);
PacketDefinition definition = this.client ? packetProtocol.getServerboundDefinition(packetId) : packetProtocol.getClientboundDefinition(packetId);
packetProtocol.getPacketHeader().writePacketId(buf, codecHelper, packetId);
definition.getSerializer().serialize(buf, codecHelper, packet);
} catch (Throwable t) {
// Reset writer index to make sure incomplete data is not written out.
buf.writerIndex(initial);
PacketErrorEvent e = new PacketErrorEvent(this.session, t);
this.session.callEvent(e);
if (!e.shouldSuppress()) {
throw t;
}
}
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf buf, List<Object> out) throws Exception {
int initial = buf.readerIndex();
PacketProtocol packetProtocol = this.session.getPacketProtocol();
PacketCodecHelper codecHelper = this.session.getCodecHelper();
try {
int id = packetProtocol.getPacketHeader().readPacketId(buf, codecHelper);
if (id == -1) {
buf.readerIndex(initial);
return;
}
Packet packet = this.client ? packetProtocol.createClientboundPacket(id, buf, codecHelper) : packetProtocol.createServerboundPacket(id, buf, codecHelper);
if (buf.readableBytes() > 0) {
throw new IllegalStateException("Packet \"" + packet.getClass().getSimpleName() + "\" not fully read.");
}
out.add(packet);
} catch (Throwable t) {
// Advance buffer to end to make sure remaining data in this packet is skipped.
buf.readerIndex(buf.readerIndex() + buf.readableBytes());
PacketErrorEvent e = new PacketErrorEvent(this.session, t);
this.session.callEvent(e);
if (!e.shouldSuppress()) {
throw t;
}
}
}
}

View file

@ -0,0 +1,85 @@
package com.github.steveice10.packetlib.tcp;
import com.github.steveice10.packetlib.Session;
import com.github.steveice10.packetlib.codec.PacketCodecHelper;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageCodec;
import io.netty.handler.codec.DecoderException;
import java.util.List;
import java.util.zip.Deflater;
import java.util.zip.Inflater;
public class TcpPacketCompression extends ByteToMessageCodec<ByteBuf> {
private static final int MAX_COMPRESSED_SIZE = 2097152;
private final Session session;
private final Deflater deflater = new Deflater();
private final Inflater inflater = new Inflater();
private final byte[] buf = new byte[8192];
private final boolean validateDecompression;
public TcpPacketCompression(Session session, boolean validateDecompression) {
this.session = session;
this.validateDecompression = validateDecompression;
}
@Override
public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
super.handlerRemoved(ctx);
this.deflater.end();
this.inflater.end();
}
@Override
public void encode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) throws Exception {
int readable = in.readableBytes();
if(readable < this.session.getCompressionThreshold()) {
this.session.getCodecHelper().writeVarInt(out, 0);
out.writeBytes(in);
} else {
byte[] bytes = new byte[readable];
in.readBytes(bytes);
this.session.getCodecHelper().writeVarInt(out, bytes.length);
this.deflater.setInput(bytes, 0, readable);
this.deflater.finish();
while(!this.deflater.finished()) {
int length = this.deflater.deflate(this.buf);
out.writeBytes(this.buf, 0, length);
}
this.deflater.reset();
}
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf buf, List<Object> out) throws Exception {
if(buf.readableBytes() != 0) {
int size = this.session.getCodecHelper().readVarInt(buf);
if(size == 0) {
out.add(buf.readBytes(buf.readableBytes()));
} else {
if (validateDecompression) { // This is sectioned off as of at least Java Edition 1.18
if (size < this.session.getCompressionThreshold()) {
throw new DecoderException("Badly compressed packet: size of " + size + " is below threshold of " + this.session.getCompressionThreshold() + ".");
}
if (size > MAX_COMPRESSED_SIZE) {
throw new DecoderException("Badly compressed packet: size of " + size + " is larger than protocol maximum of " + MAX_COMPRESSED_SIZE + ".");
}
}
byte[] bytes = new byte[buf.readableBytes()];
buf.readBytes(bytes);
this.inflater.setInput(bytes);
byte[] inflated = new byte[size];
this.inflater.inflate(inflated);
out.add(Unpooled.wrappedBuffer(inflated));
this.inflater.reset();
}
}
}
}

View file

@ -0,0 +1,49 @@
package com.github.steveice10.packetlib.tcp;
import com.github.steveice10.packetlib.crypt.PacketEncryption;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageCodec;
import java.util.List;
public class TcpPacketEncryptor extends ByteToMessageCodec<ByteBuf> {
private final PacketEncryption encryption;
private byte[] decryptedArray = new byte[0];
private byte[] encryptedArray = new byte[0];
public TcpPacketEncryptor(PacketEncryption encryption) {
this.encryption = encryption;
}
@Override
public void encode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) throws Exception {
int length = in.readableBytes();
byte[] bytes = this.getBytes(in);
int outLength = this.encryption.getEncryptOutputSize(length);
if( this.encryptedArray.length < outLength) {
this.encryptedArray = new byte[outLength];
}
out.writeBytes(this.encryptedArray, 0, this.encryption.encrypt(bytes, 0, length, this.encryptedArray, 0));
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf buf, List<Object> out) throws Exception {
int length = buf.readableBytes();
byte[] bytes = this.getBytes(buf);
ByteBuf result = ctx.alloc().heapBuffer(this.encryption.getDecryptOutputSize(length));
result.writerIndex(this.encryption.decrypt(bytes, 0, length, result.array(), result.arrayOffset()));
out.add(result);
}
private byte[] getBytes(ByteBuf buf) {
int length = buf.readableBytes();
if (this.decryptedArray.length < length) {
this.decryptedArray = new byte[length];
}
buf.readBytes(this.decryptedArray, 0, length);
return this.decryptedArray;
}
}

View file

@ -0,0 +1,55 @@
package com.github.steveice10.packetlib.tcp;
import com.github.steveice10.packetlib.Session;
import com.github.steveice10.packetlib.codec.PacketCodecHelper;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageCodec;
import io.netty.handler.codec.CorruptedFrameException;
import java.util.List;
public class TcpPacketSizer extends ByteToMessageCodec<ByteBuf> {
private final Session session;
private final int size;
public TcpPacketSizer(Session session, int size) {
this.session = session;
this.size = size;
}
@Override
public void encode(ChannelHandlerContext ctx, ByteBuf in, ByteBuf out) throws Exception {
int length = in.readableBytes();
out.ensureWritable(this.session.getPacketProtocol().getPacketHeader().getLengthSize(length) + length);
this.session.getPacketProtocol().getPacketHeader().writeLength(out, this.session.getCodecHelper(), length);
out.writeBytes(in);
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf buf, List<Object> out) throws Exception {
buf.markReaderIndex();
byte[] lengthBytes = new byte[size];
for (int index = 0; index < lengthBytes.length; index++) {
if (!buf.isReadable()) {
buf.resetReaderIndex();
return;
}
lengthBytes[index] = buf.readByte();
if ((this.session.getPacketProtocol().getPacketHeader().isLengthVariable() && lengthBytes[index] >= 0) || index == size - 1) {
int length = this.session.getPacketProtocol().getPacketHeader().readLength(Unpooled.wrappedBuffer(lengthBytes), this.session.getCodecHelper(), buf.readableBytes());
if (buf.readableBytes() < length) {
buf.resetReaderIndex();
return;
}
out.add(buf.readBytes(length));
return;
}
}
throw new CorruptedFrameException("Length is too long.");
}
}

View file

@ -0,0 +1,184 @@
package com.github.steveice10.packetlib.tcp;
import com.github.steveice10.packetlib.AbstractServer;
import com.github.steveice10.packetlib.BuiltinFlags;
import com.github.steveice10.packetlib.helper.TransportHelper;
import com.github.steveice10.packetlib.packet.PacketProtocol;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.epoll.EpollEventLoopGroup;
import io.netty.channel.epoll.EpollServerSocketChannel;
import io.netty.channel.*;
import io.netty.channel.kqueue.KQueueEventLoopGroup;
import io.netty.channel.kqueue.KQueueServerSocketChannel;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.ServerSocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.incubator.channel.uring.IOUringEventLoopGroup;
import io.netty.incubator.channel.uring.IOUringServerSocketChannel;
import io.netty.util.concurrent.Future;
import io.netty.util.concurrent.GenericFutureListener;
import java.net.InetSocketAddress;
import java.util.function.Supplier;
public class TcpServer extends AbstractServer {
private EventLoopGroup group;
private Class<? extends ServerSocketChannel> serverSocketChannel;
private Channel channel;
public TcpServer(String host, int port, Supplier<? extends PacketProtocol> protocol) {
super(host, port, protocol);
}
@Override
public boolean isListening() {
return this.channel != null && this.channel.isOpen();
}
@Override
public void bindImpl(boolean wait, final Runnable callback) {
if(this.group != null || this.channel != null) {
return;
}
switch (TransportHelper.determineTransportMethod()) {
case IO_URING:
this.group = new IOUringEventLoopGroup();
this.serverSocketChannel = IOUringServerSocketChannel.class;
break;
case EPOLL:
this.group = new EpollEventLoopGroup();
this.serverSocketChannel = EpollServerSocketChannel.class;
break;
case KQUEUE:
this.group = new KQueueEventLoopGroup();
this.serverSocketChannel = KQueueServerSocketChannel.class;
break;
case NIO:
this.group = new NioEventLoopGroup();
this.serverSocketChannel = NioServerSocketChannel.class;
break;
}
ChannelFuture future = new ServerBootstrap().channel(this.serverSocketChannel).childHandler(new ChannelInitializer<Channel>() {
@Override
public void initChannel(Channel channel) {
InetSocketAddress address = (InetSocketAddress) channel.remoteAddress();
PacketProtocol protocol = createPacketProtocol();
TcpSession session = new TcpServerSession(address.getHostName(), address.getPort(), protocol, TcpServer.this);
session.getPacketProtocol().newServerSession(TcpServer.this, session);
channel.config().setOption(ChannelOption.IP_TOS, 0x18);
try {
channel.config().setOption(ChannelOption.TCP_NODELAY, true);
} catch (ChannelException ignored) {
}
ChannelPipeline pipeline = channel.pipeline();
session.refreshReadTimeoutHandler(channel);
session.refreshWriteTimeoutHandler(channel);
int size = protocol.getPacketHeader().getLengthSize();
if (size > 0) {
pipeline.addLast("sizer", new TcpPacketSizer(session, size));
}
pipeline.addLast("codec", new TcpPacketCodec(session, false));
pipeline.addLast("manager", session);
}
}).group(this.group).localAddress(this.getHost(), this.getPort()).bind();
if(wait) {
try {
future.sync();
} catch(InterruptedException e) {
}
channel = future.channel();
if(callback != null) {
callback.run();
}
} else {
future.addListener((ChannelFutureListener) future1 -> {
if(future1.isSuccess()) {
channel = future1.channel();
if(callback != null) {
callback.run();
}
} else {
System.err.println("[ERROR] Failed to asynchronously bind connection listener.");
if(future1.cause() != null) {
future1.cause().printStackTrace();
}
}
});
}
}
@Override
public void closeImpl(boolean wait, final Runnable callback) {
if(this.channel != null) {
if(this.channel.isOpen()) {
ChannelFuture future = this.channel.close();
if(wait) {
try {
future.sync();
} catch(InterruptedException e) {
}
if(callback != null) {
callback.run();
}
} else {
future.addListener((ChannelFutureListener) future1 -> {
if(future1.isSuccess()) {
if(callback != null) {
callback.run();
}
} else {
System.err.println("[ERROR] Failed to asynchronously close connection listener.");
if(future1.cause() != null) {
future1.cause().printStackTrace();
}
}
});
}
}
this.channel = null;
}
if(this.group != null) {
Future<?> future = this.group.shutdownGracefully();
if(wait) {
try {
future.sync();
} catch(InterruptedException e) {
}
} else {
future.addListener(new GenericFutureListener() {
@Override
public void operationComplete(Future future) {
if(!future.isSuccess() && getGlobalFlag(BuiltinFlags.PRINT_DEBUG, false)) {
System.err.println("[ERROR] Failed to asynchronously close connection listener.");
if(future.cause() != null) {
future.cause().printStackTrace();
}
}
}
});
}
this.group = null;
}
}
}

View file

@ -0,0 +1,65 @@
package com.github.steveice10.packetlib.tcp;
import com.github.steveice10.packetlib.codec.PacketCodecHelper;
import com.github.steveice10.packetlib.packet.PacketProtocol;
import io.netty.channel.ChannelHandlerContext;
import java.util.HashMap;
import java.util.Map;
public class TcpServerSession extends TcpSession {
private final TcpServer server;
private final PacketCodecHelper codecHelper;
public TcpServerSession(String host, int port, PacketProtocol protocol, TcpServer server) {
super(host, port, protocol);
this.server = server;
this.codecHelper = protocol.createHelper();
}
@Override
public PacketCodecHelper getCodecHelper() {
return this.codecHelper;
}
@Override
public Map<String, Object> getFlags() {
Map<String, Object> ret = new HashMap<>();
ret.putAll(this.server.getGlobalFlags());
ret.putAll(super.getFlags());
return ret;
}
@Override
public boolean hasFlag(String key) {
if(super.hasFlag(key)) {
return true;
}
return this.server.hasGlobalFlag(key);
}
@Override
public <T> T getFlag(String key, T def) {
T ret = super.getFlag(key, null);
if(ret != null) {
return ret;
}
return this.server.getGlobalFlag(key, def);
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
super.channelActive(ctx);
this.server.addSession(this);
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
super.channelInactive(ctx);
this.server.removeSession(this);
}
}

View file

@ -0,0 +1,391 @@
package com.github.steveice10.packetlib.tcp;
import com.github.steveice10.packetlib.Session;
import com.github.steveice10.packetlib.crypt.PacketEncryption;
import com.github.steveice10.packetlib.event.session.ConnectedEvent;
import com.github.steveice10.packetlib.event.session.DisconnectedEvent;
import com.github.steveice10.packetlib.event.session.DisconnectingEvent;
import com.github.steveice10.packetlib.event.session.PacketSendingEvent;
import com.github.steveice10.packetlib.event.session.SessionEvent;
import com.github.steveice10.packetlib.event.session.SessionListener;
import com.github.steveice10.packetlib.packet.Packet;
import com.github.steveice10.packetlib.packet.PacketProtocol;
import io.netty.channel.*;
import io.netty.handler.timeout.ReadTimeoutException;
import io.netty.handler.timeout.ReadTimeoutHandler;
import io.netty.handler.timeout.WriteTimeoutException;
import io.netty.handler.timeout.WriteTimeoutHandler;
import net.kyori.adventure.text.Component;
import javax.annotation.Nullable;
import java.net.ConnectException;
import java.net.SocketAddress;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
public abstract class TcpSession extends SimpleChannelInboundHandler<Packet> implements Session {
/**
* Controls whether non-priority packets are handled in a separate event loop
*/
public static boolean USE_EVENT_LOOP_FOR_PACKETS = true;
private static EventLoopGroup PACKET_EVENT_LOOP;
protected String host;
protected int port;
private final PacketProtocol protocol;
private final EventLoop eventLoop = createEventLoop();
private int compressionThreshold = -1;
private int connectTimeout = 30;
private int readTimeout = 30;
private int writeTimeout = 0;
private final Map<String, Object> flags = new HashMap<>();
private final List<SessionListener> listeners = new CopyOnWriteArrayList<>();
private Channel channel;
protected boolean disconnected = false;
public TcpSession(String host, int port, PacketProtocol protocol) {
this.host = host;
this.port = port;
this.protocol = protocol;
}
@Override
public void connect() {
this.connect(true);
}
@Override
public void connect(boolean wait) {
}
@Override
public String getHost() {
return this.host;
}
@Override
public int getPort() {
return this.port;
}
@Override
public SocketAddress getLocalAddress() {
return this.channel != null ? this.channel.localAddress() : null;
}
@Override
public SocketAddress getRemoteAddress() {
return this.channel != null ? this.channel.remoteAddress() : null;
}
@Override
public PacketProtocol getPacketProtocol() {
return this.protocol;
}
@Override
public Map<String, Object> getFlags() {
return Collections.unmodifiableMap(this.flags);
}
@Override
public boolean hasFlag(String key) {
return this.flags.containsKey(key);
}
@Override
public <T> T getFlag(String key) {
return this.getFlag(key, null);
}
@SuppressWarnings("unchecked")
@Override
public <T> T getFlag(String key, T def) {
Object value = this.flags.get(key);
if (value == null) {
return def;
}
try {
return (T) value;
} catch (ClassCastException e) {
throw new IllegalStateException("Tried to get flag \"" + key + "\" as the wrong type. Actual type: " + value.getClass().getName());
}
}
@Override
public void setFlag(String key, Object value) {
this.flags.put(key, value);
}
@Override
public List<SessionListener> getListeners() {
return Collections.unmodifiableList(this.listeners);
}
@Override
public void addListener(SessionListener listener) {
this.listeners.add(listener);
}
@Override
public void removeListener(SessionListener listener) {
this.listeners.remove(listener);
}
@Override
public void callEvent(SessionEvent event) {
try {
for (SessionListener listener : this.listeners) {
event.call(listener);
}
} catch (Throwable t) {
exceptionCaught(null, t);
}
}
@Override
public void callPacketReceived(Packet packet) {
try {
for (SessionListener listener : this.listeners) {
listener.packetReceived(this, packet);
}
} catch (Throwable t) {
exceptionCaught(null, t);
}
}
@Override
public void callPacketSent(Packet packet) {
try {
for (SessionListener listener : this.listeners) {
listener.packetSent(this, packet);
}
} catch (Throwable t) {
exceptionCaught(null, t);
}
}
@Override
public int getCompressionThreshold() {
return this.compressionThreshold;
}
@Override
public void setCompressionThreshold(int threshold, boolean validateDecompression) {
this.compressionThreshold = threshold;
if (this.channel != null) {
if (this.compressionThreshold >= 0) {
if (this.channel.pipeline().get("compression") == null) {
this.channel.pipeline().addBefore("codec", "compression", new TcpPacketCompression(this, validateDecompression));
}
} else if (this.channel.pipeline().get("compression") != null) {
this.channel.pipeline().remove("compression");
}
}
}
@Override
public void enableEncryption(PacketEncryption encryption) {
if (channel == null) {
throw new IllegalStateException("Connect the client before initializing encryption!");
}
channel.pipeline().addBefore("sizer", "encryption", new TcpPacketEncryptor(encryption));
}
@Override
public int getConnectTimeout() {
return this.connectTimeout;
}
@Override
public void setConnectTimeout(int timeout) {
this.connectTimeout = timeout;
}
@Override
public int getReadTimeout() {
return this.readTimeout;
}
@Override
public void setReadTimeout(int timeout) {
this.readTimeout = timeout;
this.refreshReadTimeoutHandler();
}
@Override
public int getWriteTimeout() {
return this.writeTimeout;
}
@Override
public void setWriteTimeout(int timeout) {
this.writeTimeout = timeout;
this.refreshWriteTimeoutHandler();
}
@Override
public boolean isConnected() {
return this.channel != null && this.channel.isOpen() && !this.disconnected;
}
@Override
public void send(Packet packet) {
if(this.channel == null) {
return;
}
PacketSendingEvent sendingEvent = new PacketSendingEvent(this, packet);
this.callEvent(sendingEvent);
if (!sendingEvent.isCancelled()) {
final Packet toSend = sendingEvent.getPacket();
this.channel.writeAndFlush(toSend).addListener((ChannelFutureListener) future -> {
if(future.isSuccess()) {
callPacketSent(toSend);
} else {
exceptionCaught(null, future.cause());
}
});
}
}
@Override
public void disconnect(String reason) {
this.disconnect(Component.text(reason));
}
@Override
public void disconnect(String reason, Throwable cause) {
this.disconnect(Component.text(reason), cause);
}
@Override
public void disconnect(Component reason) {
this.disconnect(reason, null);
}
@Override
public void disconnect(final Component reason, final Throwable cause) {
if (this.disconnected) {
return;
}
this.disconnected = true;
if (this.channel != null && this.channel.isOpen()) {
this.callEvent(new DisconnectingEvent(this, reason, cause));
this.channel.flush().close().addListener((ChannelFutureListener) future ->
callEvent(new DisconnectedEvent(TcpSession.this,
reason != null ? reason : Component.text("Connection closed."), cause)));
} else {
this.callEvent(new DisconnectedEvent(this, reason != null ? reason : Component.text("Connection closed."), cause));
}
}
private @Nullable EventLoop createEventLoop() {
if (!USE_EVENT_LOOP_FOR_PACKETS) {
return null;
}
if (PACKET_EVENT_LOOP == null) {
PACKET_EVENT_LOOP = new DefaultEventLoopGroup();
}
return PACKET_EVENT_LOOP.next();
}
public Channel getChannel() {
return this.channel;
}
protected void refreshReadTimeoutHandler() {
this.refreshReadTimeoutHandler(this.channel);
}
protected void refreshReadTimeoutHandler(Channel channel) {
if (channel != null) {
if (this.readTimeout <= 0) {
if (channel.pipeline().get("readTimeout") != null) {
channel.pipeline().remove("readTimeout");
}
} else {
if (channel.pipeline().get("readTimeout") == null) {
channel.pipeline().addFirst("readTimeout", new ReadTimeoutHandler(this.readTimeout));
} else {
channel.pipeline().replace("readTimeout", "readTimeout", new ReadTimeoutHandler(this.readTimeout));
}
}
}
}
protected void refreshWriteTimeoutHandler() {
this.refreshWriteTimeoutHandler(this.channel);
}
protected void refreshWriteTimeoutHandler(Channel channel) {
if (channel != null) {
if (this.writeTimeout <= 0) {
if (channel.pipeline().get("writeTimeout") != null) {
channel.pipeline().remove("writeTimeout");
}
} else {
if (channel.pipeline().get("writeTimeout") == null) {
channel.pipeline().addFirst("writeTimeout", new WriteTimeoutHandler(this.writeTimeout));
} else {
channel.pipeline().replace("writeTimeout", "writeTimeout", new WriteTimeoutHandler(this.writeTimeout));
}
}
}
}
@Override
public void channelActive(ChannelHandlerContext ctx) throws Exception {
if (this.disconnected || this.channel != null) {
ctx.channel().close();
return;
}
this.channel = ctx.channel();
this.callEvent(new ConnectedEvent(this));
}
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
if (ctx.channel() == this.channel) {
this.disconnect("Connection closed.");
}
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
String message;
if (cause instanceof ConnectTimeoutException || (cause instanceof ConnectException && cause.getMessage().contains("connection timed out"))) {
message = "Connection timed out.";
} else if (cause instanceof ReadTimeoutException) {
message = "Read timed out.";
} else if (cause instanceof WriteTimeoutException) {
message = "Write timed out.";
} else {
message = cause.toString();
}
this.disconnect(message, cause);
}
@Override
protected void channelRead0(ChannelHandlerContext ctx, Packet packet) {
if (!packet.isPriority() && eventLoop != null) {
eventLoop.execute(() -> this.callPacketReceived(packet));
} else {
this.callPacketReceived(packet);
}
}
}

View file

@ -1,6 +1,5 @@
package com.github.steveice10.mc.protocol;
import com.github.steveice10.mc.auth.data.GameProfile;
import com.github.steveice10.mc.protocol.codec.MinecraftCodec;
import com.github.steveice10.mc.protocol.data.game.entity.player.GameMode;
import com.github.steveice10.mc.protocol.data.status.PlayerInfo;
@ -27,6 +26,7 @@ import java.io.DataInput;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.zip.GZIPInputStream;
@ -46,7 +46,7 @@ public class MinecraftProtocolTest {
private static final ServerStatusInfo SERVER_INFO = new ServerStatusInfo(
new VersionInfo(MinecraftCodec.CODEC.getMinecraftVersion(), MinecraftCodec.CODEC.getProtocolVersion()),
new PlayerInfo(100, 0, new GameProfile[0]),
new PlayerInfo(100, 0, new ArrayList<>()),
Component.text("Hello world!"),
null,
false

View file

@ -8,7 +8,8 @@ import com.github.steveice10.mc.protocol.data.game.recipe.RecipeType;
import com.github.steveice10.mc.protocol.data.game.recipe.data.CookedRecipeData;
import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapedRecipeData;
import com.github.steveice10.mc.protocol.data.game.recipe.data.ShapelessRecipeData;
import com.github.steveice10.mc.protocol.data.game.recipe.data.SmithingRecipeData;
import com.github.steveice10.mc.protocol.data.game.recipe.data.LegacyUpgradeRecipeData;
import com.github.steveice10.mc.protocol.data.game.recipe.data.SmithingTransformRecipeData;
import com.github.steveice10.mc.protocol.data.game.recipe.data.StoneCuttingRecipeData;
import com.github.steveice10.mc.protocol.packet.PacketTest;
import org.junit.Before;
@ -93,7 +94,7 @@ public class ServerDeclareRecipesTest extends PacketTest {
new Recipe(
RecipeType.SMITHING,
"minecraft:Recipe5",
new SmithingRecipeData(
new LegacyUpgradeRecipeData(
new Ingredient(new ItemStack[]{
new ItemStack(10)
}),

View file

@ -2,7 +2,12 @@ package com.github.steveice10.mc.protocol.packet.ingame.clientbound.level;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.EntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.MetadataType;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.*;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.BooleanEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ByteEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.FloatEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.IntEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.LongEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.metadata.type.ObjectEntityMetadata;
import com.github.steveice10.mc.protocol.data.game.entity.object.Direction;
import com.github.steveice10.mc.protocol.packet.PacketTest;
import com.github.steveice10.mc.protocol.packet.ingame.clientbound.entity.ClientboundSetEntityDataPacket;
@ -27,7 +32,7 @@ public class ClientboundSetEntityDataPacketTest extends PacketTest {
new FloatEntityMetadata(3, MetadataType.FLOAT, 3.0f),
new LongEntityMetadata(8, MetadataType.LONG, 123456789L),
new ObjectEntityMetadata<>(5, MetadataType.POSITION, Vector3i.from(0, 1, 0)),
new ObjectEntityMetadata<>(2, MetadataType.BLOCK_STATE, OptionalInt.of(60)),
new ObjectEntityMetadata<>(2, MetadataType.BLOCK_STATE, 60),
new ObjectEntityMetadata<>(6, MetadataType.DIRECTION, Direction.EAST),
new ObjectEntityMetadata<>(7, MetadataType.OPTIONAL_VARINT, OptionalInt.of(1038))
}),

View file

@ -9,6 +9,8 @@ import com.github.steveice10.mc.protocol.packet.PacketTest;
import net.kyori.adventure.text.Component;
import org.junit.Before;
import java.util.ArrayList;
import java.util.Collections;
import java.util.UUID;
public class ClientboundStatusResponsePacketTest extends PacketTest {
@ -17,9 +19,9 @@ public class ClientboundStatusResponsePacketTest extends PacketTest {
this.setPackets(new ClientboundStatusResponsePacket(
new ServerStatusInfo(
new VersionInfo(MinecraftCodec.CODEC.getMinecraftVersion(), MinecraftCodec.CODEC.getProtocolVersion()),
new PlayerInfo(100, 10, new GameProfile[]{
new GameProfile(UUID.randomUUID(), "Username")
}),
new PlayerInfo(100, 10, new ArrayList<>(
Collections.singleton(new GameProfile(UUID.randomUUID(), "Username"))
)),
Component.text("Description"),
null,
false

Binary file not shown.