Minecraft 1.19 support

This commit is contained in:
md_5
2022-06-08 02:00:00 +10:00
parent 862bb2ac72
commit eccdf87f22
49 changed files with 851 additions and 227 deletions

View File

@@ -3,6 +3,8 @@ package net.md_5.bungee.protocol;
import net.md_5.bungee.protocol.packet.BossBar;
import net.md_5.bungee.protocol.packet.Chat;
import net.md_5.bungee.protocol.packet.ClearTitles;
import net.md_5.bungee.protocol.packet.ClientChat;
import net.md_5.bungee.protocol.packet.ClientCommand;
import net.md_5.bungee.protocol.packet.ClientSettings;
import net.md_5.bungee.protocol.packet.ClientStatus;
import net.md_5.bungee.protocol.packet.Commands;
@@ -21,6 +23,7 @@ import net.md_5.bungee.protocol.packet.LoginPayloadResponse;
import net.md_5.bungee.protocol.packet.LoginRequest;
import net.md_5.bungee.protocol.packet.LoginSuccess;
import net.md_5.bungee.protocol.packet.PingPacket;
import net.md_5.bungee.protocol.packet.PlayerChat;
import net.md_5.bungee.protocol.packet.PlayerListHeaderFooter;
import net.md_5.bungee.protocol.packet.PlayerListItem;
import net.md_5.bungee.protocol.packet.PluginMessage;
@@ -32,6 +35,7 @@ import net.md_5.bungee.protocol.packet.SetCompression;
import net.md_5.bungee.protocol.packet.StatusRequest;
import net.md_5.bungee.protocol.packet.StatusResponse;
import net.md_5.bungee.protocol.packet.Subtitle;
import net.md_5.bungee.protocol.packet.SystemChat;
import net.md_5.bungee.protocol.packet.TabCompleteRequest;
import net.md_5.bungee.protocol.packet.TabCompleteResponse;
import net.md_5.bungee.protocol.packet.Team;
@@ -78,6 +82,22 @@ public abstract class AbstractPacketHandler
{
}
public void handle(ClientChat chat) throws Exception
{
}
public void handle(PlayerChat chat) throws Exception
{
}
public void handle(SystemChat chat) throws Exception
{
}
public void handle(ClientCommand command) throws Exception
{
}
public void handle(Respawn respawn) throws Exception
{
}

View File

