Finished basic connection process

This commit is contained in:
Nanit 2020-11-26 10:42:18 +02:00
parent 78dc64f636
commit 011a48724d
19 changed files with 277 additions and 33 deletions

View File

@ -2,7 +2,9 @@ package ru.nanit.limbo;
import ru.nanit.limbo.server.LimboServer; import ru.nanit.limbo.server.LimboServer;
import ru.nanit.limbo.util.Logger; import ru.nanit.limbo.util.Logger;
import ru.nanit.limbo.world.DefaultDimension;
import java.io.InputStream;
import java.nio.file.Paths; import java.nio.file.Paths;
public final class NanoLimbo { public final class NanoLimbo {
@ -11,6 +13,7 @@ public final class NanoLimbo {
public void start() throws Exception { public void start() throws Exception {
LimboConfig.load(Paths.get("./settings.properties")); LimboConfig.load(Paths.get("./settings.properties"));
DefaultDimension.init();
server = new LimboServer(); server = new LimboServer();
server.start(); server.start();
@ -23,4 +26,8 @@ public final class NanoLimbo {
Logger.error("Cannot start server: ", e); Logger.error("Cannot start server: ", e);
} }
} }
public static InputStream getResource(String path){
return NanoLimbo.class.getResourceAsStream(path);
}
} }

View File