@@ -214,6 +214,67 @@ public abstract class DefinedPacket
return new UUID( input.readLong(), input.readLong() );
}
public static void writeProperties(Property[] properties, ByteBuf buf)
{
writeVarInt( properties.length, buf );
for ( Property prop : properties )
{
writeString( prop.getName(), buf );
writeString( prop.getValue(), buf );
if ( prop.getSignature() != null )
{
buf.writeBoolean( true );
writeString( prop.getSignature(), buf );
} else
{
buf.writeBoolean( false );
}
}
}
public static Property[] readProperties(ByteBuf buf)
{
Property[] properties = new Property[ DefinedPacket.readVarInt( buf ) ];
for ( int j = 0; j < properties.length; j++ )
{
String name = readString( buf );
String value = readString( buf );
if ( buf.readBoolean() )
{
properties[j] = new Property( name, value, DefinedPacket.readString( buf ) );
} else
{
properties[j] = new Property( name, value );
}
}
return properties;
}
public static void writePublicKey(PlayerPublicKey publicKey, ByteBuf buf)
{
if ( publicKey != null )
{
buf.writeBoolean( true );
buf.writeLong( publicKey.getExpiry() );
writeArray( publicKey.getKey(), buf );
writeArray( publicKey.getSignature(), buf );
} else
{
buf.writeBoolean( false );
}
}
public static PlayerPublicKey readPublicKey(ByteBuf buf)
{
if ( buf.readBoolean() )
{
return new PlayerPublicKey( buf.readLong(), readArray( buf ), readArray( buf ) );
}
return null;
}
public static Tag readTag(ByteBuf input)
{
Tag tag = NamedTag.read( new DataInputStream( new ByteBufInputStream( input ) ) );

View File

@@ -0,0 +1,11 @@
package net.md_5.bungee.protocol;
import lombok.Data;
@Data
public class Location
{
private final String dimension;
private final long pos;
}

View File

@@ -0,0 +1,12 @@
package net.md_5.bungee.protocol;
import lombok.Data;
@Data
public class PlayerPublicKey
{
private final long expiry;
private final byte[] key;
private final byte[] signature;
}

View File

@@ -0,0 +1,19 @@
package net.md_5.bungee.protocol;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
public class Property
{
private String name;
private String value;
private String signature;
public Property(String name, String value)
{
this( name, value, null );
}
}

View File

@@ -12,6 +12,8 @@ import lombok.Getter;
import net.md_5.bungee.protocol.packet.BossBar;
import net.md_5.bungee.protocol.packet.Chat;
import net.md_5.bungee.protocol.packet.ClearTitles;
import net.md_5.bungee.protocol.packet.ClientChat;
import net.md_5.bungee.protocol.packet.ClientCommand;
import net.md_5.bungee.protocol.packet.ClientSettings;
import net.md_5.bungee.protocol.packet.Commands;
import net.md_5.bungee.protocol.packet.EncryptionRequest;
@@ -27,6 +29,7 @@ import net.md_5.bungee.protocol.packet.LoginPayloadResponse;
import net.md_5.bungee.protocol.packet.LoginRequest;
import net.md_5.bungee.protocol.packet.LoginSuccess;
import net.md_5.bungee.protocol.packet.PingPacket;
import net.md_5.bungee.protocol.packet.PlayerChat;
import net.md_5.bungee.protocol.packet.PlayerListHeaderFooter;
import net.md_5.bungee.protocol.packet.PlayerListItem;
import net.md_5.bungee.protocol.packet.PluginMessage;
@@ -38,6 +41,7 @@ import net.md_5.bungee.protocol.packet.SetCompression;
import net.md_5.bungee.protocol.packet.StatusRequest;
import net.md_5.bungee.protocol.packet.StatusResponse;
import net.md_5.bungee.protocol.packet.Subtitle;
import net.md_5.bungee.protocol.packet.SystemChat;
import net.md_5.bungee.protocol.packet.TabCompleteRequest;
import net.md_5.bungee.protocol.packet.TabCompleteResponse;
import net.md_5.bungee.protocol.packet.Team;
@@ -75,7 +79,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_15, 0x21 ),
map( ProtocolConstants.MINECRAFT_1_16, 0x20 ),
map( ProtocolConstants.MINECRAFT_1_16_2, 0x1F ),
map( ProtocolConstants.MINECRAFT_1_17, 0x21 )
map( ProtocolConstants.MINECRAFT_1_17, 0x21 ),
map( ProtocolConstants.MINECRAFT_1_19, 0x1E )
);
TO_CLIENT.registerPacket(
Login.class,
@@ -86,17 +91,18 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_15, 0x26 ),
map( ProtocolConstants.MINECRAFT_1_16, 0x25 ),
map( ProtocolConstants.MINECRAFT_1_16_2, 0x24 ),
map( ProtocolConstants.MINECRAFT_1_17, 0x26 )
map( ProtocolConstants.MINECRAFT_1_17, 0x26 ),
map( ProtocolConstants.MINECRAFT_1_19, 0x23 )
);
TO_CLIENT.registerPacket(
Chat.class,
TO_CLIENT.registerPacket( Chat.class,
Chat::new,
map( ProtocolConstants.MINECRAFT_1_8, 0x02 ),
map( ProtocolConstants.MINECRAFT_1_9, 0x0F ),
map( ProtocolConstants.MINECRAFT_1_13, 0x0E ),
map( ProtocolConstants.MINECRAFT_1_15, 0x0F ),
map( ProtocolConstants.MINECRAFT_1_16, 0x0E ),
map( ProtocolConstants.MINECRAFT_1_17, 0x0F )
map( ProtocolConstants.MINECRAFT_1_17, 0x0F ),
map( ProtocolConstants.MINECRAFT_1_19, -1 )
);
TO_CLIENT.registerPacket(
Respawn.class,
@@ -110,7 +116,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_15, 0x3B ),
map( ProtocolConstants.MINECRAFT_1_16, 0x3A ),
map( ProtocolConstants.MINECRAFT_1_16_2, 0x39 ),
map( ProtocolConstants.MINECRAFT_1_17, 0x3D )
map( ProtocolConstants.MINECRAFT_1_17, 0x3D ),
map( ProtocolConstants.MINECRAFT_1_19, 0x3B )
);
TO_CLIENT.registerPacket(
BossBar.class,
@@ -118,7 +125,13 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_9, 0x0C ),
map( ProtocolConstants.MINECRAFT_1_15, 0x0D ),
map( ProtocolConstants.MINECRAFT_1_16, 0x0C ),
map( ProtocolConstants.MINECRAFT_1_17, 0x0D )
map( ProtocolConstants.MINECRAFT_1_17, 0x0D ),
map( ProtocolConstants.MINECRAFT_1_19, 0x0A )
);
TO_CLIENT.registerPacket(
PlayerChat.class,
PlayerChat::new,
map( ProtocolConstants.MINECRAFT_1_19, 0x30 )
);
TO_CLIENT.registerPacket(
PlayerListItem.class, // PlayerInfo
@@ -131,7 +144,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_15, 0x34 ),
map( ProtocolConstants.MINECRAFT_1_16, 0x33 ),
map( ProtocolConstants.MINECRAFT_1_16_2, 0x32 ),
map( ProtocolConstants.MINECRAFT_1_17, 0x36 )
map( ProtocolConstants.MINECRAFT_1_17, 0x36 ),
map( ProtocolConstants.MINECRAFT_1_19, 0x34 )
);
TO_CLIENT.registerPacket(
TabCompleteResponse.class,
@@ -142,7 +156,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_15, 0x11 ),
map( ProtocolConstants.MINECRAFT_1_16, 0x10 ),
map( ProtocolConstants.MINECRAFT_1_16_2, 0x0F ),
map( ProtocolConstants.MINECRAFT_1_17, 0x11 )
map( ProtocolConstants.MINECRAFT_1_17, 0x11 ),
map( ProtocolConstants.MINECRAFT_1_19, 0x0E )
);
TO_CLIENT.registerPacket(
ScoreboardObjective.class,
@@ -202,7 +217,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_15, 0x19 ),
map( ProtocolConstants.MINECRAFT_1_16, 0x18 ),
map( ProtocolConstants.MINECRAFT_1_16_2, 0x17 ),
map( ProtocolConstants.MINECRAFT_1_17, 0x18 )
map( ProtocolConstants.MINECRAFT_1_17, 0x18 ),
map( ProtocolConstants.MINECRAFT_1_19, 0x15 )
);
TO_CLIENT.registerPacket(
Kick.class,
@@ -214,7 +230,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_15, 0x1B ),
map( ProtocolConstants.MINECRAFT_1_16, 0x1A ),
map( ProtocolConstants.MINECRAFT_1_16_2, 0x19 ),
map( ProtocolConstants.MINECRAFT_1_17, 0x1A )
map( ProtocolConstants.MINECRAFT_1_17, 0x1A ),
map( ProtocolConstants.MINECRAFT_1_19, 0x17 )
);
TO_CLIENT.registerPacket(
Title.class,
@@ -232,7 +249,8 @@ public enum Protocol
TO_CLIENT.registerPacket(
ClearTitles.class,
ClearTitles::new,
map( ProtocolConstants.MINECRAFT_1_17, 0x10 )
map( ProtocolConstants.MINECRAFT_1_17, 0x10 ),
map( ProtocolConstants.MINECRAFT_1_19, 0x0D )
);
TO_CLIENT.registerPacket(
Subtitle.class,
@@ -246,6 +264,11 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_17, 0x5A ),
map( ProtocolConstants.MINECRAFT_1_18, 0x5B )
);
TO_CLIENT.registerPacket(
SystemChat.class,
SystemChat::new,
map( ProtocolConstants.MINECRAFT_1_19, 0x5F )
);
TO_CLIENT.registerPacket(
PlayerListHeaderFooter.class,
PlayerListHeaderFooter::new,
@@ -259,7 +282,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_15, 0x54 ),
map( ProtocolConstants.MINECRAFT_1_16, 0x53 ),
map( ProtocolConstants.MINECRAFT_1_17, 0x5E ),
map( ProtocolConstants.MINECRAFT_1_18, 0x5F )
map( ProtocolConstants.MINECRAFT_1_18, 0x5F ),
map( ProtocolConstants.MINECRAFT_1_19, 0x60 )
);
TO_CLIENT.registerPacket(
EntityStatus.class,
@@ -271,7 +295,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_15, 0x1C ),
map( ProtocolConstants.MINECRAFT_1_16, 0x1B ),
map( ProtocolConstants.MINECRAFT_1_16_2, 0x1A ),
map( ProtocolConstants.MINECRAFT_1_17, 0x1B )
map( ProtocolConstants.MINECRAFT_1_17, 0x1B ),
map( ProtocolConstants.MINECRAFT_1_19, 0x18 )
);
TO_CLIENT.registerPacket(
Commands.class,
@@ -280,7 +305,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_15, 0x12 ),
map( ProtocolConstants.MINECRAFT_1_16, 0x11 ),
map( ProtocolConstants.MINECRAFT_1_16_2, 0x10 ),
map( ProtocolConstants.MINECRAFT_1_17, 0x12 )
map( ProtocolConstants.MINECRAFT_1_17, 0x12 ),
map( ProtocolConstants.MINECRAFT_1_19, 0x0F )
);
TO_CLIENT.registerPacket(
GameState.class,
@@ -288,7 +314,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_15, 0x1F ),
map( ProtocolConstants.MINECRAFT_1_16, 0x1E ),
map( ProtocolConstants.MINECRAFT_1_16_2, 0x1D ),
map( ProtocolConstants.MINECRAFT_1_17, 0x1E )
map( ProtocolConstants.MINECRAFT_1_17, 0x1E ),
map( ProtocolConstants.MINECRAFT_1_19, 0x1B )
);
TO_CLIENT.registerPacket(
ViewDistance.class,
@@ -296,7 +323,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_14, 0x41 ),
map( ProtocolConstants.MINECRAFT_1_15, 0x42 ),
map( ProtocolConstants.MINECRAFT_1_16, 0x41 ),
map( ProtocolConstants.MINECRAFT_1_17, 0x4A )
map( ProtocolConstants.MINECRAFT_1_17, 0x4A ),
map( ProtocolConstants.MINECRAFT_1_19, 0x49 )
);
TO_SERVER.registerPacket(
@@ -309,16 +337,27 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_13, 0x0E ),
map( ProtocolConstants.MINECRAFT_1_14, 0x0F ),
map( ProtocolConstants.MINECRAFT_1_16, 0x10 ),
map( ProtocolConstants.MINECRAFT_1_17, 0x0F )
map( ProtocolConstants.MINECRAFT_1_17, 0x0F ),
map( ProtocolConstants.MINECRAFT_1_19, 0x11 )
);
TO_SERVER.registerPacket(
Chat.class,
TO_SERVER.registerPacket( Chat.class,
Chat::new,
map( ProtocolConstants.MINECRAFT_1_8, 0x01 ),
map( ProtocolConstants.MINECRAFT_1_9, 0x02 ),
map( ProtocolConstants.MINECRAFT_1_12, 0x03 ),
map( ProtocolConstants.MINECRAFT_1_12_1, 0x02 ),
map( ProtocolConstants.MINECRAFT_1_14, 0x03 )
map( ProtocolConstants.MINECRAFT_1_14, 0x03 ),
map( ProtocolConstants.MINECRAFT_1_19, -1 )
);
TO_SERVER.registerPacket(
ClientCommand.class,
ClientCommand::new,
map( ProtocolConstants.MINECRAFT_1_19, 0x03 )
);
TO_SERVER.registerPacket(
ClientChat.class,
ClientChat::new,
map( ProtocolConstants.MINECRAFT_1_19, 0x04 )
);
TO_SERVER.registerPacket(
TabCompleteRequest.class,
@@ -328,7 +367,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_12, 0x02 ),
map( ProtocolConstants.MINECRAFT_1_12_1, 0x01 ),
map( ProtocolConstants.MINECRAFT_1_13, 0x05 ),
map( ProtocolConstants.MINECRAFT_1_14, 0x06 )
map( ProtocolConstants.MINECRAFT_1_14, 0x06 ),
map( ProtocolConstants.MINECRAFT_1_19, 0x08 )
);
TO_SERVER.registerPacket(
ClientSettings.class,
@@ -337,7 +377,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_9, 0x04 ),
map( ProtocolConstants.MINECRAFT_1_12, 0x05 ),
map( ProtocolConstants.MINECRAFT_1_12_1, 0x04 ),
map( ProtocolConstants.MINECRAFT_1_14, 0x05 )
map( ProtocolConstants.MINECRAFT_1_14, 0x05 ),
map( ProtocolConstants.MINECRAFT_1_19, 0x07 )
);
TO_SERVER.registerPacket(
PluginMessage.class,
@@ -348,7 +389,8 @@ public enum Protocol
map( ProtocolConstants.MINECRAFT_1_12_1, 0x09 ),
map( ProtocolConstants.MINECRAFT_1_13, 0x0A ),
map( ProtocolConstants.MINECRAFT_1_14, 0x0B ),
map( ProtocolConstants.MINECRAFT_1_17, 0x0A )
map( ProtocolConstants.MINECRAFT_1_17, 0x0A ),
map( ProtocolConstants.MINECRAFT_1_19, 0x0C )
);
}
},
@@ -554,6 +596,11 @@ public enum Protocol
{
// Mapping is non current, but the next one may be ok
ProtocolMapping nextMapping = mappings[mappingIndex + 1];
if ( nextMapping.packetID < 0 )
{
break;
}
if ( nextMapping.protocolVersion == protocol )
{
Preconditions.checkState( nextMapping.packetID != mapping.packetID, "Duplicate packet mapping (%s, %s)", mapping.protocolVersion, nextMapping.protocolVersion );

View File

@@ -38,6 +38,7 @@ public class ProtocolConstants
public static final int MINECRAFT_1_17_1 = 756;
public static final int MINECRAFT_1_18 = 757;
public static final int MINECRAFT_1_18_2 = 758;
public static final int MINECRAFT_1_19 = 759;
public static final List<String> SUPPORTED_VERSIONS;
public static final List<Integer> SUPPORTED_VERSION_IDS;
@@ -54,7 +55,8 @@ public class ProtocolConstants
"1.15.x",
"1.16.x",
"1.17.x",
"1.18.x"
"1.18.x",
"1.19.x"
);
ImmutableList.Builder<Integer> supportedVersionIds = ImmutableList.<Integer>builder().add(
ProtocolConstants.MINECRAFT_1_8,
@@ -87,13 +89,14 @@ public class ProtocolConstants
ProtocolConstants.MINECRAFT_1_17,
ProtocolConstants.MINECRAFT_1_17_1,
ProtocolConstants.MINECRAFT_1_18,
ProtocolConstants.MINECRAFT_1_18_2
ProtocolConstants.MINECRAFT_1_18_2,
ProtocolConstants.MINECRAFT_1_19
);
if ( SNAPSHOT_SUPPORT )
{
// supportedVersions.add( "1.18.x" );
// supportedVersionIds.add( ProtocolConstants.MINECRAFT_1_18 );
// supportedVersions.add( "1.19.x" );
// supportedVersionIds.add( ProtocolConstants.MINECRAFT_1_19 );
}
SUPPORTED_VERSIONS = supportedVersions.build();

View File

@@ -0,0 +1,50 @@
package net.md_5.bungee.protocol.packet;
import io.netty.buffer.ByteBuf;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import net.md_5.bungee.protocol.AbstractPacketHandler;
import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.ProtocolConstants;
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class ClientChat extends DefinedPacket
{
private String message;
private long timestamp;
private long salt;
private byte[] signature;
private boolean signedPreview;
@Override
public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
{
message = readString( buf, 256 );
timestamp = buf.readLong();
salt = buf.readLong();
signature = readArray( buf );
signedPreview = buf.readBoolean();
}
@Override
public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
{
writeString( message, buf );
buf.writeLong( timestamp );
buf.writeLong( salt );
writeArray( signature, buf );
buf.writeBoolean( signedPreview );
}
@Override
public void handle(AbstractPacketHandler handler) throws Exception
{
handler.handle( this );
}
}

View File

@@ -0,0 +1,68 @@
package net.md_5.bungee.protocol.packet;
import com.google.common.base.Preconditions;
import io.netty.buffer.ByteBuf;
import java.util.HashMap;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import net.md_5.bungee.protocol.AbstractPacketHandler;
import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.ProtocolConstants;
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class ClientCommand extends DefinedPacket
{
private String command;
private long timestamp;
private long salt;
private Map<String, byte[]> signatures;
private boolean signedPreview;
@Override
public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
{
command = readString( buf );
timestamp = buf.readLong();
salt = buf.readLong();
int cnt = readVarInt( buf );
Preconditions.checkArgument( cnt <= 8, "Too many signatures" );
signatures = new HashMap<>( cnt );
for ( int i = 0; i < cnt; i++ )
{
signatures.put( readString( buf, 16 ), readArray( buf ) );
}
signedPreview = buf.readBoolean();
}
@Override
public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
{
writeString( command, buf );
buf.writeLong( timestamp );
buf.writeLong( salt );
writeVarInt( signatures.size(), buf );
for ( Map.Entry<String, byte[]> entry : signatures.entrySet() )
{
writeString( entry.getKey(), buf );
writeArray( entry.getValue(), buf );
}
buf.writeBoolean( signedPreview );
}
@Override
public void handle(AbstractPacketHandler handler) throws Exception
{
handler.handle( this );
}
}

View File

@@ -23,11 +23,13 @@ import com.mojang.brigadier.tree.LiteralCommandNode;
import com.mojang.brigadier.tree.RootCommandNode;
import io.netty.buffer.ByteBuf;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import lombok.AllArgsConstructor;
@@ -36,6 +38,7 @@ import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import net.md_5.bungee.protocol.AbstractPacketHandler;
import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.ProtocolConstants;
@Data
@NoArgsConstructor
@@ -56,7 +59,7 @@ public class Commands extends DefinedPacket
private RootCommandNode root;
@Override
public void read(ByteBuf buf)
public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
{
int nodeCount = readVarInt( buf );
NetworkNode[] nodes = new NetworkNode[ nodeCount ];
@@ -79,9 +82,7 @@ public class Commands extends DefinedPacket
break;
case NODE_ARGUMENT:
String name = readString( buf );
String parser = readString( buf );
argumentBuilder = RequiredArgumentBuilder.argument( name, ArgumentRegistry.read( parser, buf ) );
argumentBuilder = RequiredArgumentBuilder.argument( name, ArgumentRegistry.read( buf, protocolVersion ) );
if ( ( flags & FLAG_SUGGESTIONS ) != 0 )
{
@@ -126,7 +127,7 @@ public class Commands extends DefinedPacket
}
@Override
public void write(ByteBuf buf)
public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
{
Map<CommandNode, Integer> indexMap = new LinkedHashMap<>();
Deque<CommandNode> nodeQueue = new ArrayDeque<>();
@@ -210,7 +211,7 @@ public class Commands extends DefinedPacket
ArgumentCommandNode argumentNode = (ArgumentCommandNode) node;
writeString( argumentNode.getName(), buf );
ArgumentRegistry.write( argumentNode.getType(), buf );
ArgumentRegistry.write( argumentNode.getType(), buf, protocolVersion );
if ( argumentNode.getCustomSuggestions() != null )
{
@@ -308,6 +309,7 @@ public class Commands extends DefinedPacket
{
private static final Map<String, ArgumentSerializer> PROVIDERS = new HashMap<>();
private static final List<ArgumentSerializer> PROVIDER_LIST = new ArrayList<>();
private static final Map<Class<?>, ProperArgumentSerializer<?>> PROPER_PROVIDERS = new HashMap<>();
//
private static final ArgumentSerializer<Void> VOID = new ArgumentSerializer<Void>()
@@ -492,6 +494,12 @@ public class Commands extends DefinedPacket
writeVarInt( t.getType().ordinal(), buf );
}
@Override
protected int getIntKey()
{
return 5;
}
@Override
protected String getKey()
{
@@ -515,82 +523,113 @@ public class Commands extends DefinedPacket
static
{
PROVIDERS.put( "brigadier:bool", VOID );
PROVIDERS.put( "brigadier:float", FLOAT );
PROVIDERS.put( "brigadier:double", DOUBLE );
PROVIDERS.put( "brigadier:integer", INTEGER );
PROVIDERS.put( "brigadier:long", LONG );
register( "brigadier:bool", VOID );
register( "brigadier:float", FLOAT );
register( "brigadier:double", DOUBLE );
register( "brigadier:integer", INTEGER );
register( "brigadier:long", LONG );
PROVIDERS.put( "brigadier:string", STRING );
register( "brigadier:string", STRING );
PROPER_PROVIDERS.put( StringArgumentType.class, STRING );
PROVIDERS.put( "minecraft:entity", BYTE );
PROVIDERS.put( "minecraft:game_profile", VOID );
PROVIDERS.put( "minecraft:block_pos", VOID );
PROVIDERS.put( "minecraft:column_pos", VOID );
PROVIDERS.put( "minecraft:vec3", VOID );
PROVIDERS.put( "minecraft:vec2", VOID );
PROVIDERS.put( "minecraft:block_state", VOID );
PROVIDERS.put( "minecraft:block_predicate", VOID );
PROVIDERS.put( "minecraft:item_stack", VOID );
PROVIDERS.put( "minecraft:item_predicate", VOID );
PROVIDERS.put( "minecraft:color", VOID );
PROVIDERS.put( "minecraft:component", VOID );
PROVIDERS.put( "minecraft:message", VOID );
PROVIDERS.put( "minecraft:nbt_compound_tag", VOID ); // 1.14
PROVIDERS.put( "minecraft:nbt_tag", VOID ); // 1.14
PROVIDERS.put( "minecraft:nbt", VOID ); // 1.13
PROVIDERS.put( "minecraft:nbt_path", VOID );
PROVIDERS.put( "minecraft:objective", VOID );
PROVIDERS.put( "minecraft:objective_criteria", VOID );
PROVIDERS.put( "minecraft:operation", VOID );
PROVIDERS.put( "minecraft:particle", VOID );
PROVIDERS.put( "minecraft:rotation", VOID );
PROVIDERS.put( "minecraft:scoreboard_slot", VOID );
PROVIDERS.put( "minecraft:score_holder", BYTE );
PROVIDERS.put( "minecraft:swizzle", VOID );
PROVIDERS.put( "minecraft:team", VOID );
PROVIDERS.put( "minecraft:item_slot", VOID );
PROVIDERS.put( "minecraft:resource_location", VOID );
PROVIDERS.put( "minecraft:mob_effect", VOID );
PROVIDERS.put( "minecraft:function", VOID );
PROVIDERS.put( "minecraft:entity_anchor", VOID );
PROVIDERS.put( "minecraft:int_range", VOID );
PROVIDERS.put( "minecraft:float_range", VOID );
PROVIDERS.put( "minecraft:item_enchantment", VOID );
PROVIDERS.put( "minecraft:entity_summon", VOID );
PROVIDERS.put( "minecraft:dimension", VOID );
PROVIDERS.put( "minecraft:time", VOID ); // 1.14
PROVIDERS.put( "minecraft:uuid", VOID ); // 1.16
PROVIDERS.put( "minecraft:test_argument", VOID ); // 1.16, debug
PROVIDERS.put( "minecraft:test_class", VOID ); // 1.16, debug
PROVIDERS.put( "minecraft:angle", VOID ); // 1.16.2
PROVIDERS.put( "minecraft:resource", RAW_STRING ); // 1.18.2
PROVIDERS.put( "minecraft:resource_or_tag", RAW_STRING ); // 1.18.2
register( "minecraft:entity", BYTE );
register( "minecraft:game_profile", VOID );
register( "minecraft:block_pos", VOID );
register( "minecraft:column_pos", VOID );
register( "minecraft:vec3", VOID );
register( "minecraft:vec2", VOID );
register( "minecraft:block_state", VOID );
register( "minecraft:block_predicate", VOID );
register( "minecraft:item_stack", VOID );
register( "minecraft:item_predicate", VOID );
register( "minecraft:color", VOID );
register( "minecraft:component", VOID );
register( "minecraft:message", VOID );
register( "minecraft:nbt_compound_tag", VOID ); // 1.14
register( "minecraft:nbt_tag", VOID ); // 1.14
register( "minecraft:nbt_path", VOID );
register( "minecraft:objective", VOID );
register( "minecraft:objective_criteria", VOID );
register( "minecraft:operation", VOID );
register( "minecraft:particle", VOID );
register( "minecraft:angle", VOID ); // 1.16.2
register( "minecraft:rotation", VOID );
register( "minecraft:scoreboard_slot", VOID );
register( "minecraft:score_holder", BYTE );
register( "minecraft:swizzle", VOID );
register( "minecraft:team", VOID );
register( "minecraft:item_slot", VOID );
register( "minecraft:resource_location", VOID );
register( "minecraft:mob_effect", VOID );
register( "minecraft:function", VOID );
register( "minecraft:entity_anchor", VOID );
register( "minecraft:int_range", VOID );
register( "minecraft:float_range", VOID );
register( "minecraft:item_enchantment", VOID );
register( "minecraft:entity_summon", VOID );
register( "minecraft:dimension", VOID );
register( "minecraft:time", VOID ); // 1.14
register( "minecraft:resource_or_tag", RAW_STRING ); // 1.18.2
register( "minecraft:resource", RAW_STRING ); // 1.18.2
register( "minecraft:template_mirror", VOID ); // 1.19
register( "minecraft:template_rotation", VOID ); // 1.19
register( "minecraft:uuid", VOID ); // 1.16
register( "minecraft:nbt", VOID ); // 1.13 // removed
}
private static ArgumentType<?> read(String key, ByteBuf buf)
private static void register(String name, ArgumentSerializer serializer)
{
ArgumentSerializer reader = PROVIDERS.get( key );
PROVIDERS.put( name, serializer );
PROVIDER_LIST.add( serializer );
}
private static ArgumentType<?> read(ByteBuf buf, int protocolVersion)
{
Object key;
ArgumentSerializer reader;
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_19 )
{
key = readVarInt( buf );
reader = PROVIDER_LIST.get( (Integer) key );
} else
{
key = readString( buf );
reader = PROVIDERS.get( (String) key );
}
Preconditions.checkArgument( reader != null, "No provider for argument " + key );
Object val = reader.read( buf );
return val != null && PROPER_PROVIDERS.containsKey( val.getClass() ) ? (ArgumentType<?>) val : new DummyType( key, reader, val );
}
private static void write(ArgumentType<?> arg, ByteBuf buf)
private static void write(ArgumentType<?> arg, ByteBuf buf, int protocolVersion)
{
ProperArgumentSerializer proper = PROPER_PROVIDERS.get( arg.getClass() );
if ( proper != null )
{
writeString( proper.getKey(), buf );
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_19 )
{
writeVarInt( proper.getIntKey(), buf );
} else
{
writeString( proper.getKey(), buf );
}
proper.write( buf, arg );
} else
{
Preconditions.checkArgument( arg instanceof DummyType, "Non dummy arg " + arg.getClass() );
DummyType dummy = (DummyType) arg;
writeString( dummy.key, buf );
if ( dummy.key instanceof Integer )
{
writeVarInt( (Integer) dummy.key, buf );
} else
{
writeString( (String) dummy.key, buf );
}
dummy.serializer.write( buf, dummy.value );
}
}
@@ -599,7 +638,7 @@ public class Commands extends DefinedPacket
private static class DummyType<T> implements ArgumentType<T>
{
private final String key;
private final Object key;
private final ArgumentSerializer<T> serializer;
private final T value;
@@ -621,6 +660,8 @@ public class Commands extends DefinedPacket
private abstract static class ProperArgumentSerializer<T> extends ArgumentSerializer<T>
{
protected abstract int getIntKey();
protected abstract String getKey();
}
}

View File

@@ -18,19 +18,36 @@ public class EncryptionResponse extends DefinedPacket
private byte[] sharedSecret;
private byte[] verifyToken;
private EncryptionData encryptionData;
@Override
public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
{
sharedSecret = readArray( buf, 128 );
verifyToken = readArray( buf, 128 );
if ( protocolVersion < ProtocolConstants.MINECRAFT_1_19 || buf.readBoolean() )
{
verifyToken = readArray( buf, 128 );
} else
{
encryptionData = new EncryptionData( buf.readLong(), readArray( buf ) );
}
}
@Override
public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
{
writeArray( sharedSecret, buf );
writeArray( verifyToken, buf );
if ( verifyToken != null )
{
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_19 )
{
buf.writeBoolean( true );
}
writeArray( verifyToken, buf );
} else
{
buf.writeLong( encryptionData.getSalt() );
writeArray( encryptionData.getSignature(), buf );
}
}
@Override
@@ -38,4 +55,12 @@ public class EncryptionResponse extends DefinedPacket
{
handler.handle( this );
}
@Data
public static class EncryptionData
{
private final long salt;
private final byte[] signature;
}
}

View File

@@ -9,6 +9,7 @@ import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import net.md_5.bungee.protocol.AbstractPacketHandler;
import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.Location;
import net.md_5.bungee.protocol.ProtocolConstants;
import se.llbit.nbt.Tag;
@@ -37,6 +38,7 @@ public class Login extends DefinedPacket
private boolean normalRespawn;
private boolean debug;
private boolean flat;
private Location deathLocation;
@Override
public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
@@ -63,7 +65,7 @@ public class Login extends DefinedPacket
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_16 )
{
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_16_2 )
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_16_2 && protocolVersion < ProtocolConstants.MINECRAFT_1_19 )
{
dimension = readTag( buf );
} else
@@ -118,6 +120,13 @@ public class Login extends DefinedPacket
debug = buf.readBoolean();
flat = buf.readBoolean();
}
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_19 )
{
if ( buf.readBoolean() )
{
deathLocation = new Location( readString( buf ), buf.readLong() );
}
}
}
@Override
@@ -144,7 +153,7 @@ public class Login extends DefinedPacket
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_16 )
{
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_16_2 )
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_16_2 && protocolVersion < ProtocolConstants.MINECRAFT_1_19 )
{
writeTag( (Tag) dimension, buf );
} else
@@ -199,6 +208,18 @@ public class Login extends DefinedPacket
buf.writeBoolean( debug );
buf.writeBoolean( flat );
}
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_19 )
{
if ( deathLocation != null )
{
buf.writeBoolean( true );
writeString( deathLocation.getDimension(), buf );
buf.writeLong( deathLocation.getPos() );
} else
{
buf.writeBoolean( false );
}
}
}
@Override

View File

@@ -7,6 +7,8 @@ import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import net.md_5.bungee.protocol.AbstractPacketHandler;
import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.PlayerPublicKey;
import net.md_5.bungee.protocol.ProtocolConstants;
@Data
@NoArgsConstructor
@@ -16,17 +18,26 @@ public class LoginRequest extends DefinedPacket
{
private String data;
private PlayerPublicKey publicKey;
@Override
public void read(ByteBuf buf)
public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
{
data = readString( buf, 16 );
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_19 )
{
publicKey = readPublicKey( buf );
}
}
@Override
public void write(ByteBuf buf)
public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
{
writeString( data, buf );
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_19 )
{
writePublicKey( publicKey, buf );
}
}
@Override

View File

@@ -8,6 +8,7 @@ import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import net.md_5.bungee.protocol.AbstractPacketHandler;
import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.Property;
import net.md_5.bungee.protocol.ProtocolConstants;
@Data
@@ -19,6 +20,7 @@ public class LoginSuccess extends DefinedPacket
private UUID uuid;
private String username;
private Property[] properties;
@Override
public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
@@ -31,6 +33,10 @@ public class LoginSuccess extends DefinedPacket
uuid = UUID.fromString( readString( buf ) );
}
username = readString( buf );
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_19 )
{
properties = readProperties( buf );
}
}
@Override
@@ -44,6 +50,10 @@ public class LoginSuccess extends DefinedPacket
writeString( uuid.toString(), buf );
}
writeString( username, buf );
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_19 )
{
writeProperties( properties, buf );
}
}
@Override