@ -4,7 +4,11 @@ import io.netty.channel.Channel;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInboundHandlerAdapter;
import ru.nanit.limbo.LimboConfig;
import ru.nanit.limbo.protocol.packets.login.*; import ru.nanit.limbo.protocol.packets.login.*;
import ru.nanit.limbo.protocol.packets.play.PacketJoinGame;
import ru.nanit.limbo.protocol.packets.play.PacketPlayerPositionAndLook;
import ru.nanit.limbo.protocol.packets.play.PacketUpdateViewPos;
import ru.nanit.limbo.protocol.registry.Version; import ru.nanit.limbo.protocol.registry.Version;
import ru.nanit.limbo.protocol.pipeline.PacketDecoder; import ru.nanit.limbo.protocol.pipeline.PacketDecoder;
import ru.nanit.limbo.protocol.pipeline.PacketEncoder; import ru.nanit.limbo.protocol.pipeline.PacketEncoder;
@ -14,13 +18,18 @@ import ru.nanit.limbo.protocol.packets.status.PacketStatusRequest;
import ru.nanit.limbo.protocol.packets.status.PacketStatusResponse; import ru.nanit.limbo.protocol.packets.status.PacketStatusResponse;
import ru.nanit.limbo.protocol.registry.State; import ru.nanit.limbo.protocol.registry.State;
import ru.nanit.limbo.server.LimboServer; import ru.nanit.limbo.server.LimboServer;
import ru.nanit.limbo.util.Logger;
import ru.nanit.limbo.util.UuidUtil; import ru.nanit.limbo.util.UuidUtil;
import ru.nanit.limbo.world.DefaultDimension;
import java.util.concurrent.ThreadLocalRandom;
public class ClientConnection extends ChannelInboundHandlerAdapter { public class ClientConnection extends ChannelInboundHandlerAdapter {
private final LimboServer server; private final LimboServer server;
private final Channel channel; private final Channel channel;
private State state;
private String username; private String username;
public ClientConnection(Channel channel, LimboServer server){ public ClientConnection(Channel channel, LimboServer server){
@ -28,6 +37,19 @@ public class ClientConnection extends ChannelInboundHandlerAdapter {
this.server = server; this.server = server;
} }
@Override
public void channelInactive(ChannelHandlerContext ctx) throws Exception {
if (state.equals(State.PLAY)){
Logger.info("Player %s disconnected", this.username);
}
super.channelInactive(ctx);
}
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
Logger.error("Unhandled exception: %s", cause.getMessage());
}
@Override @Override
public void channelRead(ChannelHandlerContext ctx, Object msg) { public void channelRead(ChannelHandlerContext ctx, Object msg) {
handlePacket(msg); handlePacket(msg);
@ -51,7 +73,6 @@ public class ClientConnection extends ChannelInboundHandlerAdapter {
if (packet instanceof PacketLoginStart){ if (packet instanceof PacketLoginStart){
this.username = ((PacketLoginStart) packet).getUsername(); this.username = ((PacketLoginStart) packet).getUsername();
// Limbo always in offline mode. Online mode set on proxy side
PacketLoginSuccess loginSuccess = new PacketLoginSuccess(); PacketLoginSuccess loginSuccess = new PacketLoginSuccess();
loginSuccess.setUuid(UuidUtil.getOfflineModeUuid(this.username)); loginSuccess.setUuid(UuidUtil.getOfflineModeUuid(this.username));
@ -59,9 +80,49 @@ public class ClientConnection extends ChannelInboundHandlerAdapter {
sendPacket(loginSuccess); sendPacket(loginSuccess);
updateState(State.PLAY); updateState(State.PLAY);
Logger.info("Player %s connected", this.username);
startJoinProcess();
} }
} }
private void startJoinProcess(){
PacketJoinGame joinGame = new PacketJoinGame();
joinGame.setEntityId(0);
joinGame.setEnableRespawnScreen(true);
joinGame.setFlat(false);
joinGame.setGameMode(2);
joinGame.setHardcore(false);
joinGame.setMaxPlayers(LimboConfig.getMaxPlayers());
joinGame.setPreviousGameMode(-1);
joinGame.setReducedDebugInfo(false);
joinGame.setDebug(false);
joinGame.setViewDistance(2);
joinGame.setWorldName("minecraft:world");
joinGame.setWorldNames("minecraft:world");
joinGame.setHashedSeed(0);
joinGame.setDimensionCodec(DefaultDimension.getCodec());
joinGame.setDimension(DefaultDimension.getDimension());
PacketPlayerPositionAndLook positionAndLook = new PacketPlayerPositionAndLook();
positionAndLook.setX(0.0);
positionAndLook.setY(2.0);
positionAndLook.setZ(0.0);
positionAndLook.setYaw(90.0F);
positionAndLook.setPitch(0.0F);
positionAndLook.setTeleportId(ThreadLocalRandom.current().nextInt());
PacketUpdateViewPos updateViewPos = new PacketUpdateViewPos();
updateViewPos.setChunkX(0);
updateViewPos.setChunkY(0);
sendPacket(joinGame);
sendPacket(positionAndLook);
sendPacket(updateViewPos);
}
public void sendPacket(Object packet){ public void sendPacket(Object packet){
if (isConnected()) if (isConnected())
channel.writeAndFlush(packet, channel.voidPromise()); channel.writeAndFlush(packet, channel.voidPromise());
@ -89,6 +150,8 @@ public class ClientConnection extends ChannelInboundHandlerAdapter {
} }
public void updateState(State state){ public void updateState(State state){
this.state = state;
channel.pipeline().get(PacketDecoder.class).updateState(state); channel.pipeline().get(PacketDecoder.class).updateState(state);
channel.pipeline().get(PacketEncoder.class).updateState(state); channel.pipeline().get(PacketEncoder.class).updateState(state);
} }
@ -101,5 +164,7 @@ public class ClientConnection extends ChannelInboundHandlerAdapter {
decoder.updateState(state); decoder.updateState(state);
encoder.updateVersion(version); encoder.updateVersion(version);
encoder.updateState(state); encoder.updateState(state);
this.state = state;
} }
} }

View File

@ -4,8 +4,8 @@ import ru.nanit.limbo.protocol.registry.Version;
public interface Packet { public interface Packet {
void encode(ByteMessage msg, Direction direction, Version version); void encode(ByteMessage msg, Version version);
void decode(ByteMessage msg, Direction direction, Version version); void decode(ByteMessage msg, Version version);
} }

View File

@ -5,7 +5,7 @@ import ru.nanit.limbo.protocol.registry.Version;
public interface PacketIn extends Packet { public interface PacketIn extends Packet {
@Override @Override
default void encode(ByteMessage msg, Direction direction, Version version) { default void encode(ByteMessage msg, Version version) {
// Can be ignored for incoming packets // Can be ignored for incoming packets
} }

View File

@ -5,7 +5,7 @@ import ru.nanit.limbo.protocol.registry.Version;
public interface PacketOut extends Packet { public interface PacketOut extends Packet {
@Override @Override
default void decode(ByteMessage msg, Direction direction, Version version) { default void decode(ByteMessage msg, Version version) {
// Can be ignored for outgoing packets // Can be ignored for outgoing packets
} }

View File

@ -2,7 +2,6 @@ package ru.nanit.limbo.protocol.packets;
import ru.nanit.limbo.protocol.ByteMessage; import ru.nanit.limbo.protocol.ByteMessage;
import ru.nanit.limbo.protocol.Packet; import ru.nanit.limbo.protocol.Packet;
import ru.nanit.limbo.protocol.Direction;
import ru.nanit.limbo.protocol.registry.Version; import ru.nanit.limbo.protocol.registry.Version;
public class PacketHandshake implements Packet { public class PacketHandshake implements Packet {
@ -45,7 +44,7 @@ public class PacketHandshake implements Packet {
} }
@Override @Override
public void encode(ByteMessage msg, Direction direction, Version version) { public void encode(ByteMessage msg, Version version) {
msg.writeVarInt(this.version.getProtocolNumber()); msg.writeVarInt(this.version.getProtocolNumber());
msg.writeString(host); msg.writeString(host);
msg.writeShort(port); msg.writeShort(port);
@ -53,7 +52,7 @@ public class PacketHandshake implements Packet {
} }
@Override @Override
public void decode(ByteMessage msg, Direction direction, Version version) { public void decode(ByteMessage msg, Version version) {
this.version = Version.of(msg.readVarInt()); this.version = Version.of(msg.readVarInt());
this.host = msg.readString(); this.host = msg.readString();
this.port = msg.readUnsignedShort(); this.port = msg.readUnsignedShort();

View File

@ -1,7 +1,6 @@
package ru.nanit.limbo.protocol.packets.login; package ru.nanit.limbo.protocol.packets.login;
import ru.nanit.limbo.protocol.ByteMessage; import ru.nanit.limbo.protocol.ByteMessage;
import ru.nanit.limbo.protocol.Direction;
import ru.nanit.limbo.protocol.PacketOut; import ru.nanit.limbo.protocol.PacketOut;
import ru.nanit.limbo.protocol.registry.Version; import ru.nanit.limbo.protocol.registry.Version;
@ -14,7 +13,7 @@ public class PacketDisconnect implements PacketOut {
} }
@Override @Override
public void encode(ByteMessage msg, Direction direction, Version version) { public void encode(ByteMessage msg, Version version) {
msg.writeString(String.format("{\"text\": \"%s\"}", reason)); msg.writeString(String.format("{\"text\": \"%s\"}", reason));
} }

View File

@ -12,7 +12,7 @@ public class PacketLoginStart implements PacketIn {
} }
@Override @Override
public void decode(ByteMessage msg, Direction direction, Version version) { public void decode(ByteMessage msg, Version version) {
this.username = msg.readString(); this.username = msg.readString();
} }

View File

@ -1,7 +1,6 @@
package ru.nanit.limbo.protocol.packets.login; package ru.nanit.limbo.protocol.packets.login;
import ru.nanit.limbo.protocol.ByteMessage; import ru.nanit.limbo.protocol.ByteMessage;
import ru.nanit.limbo.protocol.Direction;
import ru.nanit.limbo.protocol.PacketOut; import ru.nanit.limbo.protocol.PacketOut;
import ru.nanit.limbo.protocol.registry.Version; import ru.nanit.limbo.protocol.registry.Version;
@ -21,7 +20,7 @@ public class PacketLoginSuccess implements PacketOut {
} }
@Override @Override
public void encode(ByteMessage msg, Direction direction, Version version) { public void encode(ByteMessage msg, Version version) {
msg.writeUuid(uuid); msg.writeUuid(uuid);
msg.writeString(username); msg.writeString(username);
} }

View File

@ -2,20 +2,16 @@ package ru.nanit.limbo.protocol.packets.play;
import net.kyori.adventure.nbt.CompoundBinaryTag; import net.kyori.adventure.nbt.CompoundBinaryTag;
import ru.nanit.limbo.protocol.ByteMessage; import ru.nanit.limbo.protocol.ByteMessage;
import ru.nanit.limbo.protocol.Direction;
import ru.nanit.limbo.protocol.PacketOut; import ru.nanit.limbo.protocol.PacketOut;
import ru.nanit.limbo.protocol.registry.Version; import ru.nanit.limbo.protocol.registry.Version;
import java.util.List;
public class PacketJoinGame implements PacketOut { public class PacketJoinGame implements PacketOut {
private int entityId; private int entityId;
private boolean isHardcore = false; private boolean isHardcore = false;
private int gameMode = 2; private int gameMode = 2;
private int previousGameMode = -1; private int previousGameMode = -1;
private int worldCount = 1; private String[] worldNames;
private List<String> worldNames;
private CompoundBinaryTag dimensionCodec; private CompoundBinaryTag dimensionCodec;
private CompoundBinaryTag dimension; private CompoundBinaryTag dimension;
private String worldName; private String worldName;
@ -43,11 +39,7 @@ public class PacketJoinGame implements PacketOut {
this.previousGameMode = previousGameMode; this.previousGameMode = previousGameMode;
} }
public void setWorldCount(int worldCount) { public void setWorldNames(String... worldNames) {
this.worldCount = worldCount;
}
public void setWorldNames(List<String> worldNames) {
this.worldNames = worldNames; this.worldNames = worldNames;
} }
@ -92,8 +84,22 @@ public class PacketJoinGame implements PacketOut {
} }
@Override @Override
public void encode(ByteMessage msg, Direction direction, Version version) { public void encode(ByteMessage msg, Version version) {
msg.writeInt(entityId);
msg.writeBoolean(isHardcore);
msg.writeByte(gameMode);
msg.writeByte(previousGameMode);
msg.writeStringsArray(worldNames);
msg.writeCompoundTag(dimensionCodec);
msg.writeCompoundTag(dimension);
msg.writeString(worldName);
msg.writeLong(hashedSeed);
msg.writeVarInt(maxPlayers);
msg.writeVarInt(viewDistance);
msg.writeBoolean(reducedDebugInfo);
msg.writeBoolean(enableRespawnScreen);
msg.writeBoolean(isDebug);
msg.writeBoolean(isFlat);
} }
} }

View File

@ -0,0 +1,56 @@
package ru.nanit.limbo.protocol.packets.play;
import ru.nanit.limbo.protocol.ByteMessage;
import ru.nanit.limbo.protocol.PacketOut;
import ru.nanit.limbo.protocol.registry.Version;
public class PacketPlayerPositionAndLook implements PacketOut {
private double x;
private double y;
private double z;
private float yaw;
private float pitch;
private byte flags = 0x01;
private int teleportId;
public void setX(double x) {
this.x = x;
}
public void setY(double y) {
this.y = y;
}
public void setZ(double z) {
this.z = z;
}
public void setYaw(float yaw) {
this.yaw = yaw;
}
public void setPitch(float pitch) {
this.pitch = pitch;
}
public void setFlags(byte flags) {
this.flags = flags;
}
public void setTeleportId(int teleportId) {
this.teleportId = teleportId;
}
@Override
public void encode(ByteMessage msg, Version version) {
msg.writeDouble(x);
msg.writeDouble(y);
msg.writeDouble(z);
msg.writeFloat(yaw);
msg.writeFloat(pitch);
msg.writeByte(flags);
msg.writeVarInt(teleportId);
}
}

View File

@ -0,0 +1,26 @@
package ru.nanit.limbo.protocol.packets.play;
import ru.nanit.limbo.protocol.ByteMessage;
import ru.nanit.limbo.protocol.PacketOut;
import ru.nanit.limbo.protocol.registry.Version;
public class PacketUpdateViewPos implements PacketOut {
private int chunkX;
private int chunkY;
public void setChunkX(int chunkX) {
this.chunkX = chunkX;
}
public void setChunkY(int chunkY) {
this.chunkY = chunkY;
}
@Override
public void encode(ByteMessage msg, Version version) {
msg.writeVarInt(chunkX);
msg.writeVarInt(chunkY);
}
}

View File

@ -2,7 +2,6 @@ package ru.nanit.limbo.protocol.packets.status;
import ru.nanit.limbo.protocol.ByteMessage; import ru.nanit.limbo.protocol.ByteMessage;
import ru.nanit.limbo.protocol.Packet; import ru.nanit.limbo.protocol.Packet;
import ru.nanit.limbo.protocol.Direction;
import ru.nanit.limbo.protocol.registry.Version; import ru.nanit.limbo.protocol.registry.Version;
public class PacketStatusPing implements Packet { public class PacketStatusPing implements Packet {
@ -10,12 +9,12 @@ public class PacketStatusPing implements Packet {
private long randomId; private long randomId;
@Override @Override
public void encode(ByteMessage msg, Direction direction, Version version) { public void encode(ByteMessage msg, Version version) {
msg.writeLong(randomId); msg.writeLong(randomId);
} }
@Override @Override
public void decode(ByteMessage msg, Direction direction, Version version) { public void decode(ByteMessage msg, Version version) {
this.randomId = msg.readLong(); this.randomId = msg.readLong();
} }

View File

@ -6,7 +6,7 @@ import ru.nanit.limbo.protocol.registry.Version;
public class PacketStatusRequest implements PacketIn { public class PacketStatusRequest implements PacketIn {
@Override @Override
public void decode(ByteMessage msg, Direction direction, Version version) { public void decode(ByteMessage msg, Version version) {
} }

View File

@ -9,7 +9,7 @@ public class PacketStatusResponse implements PacketOut {
private static final String TEMPLATE = "{ \"version\": { \"name\": \"%s\", \"protocol\": %d }, \"players\": { \"max\": %d, \"online\": %d, \"sample\": [] }, \"description\": %s }"; private static final String TEMPLATE = "{ \"version\": { \"name\": \"%s\", \"protocol\": %d }, \"players\": { \"max\": %d, \"online\": %d, \"sample\": [] }, \"description\": %s }";
@Override @Override
public void encode(ByteMessage msg, Direction direction, Version version) { public void encode(ByteMessage msg, Version version) {
String ver = LimboConfig.getPingData().getVersion(); String ver = LimboConfig.getPingData().getVersion();
String desc = LimboConfig.getPingData().getDescription(); String desc = LimboConfig.getPingData().getDescription();
String json = getResponseJson(ver, version.getProtocolNumber(), LimboConfig.getMaxPlayers(), 0, desc); String json = getResponseJson(ver, version.getProtocolNumber(), LimboConfig.getMaxPlayers(), 0, desc);

View File

@ -30,7 +30,7 @@ public class PacketDecoder extends MessageToMessageDecoder<ByteBuf> {
if (packet != null){ if (packet != null){
try { try {
packet.decode(msg, Direction.SERVER, mappings.getVersion()); packet.decode(msg, mappings.getVersion());
} catch (Exception e){ } catch (Exception e){
Logger.warning("Cannot decode packet 0x%s: %s", Integer.toHexString(packetId), e.getMessage()); Logger.warning("Cannot decode packet 0x%s: %s", Integer.toHexString(packetId), e.getMessage());
} }

View File

@ -5,7 +5,6 @@ import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder; import io.netty.handler.codec.MessageToByteEncoder;
import ru.nanit.limbo.protocol.ByteMessage; import ru.nanit.limbo.protocol.ByteMessage;
import ru.nanit.limbo.protocol.Packet; import ru.nanit.limbo.protocol.Packet;
import ru.nanit.limbo.protocol.Direction;
import ru.nanit.limbo.protocol.registry.Version; import ru.nanit.limbo.protocol.registry.Version;
import ru.nanit.limbo.protocol.registry.State; import ru.nanit.limbo.protocol.registry.State;
import ru.nanit.limbo.util.Logger; import ru.nanit.limbo.util.Logger;
@ -35,7 +34,7 @@ public class PacketEncoder extends MessageToByteEncoder<Packet> {
msg.writeVarInt(packetId); msg.writeVarInt(packetId);
try { try {
packet.encode(msg, Direction.CLIENT, version); packet.encode(msg, version);
} catch (Exception e){ } catch (Exception e){
Logger.warning("Cannot encode packet 0x%s: %s", Integer.toHexString(packetId), e.getMessage()); Logger.warning("Cannot encode packet 0x%s: %s", Integer.toHexString(packetId), e.getMessage());
} }

View File

@ -4,6 +4,8 @@ import ru.nanit.limbo.protocol.Packet;
import ru.nanit.limbo.protocol.packets.*; import ru.nanit.limbo.protocol.packets.*;
import ru.nanit.limbo.protocol.packets.login.*; import ru.nanit.limbo.protocol.packets.login.*;
import ru.nanit.limbo.protocol.packets.play.PacketJoinGame; import ru.nanit.limbo.protocol.packets.play.PacketJoinGame;
import ru.nanit.limbo.protocol.packets.play.PacketPlayerPositionAndLook;
import ru.nanit.limbo.protocol.packets.play.PacketUpdateViewPos;
import ru.nanit.limbo.protocol.packets.status.PacketStatusPing; import ru.nanit.limbo.protocol.packets.status.PacketStatusPing;
import ru.nanit.limbo.protocol.packets.status.PacketStatusRequest; import ru.nanit.limbo.protocol.packets.status.PacketStatusRequest;
import ru.nanit.limbo.protocol.packets.status.PacketStatusResponse; import ru.nanit.limbo.protocol.packets.status.PacketStatusResponse;
@ -37,6 +39,8 @@ public enum State {
PLAY(3){ PLAY(3){
{ {
clientBound.register(Version.V1_16_4, 0x24, PacketJoinGame::new); clientBound.register(Version.V1_16_4, 0x24, PacketJoinGame::new);
clientBound.register(Version.V1_16_4, 0x34, PacketPlayerPositionAndLook::new);
clientBound.register(Version.V1_16_4, 0x40, PacketUpdateViewPos::new);
} }
}; };

View File

@ -0,0 +1,85 @@
package ru.nanit.limbo.world;
import net.kyori.adventure.nbt.CompoundBinaryTag;
import net.kyori.adventure.nbt.ListBinaryTag;
public final class DefaultDimension {
private static CompoundBinaryTag CODEC;
private static CompoundBinaryTag DIMENSION;
public static void init(){
DIMENSION = CompoundBinaryTag.builder()
.putString("name", "minecraft:the_end")
.putByte("piglin_safe", (byte) 0)
.putByte("natural", (byte) 0)
.putFloat("ambient_light", 0.0F)
.putString("infiniburn", "minecraft:infiniburn_end")
.putByte("respawn_anchor_works", (byte) 0)
.putByte("has_skylight", (byte) 0)
.putByte("bed_works", (byte) 0)
.putString("effects", "minecraft:the_end")
.putLong("fixed_time", 6000L)
.putByte("has_raids", (byte) 1)
.putInt("logical_height", 256)
.putDouble("coordinate_scale", 1.0)
.putByte("ultrawarm", (byte) 0)
.putByte("has_ceiling", (byte) 0)
.build();
CompoundBinaryTag dimensionData = CompoundBinaryTag.builder()
.putString("name", "minecraft:the_end")
.putInt("id", 2)
.put("element", DIMENSION)
.build();
CompoundBinaryTag plains = CompoundBinaryTag.builder()
.putString("name", "minecraft:plains")
.putInt("id", 1)
.put("element", CompoundBinaryTag.builder()
.putString("precipitation", "rain")
.putFloat("depth", 0.125F)
.putFloat("temperature", 0.8F)
.putFloat("scale", 0.05F)
.putFloat("downfall", 0.4F)
.putString("category", "plains")
.put("effects", CompoundBinaryTag.builder()
.putInt("sky_color", 7907327)
.putInt("water_fog_color", 329011)
.putInt("fog_color", 12638463)
.putInt("water_color", 4159204)
.put("mood_sound", CompoundBinaryTag.builder()
.putInt("tick_delay", 6000)
.putFloat("offset", 2.0F)
.putString("sound", "minecraft:ambient.cave")
.putInt("block_search_extent", 8)
.build())
.build())
.build())
.build();
CODEC = CompoundBinaryTag.builder()
.put("minecraft:dimension_type", CompoundBinaryTag.builder()
.putString("type", "minecraft:dimension_type")
.put("value", ListBinaryTag.builder()
.add(dimensionData)
.build())
.build())
.put("minecraft:worldgen/biome", CompoundBinaryTag.builder()
.putString("type", "minecraft:worldgen/biome")
.put("value", ListBinaryTag.builder()
.add(plains)
.build())
.build())
.build();
}
public static CompoundBinaryTag getCodec(){
return CODEC;
}
public static CompoundBinaryTag getDimension(){
return DIMENSION;
}
}