View File

@@ -0,0 +1,84 @@
package net.md_5.bungee.protocol.packet;
import io.netty.buffer.ByteBuf;
import java.util.UUID;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import net.md_5.bungee.protocol.AbstractPacketHandler;
import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.ProtocolConstants;
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class PlayerChat extends DefinedPacket
{
private static final UUID EMPTY_UUID = new UUID( 0L, 0L );
private String signedContent;
private String unsignedContent; // nullable
private UUID sender;
private int typeId;
private String displayName;
private String teamName; // nullable
private long timestamp;
private long salt;
private byte[] signature;
@Override
public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
{
signedContent = readString( buf, 262144 );
if ( buf.readBoolean() )
{
unsignedContent = readString( buf, 262144 );
}
typeId = readVarInt( buf );
sender = readUUID( buf );
displayName = readString( buf, 262144 );
if ( buf.readBoolean() )
{
teamName = readString( buf, 262144 );
}
timestamp = buf.readLong();
salt = buf.readLong();
signature = readArray( buf );
}
@Override
public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
{
writeString( signedContent, buf );
if ( unsignedContent != null )
{
buf.writeBoolean( true );
writeString( unsignedContent, buf );
} else
{
buf.writeBoolean( false );
}
writeVarInt( typeId, buf );
writeUUID( sender, buf );
writeString( displayName, buf );
if ( teamName != null )
{
buf.writeBoolean( true );
writeString( teamName, buf );
} else
{
buf.writeBoolean( false );
}
buf.writeLong( timestamp );
buf.writeLong( salt );
writeArray( signature, buf );
}
@Override
public void handle(AbstractPacketHandler handler) throws Exception
{
handler.handle( this );
}
}

View File

@@ -7,6 +7,8 @@ import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import net.md_5.bungee.protocol.AbstractPacketHandler;
import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.PlayerPublicKey;
import net.md_5.bungee.protocol.Property;
import net.md_5.bungee.protocol.ProtocolConstants;
@Data
@@ -31,31 +33,17 @@ public class PlayerListItem extends DefinedPacket
{
case ADD_PLAYER:
item.username = DefinedPacket.readString( buf );
item.properties = new String[ DefinedPacket.readVarInt( buf ) ][];
for ( int j = 0; j < item.properties.length; j++ )
{
String name = DefinedPacket.readString( buf );
String value = DefinedPacket.readString( buf );
if ( buf.readBoolean() )
{
item.properties[j] = new String[]
{
name, value, DefinedPacket.readString( buf )
};
} else
{
item.properties[j] = new String[]
{
name, value
};
}
}
item.properties = DefinedPacket.readProperties( buf );
item.gamemode = DefinedPacket.readVarInt( buf );
item.ping = DefinedPacket.readVarInt( buf );
if ( buf.readBoolean() )
{
item.displayName = DefinedPacket.readString( buf );
}
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_19 )
{
item.publicKey = readPublicKey( buf );
}
break;
case UPDATE_GAMEMODE:
item.gamemode = DefinedPacket.readVarInt( buf );
@@ -84,20 +72,7 @@ public class PlayerListItem extends DefinedPacket
{
case ADD_PLAYER:
DefinedPacket.writeString( item.username, buf );
DefinedPacket.writeVarInt( item.properties.length, buf );
for ( String[] prop : item.properties )
{
DefinedPacket.writeString( prop[0], buf );
DefinedPacket.writeString( prop[1], buf );
if ( prop.length >= 3 )
{
buf.writeBoolean( true );
DefinedPacket.writeString( prop[2], buf );
} else
{
buf.writeBoolean( false );
}
}
DefinedPacket.writeProperties( item.properties, buf );
DefinedPacket.writeVarInt( item.gamemode, buf );
DefinedPacket.writeVarInt( item.ping, buf );
buf.writeBoolean( item.displayName != null );
@@ -105,6 +80,10 @@ public class PlayerListItem extends DefinedPacket
{
DefinedPacket.writeString( item.displayName, buf );
}
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_19 )
{
writePublicKey( item.publicKey, buf );
}
break;
case UPDATE_GAMEMODE:
DefinedPacket.writeVarInt( item.gamemode, buf );
@@ -148,7 +127,8 @@ public class PlayerListItem extends DefinedPacket
// ADD_PLAYER
private String username;
private String[][] properties;
private Property[] properties;
private PlayerPublicKey publicKey;
// ADD_PLAYER & UPDATE_GAMEMODE
private int gamemode;

View File

@@ -7,6 +7,7 @@ import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import net.md_5.bungee.protocol.AbstractPacketHandler;
import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.Location;
import net.md_5.bungee.protocol.ProtocolConstants;
import se.llbit.nbt.Tag;
@@ -27,13 +28,14 @@ public class Respawn extends DefinedPacket
private boolean debug;
private boolean flat;
private boolean copyMeta;
private Location deathLocation;
@Override
public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
{
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_16 )
{
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_16_2 )
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_16_2 && protocolVersion < ProtocolConstants.MINECRAFT_1_19 )
{
dimension = readTag( buf );
} else
@@ -64,6 +66,13 @@ public class Respawn extends DefinedPacket
{
levelType = readString( buf );
}
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_19 )
{
if ( buf.readBoolean() )
{
deathLocation = new Location( readString( buf ), buf.readLong() );
}
}
}
@Override
@@ -71,7 +80,7 @@ public class Respawn extends DefinedPacket
{
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_16 )
{
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_16_2 )
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_16_2 && protocolVersion < ProtocolConstants.MINECRAFT_1_19 )
{
writeTag( (Tag) dimension, buf );
} else
@@ -102,6 +111,18 @@ public class Respawn extends DefinedPacket
{
writeString( levelType, buf );
}
if ( protocolVersion >= ProtocolConstants.MINECRAFT_1_19 )
{
if ( deathLocation != null )
{
buf.writeBoolean( true );
writeString( deathLocation.getDimension(), buf );
buf.writeLong( deathLocation.getPos() );
} else
{
buf.writeBoolean( false );
}
}
}
@Override

View File

@@ -0,0 +1,41 @@
package net.md_5.bungee.protocol.packet;
import io.netty.buffer.ByteBuf;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import net.md_5.bungee.protocol.AbstractPacketHandler;
import net.md_5.bungee.protocol.DefinedPacket;
import net.md_5.bungee.protocol.ProtocolConstants;
@Data
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class SystemChat extends DefinedPacket
{
private String message;
private int position;
@Override
public void read(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
{
message = readString( buf, 262144 );
position = readVarInt( buf );
}
@Override
public void write(ByteBuf buf, ProtocolConstants.Direction direction, int protocolVersion)
{
writeString( message, buf );
writeVarInt( position, buf );
}
@Override
public void handle(AbstractPacketHandler handler) throws Exception
{
handler.handle( this );
}
}