Archived
0

5 Commits

Author SHA1 Message Date
c705a2090d Scoreboard works 2021-05-08 17:58:15 +03:00
6dbf785d22 ScoreboardUpdateScorePacket 2021-05-08 17:01:32 +03:00
050130be60 ScoreboardObjectivePacket 2021-05-08 15:36:10 +03:00
9ccb9a6221 TeamsPacket 2021-05-08 04:48:28 +03:00
fda80d9f9d ScoreboardDisplayPacket 2021-05-06 17:05:20 +03:00
40 changed files with 423 additions and 501 deletions

View File

@@ -1,13 +1,12 @@
# MC-SERVER # MC-SERVER
![version: 1.1](https://img.shields.io/badge/version-1.1-05b.svg?style=flat) ![version: 1.0-SNAPSHOT](https://img.shields.io/badge/version-1.0-05b.svg?style=flat)
![codename: VOID](https://img.shields.io/badge/codename-VOID-509.svg?style=flat) ![codename: ZERO](https://img.shields.io/badge/codename-ZERO-509.svg?style=flat)
![protocol: 1.12.2](https://img.shields.io/badge/protocol-1.12.2-075.svg?style=flat) ![protocol: 1.12.2](https://img.shields.io/badge/protocol-1.12.2-075.svg?style=flat)
Написанный с нуля сервер **Minecraft 1.12.2**. Написанный с нуля сервер **Minecraft 1.12.2**.
На данный момент сервер может показывать о себе информацию в списке серверов (motd, онлайн, иконка) и позволять На данный момент может только показывать информацию о себе. Подключение к серверу не возможно.
игрокам подключиться к себе. Загружается пустой мир.
--- ---

View File

@@ -1,3 +1,3 @@
project.group=mc-project project.group=mc-project
project.name=mc-server project.name=mc-server
project.version=1.1 project.version=1.1-SNAPSHOT

View File

@@ -2,16 +2,12 @@ package mc.protocol;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import lombok.EqualsAndHashCode; import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import mc.protocol.api.ConnectionContext; import mc.protocol.api.ConnectionContext;
import mc.protocol.packets.ServerSidePacket; import mc.protocol.packets.ServerSidePacket;
import mc.protocol.pool.Passivable; import mc.protocol.pool.Passivable;
import java.util.Map;
import java.util.Optional;
@EqualsAndHashCode @EqualsAndHashCode
public class NettyConnectionContext implements ConnectionContext, Passivable { public class NettyConnectionContext implements ConnectionContext, Passivable {
@@ -19,14 +15,6 @@ public class NettyConnectionContext implements ConnectionContext, Passivable {
@Setter @Setter
private ChannelHandlerContext ctx; private ChannelHandlerContext ctx;
/**
* @deprecated костыль
*/
@Deprecated
@Getter
@Setter
private boolean usedContext;
@Override @Override
public State getState() { public State getState() {
return ctx.channel().attr(NetworkAttributes.STATE).get(); return ctx.channel().attr(NetworkAttributes.STATE).get();
@@ -37,27 +25,6 @@ public class NettyConnectionContext implements ConnectionContext, Passivable {
ctx.channel().attr(NetworkAttributes.STATE).set(state); ctx.channel().attr(NetworkAttributes.STATE).set(state);
} }
/**
* @deprecated костыль
*/
@Deprecated
@Override
public <T> void setCustomProperty(String key, T value) {
Map<String, Object> map = ctx.channel().attr(NetworkAttributes.CUSTOM_PROPERTIES).get();
map.put(key, value);
}
/**
* @deprecated костыль
*/
@Deprecated
@SuppressWarnings("unchecked")
@Override
public <T> Optional<T> getCustomProperty(String key, Class<T> classResult) {
Map<String, Object> map = ctx.channel().attr(NetworkAttributes.CUSTOM_PROPERTIES).get();
return (Optional<T>) Optional.ofNullable(map.getOrDefault(key, null));
}
@Override @Override
public void send(ServerSidePacket packet) { public void send(ServerSidePacket packet) {
ctx.write(packet); ctx.write(packet);

View File

@@ -3,16 +3,8 @@ package mc.protocol;
import io.netty.util.AttributeKey; import io.netty.util.AttributeKey;
import lombok.experimental.UtilityClass; import lombok.experimental.UtilityClass;
import java.util.Map;
@UtilityClass @UtilityClass
public class NetworkAttributes { public class NetworkAttributes {
public static final AttributeKey<State> STATE = AttributeKey.newInstance("STATE"); public static final AttributeKey<State> STATE = AttributeKey.newInstance("STATE");
/**
* @deprecated костыль
*/
@Deprecated
public static final AttributeKey<Map<String, Object>> CUSTOM_PROPERTIES = AttributeKey.newInstance("CUSTOM_PROPERTIES");
} }

View File

@@ -3,20 +3,14 @@ package mc.protocol;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler; import io.netty.channel.SimpleChannelInboundHandler;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import mc.protocol.packets.ClientSidePacket; import mc.protocol.packets.ClientSidePacket;
import mc.protocol.event.EventBus; import mc.protocol.event.EventBus;
import mc.protocol.pool.PacketPool; import mc.protocol.pool.PacketPool;
import org.apache.commons.pool2.ObjectPool; import org.apache.commons.pool2.ObjectPool;
import java.io.IOException;
@Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
public class PacketInboundHandler extends SimpleChannelInboundHandler<ClientSidePacket> { public class PacketInboundHandler extends SimpleChannelInboundHandler<ClientSidePacket> {
private static final String CLIENT_FORCE_DISCONNECTED_IOEXCEPTION_MESSAGE_RU = "Программа на вашем хост-компьютере разорвала установленное подключение";
private final ObjectPool<NettyConnectionContext> poolNettyConnectionContext; private final ObjectPool<NettyConnectionContext> poolNettyConnectionContext;
private final PacketPool poolPackets; private final PacketPool poolPackets;
private final EventBus eventBus; private final EventBus eventBus;
@@ -28,21 +22,7 @@ public class PacketInboundHandler extends SimpleChannelInboundHandler<ClientSide
NettyConnectionContext context = poolNettyConnectionContext.borrowObject().setCtx(ctx); NettyConnectionContext context = poolNettyConnectionContext.borrowObject().setCtx(ctx);
eventBus.emit(state, context, packet); eventBus.emit(state, context, packet);
if (!context.isUsedContext()) { poolNettyConnectionContext.returnObject(context);
poolNettyConnectionContext.returnObject(context);
}
poolPackets.returnObject(packet); poolPackets.returnObject(packet);
} }
@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
if (cause instanceof IOException && cause.getLocalizedMessage().equalsIgnoreCase(CLIENT_FORCE_DISCONNECTED_IOEXCEPTION_MESSAGE_RU)) {
log.warn("Client '{}' force disconnected", ctx.channel().remoteAddress());
if (log.isTraceEnabled()) {
log.trace("", cause);
}
} else {
log.error("{}", cause.getMessage(), cause);
}
}
} }

View File

@@ -4,7 +4,7 @@ import lombok.Getter;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import mc.protocol.packets.ClientSidePacket; import mc.protocol.packets.ClientSidePacket;
import mc.protocol.packets.Packet; import mc.protocol.packets.Packet;
import mc.protocol.packets.KeepAlivePacket; import mc.protocol.packets.PingPacket;
import mc.protocol.packets.ServerSidePacket; import mc.protocol.packets.ServerSidePacket;
import mc.protocol.packets.client.*; import mc.protocol.packets.client.*;
import mc.protocol.packets.server.*; import mc.protocol.packets.server.*;
@@ -24,12 +24,12 @@ public enum State {
// client side // client side
Map.of( Map.of(
0x00, StatusServerRequestPacket.class, 0x00, StatusServerRequestPacket.class,
0x01, KeepAlivePacket.class 0x01, PingPacket.class
), ),
// server side // server side
Map.of( Map.of(
StatusServerResponse.class, 0x00, StatusServerResponse.class, 0x00,
KeepAlivePacket.class, 0x01 PingPacket.class, 0x01
) )
), ),
LOGIN(2, LOGIN(2,
@@ -47,20 +47,23 @@ public enum State {
0x00, TeleportConfirmPacket.class, 0x00, TeleportConfirmPacket.class,
0x04, ClientSettingsPacket.class, 0x04, ClientSettingsPacket.class,
0x09, PluginMessagePacket.class, 0x09, PluginMessagePacket.class,
0x0B, KeepAlivePacket.class, 0x0B, PingPacket.class,
0x0D, PlayerPositionPacket.class, 0x0D, PlayerPositionPacket.class,
0x0E, CPlayerPositionAndLookPacket.class, 0x0E, CPlayerPositionAndLookPacket.class,
0x0F, PlayerLookPacket.class, 0x0F, PlayerLookPacket.class
0x15, EntityActionPacket.class
), ),
// client bound // client bound
Map.of( Map.of(
KeepAlivePacket.class, 0x1F, PingPacket.class, 0x1F,
ChunkDataPacket.class, 0x20,
JoinGamePacket.class, 0x23, JoinGamePacket.class, 0x23,
ScoreboardDisplayPacket.class, 0x3B,
ScoreboardObjectivePacket.class, 0x42,
TeamsPacket.class, 0x44,
ScoreboardUpdateScorePacket.class, 0x45,
SpawnPositionPacket.class, 0x46,
ChunkDataPacket.class, 0x20,
PlayerAbilitiesPacket.class,0x2C, PlayerAbilitiesPacket.class,0x2C,
SPlayerPositionAndLookPacket.class, 0x2F, SPlayerPositionAndLookPacket.class, 0x2F
SpawnPositionPacket.class, 0x46
) )
); );

View File

@@ -3,37 +3,11 @@ package mc.protocol.api;
import mc.protocol.State; import mc.protocol.State;
import mc.protocol.packets.ServerSidePacket; import mc.protocol.packets.ServerSidePacket;
import java.util.Optional;
public interface ConnectionContext { public interface ConnectionContext {
/**
* @deprecated костыль
*/
@Deprecated
void setUsedContext(boolean value);
/**
* @deprecated костыль
*/
@Deprecated
boolean isUsedContext();
State getState(); State getState();
void setState(State state); void setState(State state);
/**
* @deprecated костыль
*/
@Deprecated
<T> void setCustomProperty(String key, T value);
/**
* @deprecated костыль
*/
@Deprecated
<T> Optional<T> getCustomProperty(String key, Class<T> classResult);
void send(ServerSidePacket packet); void send(ServerSidePacket packet);
void sendNow(ServerSidePacket packet); void sendNow(ServerSidePacket packet);
void flushSending(); void flushSending();

View File

@@ -99,6 +99,11 @@ public class NetByteBuf extends ByteBuf {
} }
public void writeString(String string) { public void writeString(String string) {
if (string == null) {
writeVarInt(0);
return;
}
byte[] buf = string.getBytes(StandardCharsets.UTF_8); byte[] buf = string.getBytes(StandardCharsets.UTF_8);
if (buf.length > Short.MAX_VALUE) { if (buf.length > Short.MAX_VALUE) {

View File

@@ -17,7 +17,6 @@ import mc.protocol.pool.PacketPool;
import org.apache.commons.pool2.ObjectPool; import org.apache.commons.pool2.ObjectPool;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.function.Consumer; import java.util.function.Consumer;
@@ -37,8 +36,6 @@ public class ProtocolDecoder extends ByteToMessageDecoder {
@Override @Override
public void channelActive(@Nonnull ChannelHandlerContext ctx) throws Exception { public void channelActive(@Nonnull ChannelHandlerContext ctx) throws Exception {
ctx.channel().attr(NetworkAttributes.CUSTOM_PROPERTIES).set(new HashMap<>());
NettyConnectionContext context = poolNettyConnectionContext.borrowObject().setCtx(ctx); NettyConnectionContext context = poolNettyConnectionContext.borrowObject().setCtx(ctx);
consumerNewConnection.accept(context); consumerNewConnection.accept(context);
@@ -51,9 +48,6 @@ public class ProtocolDecoder extends ByteToMessageDecoder {
NettyConnectionContext context = poolNettyConnectionContext.borrowObject().setCtx(ctx); NettyConnectionContext context = poolNettyConnectionContext.borrowObject().setCtx(ctx);
consumerDisconnect.accept(context); consumerDisconnect.accept(context);
ctx.channel().attr(NetworkAttributes.CUSTOM_PROPERTIES).get().clear();
ctx.channel().attr(NetworkAttributes.CUSTOM_PROPERTIES).set(null);
poolNettyConnectionContext.returnObject(context); poolNettyConnectionContext.returnObject(context);
super.channelInactive(ctx); super.channelInactive(ctx);
} }

View File

@@ -9,16 +9,4 @@ public class Location {
private double x; private double x;
private double y; private double y;
private double z; private double z;
public int getIntX() {
return (int) x;
}
public int getIntZ() {
return (int) z;
}
public Location toChunkXZ() {
return new Location(this.getIntX() >> 4, 0d, this.getIntZ() >> 4);
}
} }

View File

@@ -25,7 +25,7 @@ import mc.protocol.io.NetByteBuf;
* @see <a href="https://wiki.vg/index.php?title=Protocol&oldid=7368#Keep_Alive">Keep Alive</a> * @see <a href="https://wiki.vg/index.php?title=Protocol&oldid=7368#Keep_Alive">Keep Alive</a>
*/ */
@Data @Data
public class KeepAlivePacket implements ClientSidePacket, ServerSidePacket { public class PingPacket implements ClientSidePacket, ServerSidePacket {
private Long payload; private Long payload;

View File

@@ -1,52 +0,0 @@
package mc.protocol.packets.client;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.ToString;
import mc.protocol.io.NetByteBuf;
import mc.protocol.packets.ClientSidePacket;
import mc.protocol.utils.EntityActionAction;
/**
* Entity Action packet.
*
* <p>Структура пакета</p>
* <pre>
* | FIELD | TYPE | NOTES |
* |------------|--------|-------------------------------------------|
* | Entity ID | VarInt | ID игрока |
* | Action ID | VarInt | ID действия |
* | Jump Boost | VarInt | Используется только при "Action ID" = 5. |
* | | | В этом случае значение будет от 0 до 100. |
* | | | В остальных случаях значение 0. |
* </pre>
*
* @see <a href="https://wiki.vg/index.php?title=Protocol&oldid=14204#Entity_Action" target="_top">Entity Action</a>
*/
@NoArgsConstructor
@Getter
@EqualsAndHashCode
@ToString
public class EntityActionPacket implements ClientSidePacket {
private Integer entityId;
private EntityActionAction action;
private Integer jumpBoost;
@Override
public void readSelf(NetByteBuf netByteBuf) {
this.entityId = netByteBuf.readVarInt();
int actionId = netByteBuf.readVarInt();
this.jumpBoost = netByteBuf.readVarInt();
this.action = EntityActionAction.valueOfCode(actionId);
}
@Override
public void passivate() {
this.entityId = null;
this.action = null;
this.jumpBoost = null;
}
}

View File

@@ -28,17 +28,13 @@ import mc.protocol.packets.ServerSidePacket;
@Data @Data
public class ChunkDataPacket implements ServerSidePacket { public class ChunkDataPacket implements ServerSidePacket {
private static NetByteBuf voidData;
private int x; private int x;
private int z; private int z;
@SuppressWarnings("java:S125")
@Override @Override
public void writeSelf(NetByteBuf netByteBuf) { public void writeSelf(NetByteBuf netByteBuf) {
netByteBuf.writeInt(x); netByteBuf.writeInt(x);
netByteBuf.writeInt(z); netByteBuf.writeInt(z);
/* Временное отключение кода
netByteBuf.writeBoolean(true); // Is Full chunk netByteBuf.writeBoolean(true); // Is Full chunk
netByteBuf.writeVarInt(0b11111111); // Available Sections netByteBuf.writeVarInt(0b11111111); // Available Sections
@@ -68,41 +64,6 @@ public class ChunkDataPacket implements ServerSidePacket {
netByteBuf.writeVarInt(data.readableBytes()); // Size of Data netByteBuf.writeVarInt(data.readableBytes()); // Size of Data
netByteBuf.writeBytes(data); // Data netByteBuf.writeBytes(data); // Data
netByteBuf.writeVarInt(0); // Number of block entities netByteBuf.writeVarInt(0); // Number of block entities
// write NBT's /* write NBT's */
*/
netByteBuf.writeBytes(voidData);
voidData.resetReaderIndex();
voidData.resetWriterIndex();
}
static {
voidData = new NetByteBuf(Unpooled.buffer());
voidData.writeBoolean(true); // Is Full chunk
voidData.writeVarInt(0b11111111); // Available Sections
NetByteBuf data = new NetByteBuf(Unpooled.buffer());
for (int i = 0; i < 16; i++) {
NetByteBuf dataBuff = new NetByteBuf(Unpooled.wrappedBuffer(new byte[4096]));
NetByteBuf blockLight = new NetByteBuf(Unpooled.wrappedBuffer(new byte[2048]));
NetByteBuf skyLight = new NetByteBuf(Unpooled.wrappedBuffer(new byte[2048]));
NetByteBuf biomes = new NetByteBuf(Unpooled.wrappedBuffer(new byte[256]));
data.writeUnsignedByte(13);
data.writeUnsignedByte(0);
data.writeVarInt(dataBuff.readableBytes());
data.writeBytes(dataBuff);
data.writeBytes(blockLight);
data.writeBytes(skyLight);
data.writeBytes(biomes);
}
voidData.writeVarInt(data.readableBytes());
voidData.writeBytes(data);
voidData.writeVarInt(0);
voidData.markReaderIndex();
voidData.markWriterIndex();
} }
} }

View File

@@ -0,0 +1,39 @@
package mc.protocol.packets.server;
import lombok.Data;
import mc.protocol.io.NetByteBuf;
import mc.protocol.packets.ServerSidePacket;
/**
* Отображение Scoreboard.
*
* <p>Структура пакета</p>
* <pre>
* | FIELD | TYPE | NOTES |
* |------------|-------------|--------------------------------|
* | Position | Byte | Положение: |
* | | | 0 - list |
* | | | 1 - sidebar |
* | | | 2 - below name |
* | | | 3-18 - team specific sidebar |
* | Score Name | String (16) | Уникальное название Scoreboard |
* </pre>
*
* @see <a href="https://wiki.vg/index.php?title=Protocol&oldid=14204#Display_Scoreboard" target="_top">Display Scoreboard</a>
*/
@Data
public class ScoreboardDisplayPacket implements ServerSidePacket {
private int position;
private String scoreName;
public void setPosition(int position) {
this.position = (position < 0) ? 0 : (Math.min(position, 18));
}
@Override
public void writeSelf(NetByteBuf netByteBuf) {
netByteBuf.writeByte(this.position);
netByteBuf.writeString(this.scoreName);
}
}

View File

@@ -0,0 +1,44 @@
package mc.protocol.packets.server;
import lombok.Data;
import mc.protocol.io.NetByteBuf;
import mc.protocol.packets.ServerSidePacket;
import mc.protocol.utils.ScoreboardObjectiveMode;
import mc.protocol.utils.ScoreboardObjectiveType;
/**
* Scoreboard objective packet.
*
* <p>Структура пакета</p>
* <pre>
* | FIELD | TYPE | NOTES |
* |-----------------|-------------|---------------------------------------------------|
* | Objective Name | String (16) | Уникальное наименование цели (objective) |
* | Mode | Byte | 0 - создание Scoreboard |
* | | | 1 - удаление Scoreboard |
* | | | 2 - обновление Scoreboard |
* | Objective Value | String (32) | Если "Mode" равен 0 или 2. Отображаемый текст |
* | Type | String (16) | Если "Mode" равен 0 или 2. "integer" или "hearts" |
* </pre>
*
* @see <a href="https://wiki.vg/index.php?title=Protocol&oldid=14204#Scoreboard_Objective" target="_top">Scoreboard Objective</a>
*/
@Data
public class ScoreboardObjectivePacket implements ServerSidePacket {
private String objectiveName;
private ScoreboardObjectiveMode mode;
private String objectiveValue;
private ScoreboardObjectiveType type;
@Override
public void writeSelf(NetByteBuf netByteBuf) {
netByteBuf.writeString(this.objectiveName);
netByteBuf.writeByte(this.mode.getCode());
if (ScoreboardObjectiveMode.CREATE.equals(this.mode) || ScoreboardObjectiveMode.UPDATE.equals(this.mode)) {
netByteBuf.writeString(this.objectiveValue);
netByteBuf.writeString(this.type.name().toLowerCase());
}
}
}

View File

@@ -0,0 +1,43 @@
package mc.protocol.packets.server;
import lombok.Data;
import mc.protocol.io.NetByteBuf;
import mc.protocol.packets.ServerSidePacket;
import mc.protocol.utils.ScoreboardUpdateScoreAction;
/**
* Update score packet.
*
* <p>Структура пакета</p>
* <pre>
* | FIELD | TYPE | NOTES |
* |-----------------|-------------|--------------------------------------------------- |
* | Entity Name | String (40) | Сущность, которой принадлежит счет (score). |
* | | | Для Игроков - это ник |
* | | | Для других сущностей - это UUID |
* | Action | Byte | 0 - создать или обновить счет (score); 1 - удалить |
* | Objective Name | String (16) | Имя сущности, которой принадлежит счет (score) |
* | Value | VarInt | Если "Action" = 0. Значение счета (score) |
* </pre>
*
* @see <a href="https://wiki.vg/index.php?title=Protocol&oldid=14204#Update_Score" target="_top">Update Score</a>
*/
@Data
public class ScoreboardUpdateScorePacket implements ServerSidePacket {
private String entityName;
private ScoreboardUpdateScoreAction action;
private String objective;
private int value;
@Override
public void writeSelf(NetByteBuf netByteBuf) {
netByteBuf.writeString(this.entityName);
netByteBuf.writeByte(this.action.getCode());
netByteBuf.writeString(this.objective);
if (ScoreboardUpdateScoreAction.CREATE_OR_UPDATE.equals(this.action)) {
netByteBuf.writeVarInt(this.value);
}
}
}

View File

@@ -0,0 +1,119 @@
package mc.protocol.packets.server;
import lombok.Data;
import mc.protocol.io.NetByteBuf;
import mc.protocol.packets.ServerSidePacket;
import mc.protocol.utils.TeamsCollisionRule;
import mc.protocol.utils.TeamsMode;
import mc.protocol.utils.TeamsNameTagVisibility;
import java.util.ArrayList;
import java.util.List;
/**
* Teams packet.
*
* <p>Структура пакета</p>
* <pre>
* | FIELD | TYPE | NOTES |
* |------------ |-------------|-------------------------------------------------------|
* | Team Name | String (16) | Уникальное название команды (совместно со scoreboard) |
* | Mode | Byte | Режим. Определяет остальые поля пакета |
* | Data Fields | - | Определяется "Mode" |
* </pre>
*
* <p>Варианты "Mode"</p>
* <pre>
* | MODE | DATA FIELD | TYPE | NOTES |
* | VALUE | DESCRIPTION | | | |
* |-------|--------------------------|---------------------|----------------------|---------------------------------------------------|
* | 0 | create team | Team Display Name | String (32) | |
* | | | Team Prefix | String (16) | Отображается перед именем игроков текущей команды |
* | | | Team Suffix | String (16) | Отображается после имени игроков текущей команды |
* | | | Friendly Flags | Byte | Битовая маска: |
* | | | | | 0x01 - разрешён friendly fire |
* | | | | | 0x02 - могут видеть невидимок своей команды |
* | | | Name Tag Visibility | String (32) | фиксированные значения: |
* | | | | | - always |
* | | | | | - hideForOtherTeams |
* | | | | | - hideForOwnTeam |
* | | | | | - never |
* | | | Collision Rule | String (32) | фиксированные значения: |
* | | | | | - always |
* | | | | | - pushOtherTeams |
* | | | | | - pushOwnTeam |
* | | | | | - never |
* | | | Color | Byte | For colors, the same Chat colors (0-15). |
* | | | | | -1 indicates RESET/no color. |
* | | | Entity Count | VarInt | Количество элементов в поле "Entities" |
* | | | Entities | Array of String (40) | Уникальные идентификаторы участников команды. |
* | | | | | Для Игроков - это Имена |
* | | | | | Для любых других сущностей - это UUID |
* | 1 | remove team | - | - | удаление текущей команды |
* | 2 | update team info | Team Display Name | String (32) | |
* | | | Team Prefix | String (16) | (см. выше) |
* | | | Team Suffix | String (16) | (см. выше) |
* | | | Friendly Flags | Byte | (см. выше) |
* | | | Name Tag Visibility | String (32) | (см. выше) |
* | | | Collision Rule | String (32) | (см. выше) |
* | | | Color | Byte | (см. выше) |
* | 3 | add players to team | Entity Count | VarInt | (см. выше) |
* | | | Entities | Array of String (40) | (см. выше) |
* | 4 | remove players from team | Entity Count | VarInt | (см. выше) |
* | | | Entities | Array of String (40) | (см. выше) |
* </pre>
*
* @see <a href="https://wiki.vg/index.php?title=Protocol&oldid=14204#Teams" target="_top">Teams</a>
*/
@Data
public class TeamsPacket implements ServerSidePacket {
private final List<String> members = new ArrayList<>();
private String name;
private TeamsMode mode;
private String displayName;
private String prefix;
private String suffix;
private TeamsNameTagVisibility nameTagVisibility;
private TeamsCollisionRule collisionRule;
private int color;
@Override
public void writeSelf(NetByteBuf netByteBuf) {
netByteBuf.writeString(this.name);
netByteBuf.writeByte(this.mode.getCode());
switch (this.mode) {
case CREATE:
netByteBuf.writeString(this.displayName);
netByteBuf.writeString(this.prefix);
netByteBuf.writeString(this.suffix);
netByteBuf.writeByte(0); // Friendly Flags
netByteBuf.writeString(this.nameTagVisibility.getCode());
netByteBuf.writeString(this.collisionRule.getCode());
netByteBuf.writeByte(this.color);
netByteBuf.writeVarInt(this.members.size());
this.members.forEach(netByteBuf::writeString);
break;
case UPDATE:
netByteBuf.writeString(this.displayName);
netByteBuf.writeString(this.prefix);
netByteBuf.writeString(this.suffix);
netByteBuf.writeByte(0); // Friendly Flags
netByteBuf.writeString(this.nameTagVisibility.getCode());
netByteBuf.writeString(this.collisionRule.getCode());
netByteBuf.writeByte(this.color);
break;
case ADD_MEMBER:
case REMOVE_MEMBER:
netByteBuf.writeVarInt(this.members.size());
this.members.forEach(netByteBuf::writeString);
break;
case REMOVE:
default:
break;
}
}
}

View File

@@ -1,33 +0,0 @@
package mc.protocol.utils;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import javax.annotation.Nullable;
@RequiredArgsConstructor
public enum EntityActionAction {
START_SNEAKING(0),
STOP_SNEAKING(1),
LEAVE_BED(2),
START_SPRINTING(3),
STOP_SPRINTING(4),
START_JUMP_WITH_HORSE(5),
STOP_JUMP_WITH_HORSE(6),
OPEN_HORSE_INVENTORY(7),
START_FLYING_WITH_ELYTRA(8);
@Nullable
public static EntityActionAction valueOfCode(int code) {
for (EntityActionAction action : EntityActionAction.values()) {
if (action.code == code) {
return action;
}
}
return null;
}
@Getter
private final int code;
}

View File

@@ -0,0 +1,14 @@
package mc.protocol.utils;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public enum ScoreboardObjectiveMode {
CREATE(0),
REMOVE(1),
UPDATE(2);
@Getter
private final int code;
}

View File

@@ -0,0 +1,6 @@
package mc.protocol.utils;
public enum ScoreboardObjectiveType {
INTEGER,
HEARTS
}

View File

@@ -0,0 +1,10 @@
package mc.protocol.utils;
import lombok.experimental.UtilityClass;
@UtilityClass
public class ScoreboardPosition {
public final int LIST = 0;
public final int SIDEBAR = 1;
public final int BELOW_NAME = 2;
}

View File

@@ -0,0 +1,13 @@
package mc.protocol.utils;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public enum ScoreboardUpdateScoreAction {
CREATE_OR_UPDATE(0),
REMOVE(1);
@Getter
private final int code;
}

View File

@@ -0,0 +1,15 @@
package mc.protocol.utils;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public enum TeamsCollisionRule {
ALWAYS("always"),
PUSH_OTHER_TEAMS("pushOtherTeams"),
PUSH_OWN_TEAM("pushOwnTeam"),
NEVER("never");
@Getter
private final String code;
}

View File

@@ -0,0 +1,16 @@
package mc.protocol.utils;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public enum TeamsMode {
CREATE(0),
REMOVE(1),
UPDATE(2),
ADD_MEMBER(3),
REMOVE_MEMBER(4);
@Getter
private final int code;
}

View File

@@ -0,0 +1,15 @@
package mc.protocol.utils;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
@RequiredArgsConstructor
public enum TeamsNameTagVisibility {
ALWAYS("always"),
HIDE_FOR_OTHER_TEAMS("hideForOtherTeams"),
HIDE_FOR_OWN_TEAM("hideForOwnTeam"),
NEVER("never");
@Getter
private final String code;
}

View File

@@ -1,7 +0,0 @@
package mc.protocol.world;
public interface Chunk {
int getX();
int getZ();
}

View File

@@ -1,13 +0,0 @@
package mc.protocol.world;
import mc.protocol.model.Location;
import mc.protocol.utils.LevelType;
public interface World {
LevelType getLevelType();
Location getSpawn();
Chunk getChunk(int x, int z);
}

View File

@@ -12,7 +12,7 @@ import mc.protocol.api.Server;
import mc.protocol.di.DaggerProtocolComponent; import mc.protocol.di.DaggerProtocolComponent;
import mc.protocol.di.ProtocolComponent; import mc.protocol.di.ProtocolComponent;
import mc.protocol.di.ProtocolModule; import mc.protocol.di.ProtocolModule;
import mc.protocol.packets.KeepAlivePacket; import mc.protocol.packets.PingPacket;
import mc.protocol.packets.client.HandshakePacket; import mc.protocol.packets.client.HandshakePacket;
import mc.protocol.packets.client.LoginStartPacket; import mc.protocol.packets.client.LoginStartPacket;
import mc.protocol.packets.client.StatusServerRequestPacket; import mc.protocol.packets.client.StatusServerRequestPacket;
@@ -20,7 +20,6 @@ import mc.server.config.Config;
import mc.server.di.ConfigModule; import mc.server.di.ConfigModule;
import mc.server.di.DaggerServerComponent; import mc.server.di.DaggerServerComponent;
import mc.server.di.ServerComponent; import mc.server.di.ServerComponent;
import mc.server.service.PlayerManager;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -49,7 +48,6 @@ public class Main {
.build(); .build();
Config config = serverComponent.getConfig(); Config config = serverComponent.getConfig();
PlayerManager playerManager = serverComponent.getPlayerManager();
ProtocolComponent protocolComponent = DaggerProtocolComponent.builder() ProtocolComponent protocolComponent = DaggerProtocolComponent.builder()
.protocolModule(new ProtocolModule(true)) .protocolModule(new ProtocolModule(true))
@@ -59,16 +57,13 @@ public class Main {
PacketHandler packetHandler = serverComponent.getPacketHandler(); PacketHandler packetHandler = serverComponent.getPacketHandler();
server.onNewConnect(connectionContext -> connectionContext.setState(State.HANDSHAKING)); server.onNewConnect(connectionContext -> connectionContext.setState(State.HANDSHAKING));
server.onDisonnect(connectionContext -> { server.onDisonnect(connectionContext -> connectionContext.setState(null));
connectionContext.setState(null);
connectionContext.getCustomProperty("player", Player.class).ifPresent(playerManager::remove);
});
server.listenPacket(State.HANDSHAKING, HandshakePacket.class, packetHandler::onHandshake); server.listenPacket(State.HANDSHAKING, HandshakePacket.class, packetHandler::onHandshake);
server.listenPacket(State.STATUS, KeepAlivePacket.class, packetHandler::onKeepAlive); server.listenPacket(State.STATUS, PingPacket.class, packetHandler::onKeepAlive);
server.listenPacket(State.STATUS, StatusServerRequestPacket.class, packetHandler::onServerStatus); server.listenPacket(State.STATUS, StatusServerRequestPacket.class, packetHandler::onServerStatus);
server.listenPacket(State.LOGIN, LoginStartPacket.class, packetHandler::onLoginStart); server.listenPacket(State.LOGIN, LoginStartPacket.class, packetHandler::onLoginStart);
server.listenPacket(State.PLAY, KeepAlivePacket.class, packetHandler::onKeepAlivePlay); server.listenPacket(State.PLAY, PingPacket.class, packetHandler::onKeepAlivePlay);
server.bind(config.server().host(), config.server().port()); server.bind(config.server().host(), config.server().port());
} }

View File

@@ -7,18 +7,14 @@ import mc.protocol.api.ConnectionContext;
import mc.protocol.model.Location; import mc.protocol.model.Location;
import mc.protocol.model.Look; import mc.protocol.model.Look;
import mc.protocol.model.ServerInfo; import mc.protocol.model.ServerInfo;
import mc.protocol.packets.KeepAlivePacket; import mc.protocol.packets.PingPacket;
import mc.protocol.packets.client.HandshakePacket; import mc.protocol.packets.client.HandshakePacket;
import mc.protocol.packets.client.LoginStartPacket; import mc.protocol.packets.client.LoginStartPacket;
import mc.protocol.packets.client.StatusServerRequestPacket; import mc.protocol.packets.client.StatusServerRequestPacket;
import mc.protocol.packets.server.*; import mc.protocol.packets.server.*;
import mc.protocol.serializer.TextSerializer; import mc.protocol.serializer.TextSerializer;
import mc.protocol.utils.Difficulty; import mc.protocol.utils.*;
import mc.protocol.utils.GameMode;
import mc.protocol.world.Chunk;
import mc.protocol.world.World;
import mc.server.config.Config; import mc.server.config.Config;
import mc.server.service.PlayerManager;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import java.io.IOException; import java.io.IOException;
@@ -27,7 +23,7 @@ import java.nio.file.Path;
import java.util.Base64; import java.util.Base64;
import java.util.Collections; import java.util.Collections;
import java.util.Random; import java.util.Random;
import java.util.concurrent.TimeUnit; import java.util.UUID;
@Slf4j @Slf4j
@RequiredArgsConstructor @RequiredArgsConstructor
@@ -35,27 +31,18 @@ public class PacketHandler {
private final Random random = new Random(System.currentTimeMillis()); private final Random random = new Random(System.currentTimeMillis());
private final Config config; private final Config config;
private final World world;
private final PlayerManager playerManager;
public void onHandshake(ConnectionContext context, HandshakePacket packet) { public void onHandshake(ConnectionContext context, HandshakePacket packet) {
context.setState(packet.getNextState()); context.setState(packet.getNextState());
} }
public void onKeepAlive(ConnectionContext context, KeepAlivePacket packet) { public void onKeepAlive(ConnectionContext context, PingPacket packet) {
context.sendNow(packet); context.sendNow(packet);
context.disconnect(); context.disconnect();
} }
public void onKeepAlivePlay(ConnectionContext context, KeepAlivePacket packet) { public void onKeepAlivePlay(ConnectionContext context, PingPacket packet) {
try { context.sendNow(packet);
TimeUnit.MILLISECONDS.sleep(50);
context.sendNow(packet);
} catch (InterruptedException e) {
if (log.isTraceEnabled()) {
log.trace("{}", e.getMessage(), e);
}
}
} }
@SuppressWarnings("unused") @SuppressWarnings("unused")
@@ -64,11 +51,7 @@ public class PacketHandler {
serverInfo.version().name(ProtocolConstant.PROTOCOL_NAME); serverInfo.version().name(ProtocolConstant.PROTOCOL_NAME);
serverInfo.version().protocol(ProtocolConstant.PROTOCOL_NUMBER); serverInfo.version().protocol(ProtocolConstant.PROTOCOL_NUMBER);
serverInfo.players().max(config.players().maxOnlile()); serverInfo.players().max(config.players().maxOnlile());
if (config.players().fakeOnline().enable()) { serverInfo.players().online(config.players().onlile());
serverInfo.players().online(config.players().fakeOnline().value());
} else {
serverInfo.players().online(playerManager.online());
}
serverInfo.players().sample(Collections.emptyList()); serverInfo.players().sample(Collections.emptyList());
serverInfo.description(TextSerializer.fromPlain(config.motd())); serverInfo.description(TextSerializer.fromPlain(config.motd()));
@@ -82,29 +65,31 @@ public class PacketHandler {
context.sendNow(response); context.sendNow(response);
} }
@SuppressWarnings("java:S2589")
public void onLoginStart(ConnectionContext context, LoginStartPacket loginStartPacket) { public void onLoginStart(ConnectionContext context, LoginStartPacket loginStartPacket) {
Player player = playerManager.addAndCreate(context, loginStartPacket.getName(), GameMode.SURVIVAL, world.getSpawn()); UUID playerUuid = UUID.randomUUID();
context.setCustomProperty("player", player); int playerEid = random.nextInt();
String playerName = loginStartPacket.getName();
var loginSuccessPacket = new LoginSuccessPacket(); var loginSuccessPacket = new LoginSuccessPacket();
loginSuccessPacket.setUuid(player.getUuid()); loginSuccessPacket.setUuid(playerUuid);
loginSuccessPacket.setName(player.getName()); loginSuccessPacket.setName(playerName);
context.sendNow(loginSuccessPacket); context.sendNow(loginSuccessPacket);
context.setState(State.PLAY); context.setState(State.PLAY);
var joinGamePacket = new JoinGamePacket(); var joinGamePacket = new JoinGamePacket();
joinGamePacket.setEntityId(random.nextInt()); joinGamePacket.setEntityId(playerEid);
joinGamePacket.setGameMode(player.getGameMode()); joinGamePacket.setGameMode(GameMode.SPECTATOR);
joinGamePacket.setDimension(0/*Overworld*/); joinGamePacket.setDimension(0/*Overworld*/);
joinGamePacket.setDifficulty(Difficulty.PEACEFUL); joinGamePacket.setDifficulty(Difficulty.PEACEFUL);
joinGamePacket.setLevelType(world.getLevelType()); joinGamePacket.setLevelType(LevelType.FLAT);
context.send(joinGamePacket); context.send(joinGamePacket);
Location spawnLocation = new Location(0d, 63d, 0d);
var spawnPositionPacket = new SpawnPositionPacket(); var spawnPositionPacket = new SpawnPositionPacket();
spawnPositionPacket.setSpawn(player.getLocation()); spawnPositionPacket.setSpawn(spawnLocation);
context.send(spawnPositionPacket); context.send(spawnPositionPacket);
@@ -120,47 +105,51 @@ public class PacketHandler {
context.flushSending(); context.flushSending();
Location chunkLocation = player.getLocation().toChunkXZ();
Chunk chunk = world.getChunk(chunkLocation.getIntX(), chunkLocation.getIntZ());
var chunkDataPacket = new ChunkDataPacket(); var chunkDataPacket = new ChunkDataPacket();
chunkDataPacket.setX(chunk.getX()); chunkDataPacket.setX(0);
chunkDataPacket.setZ(chunk.getZ()); chunkDataPacket.setZ(0);
context.send(chunkDataPacket); context.sendNow(chunkDataPacket);
for (int i = 1; i <= config.world().viewDistance(); i++) {
int minX = chunkLocation.getIntX() - i;
int minZ = chunkLocation.getIntZ() - i;
int maxX = chunkLocation.getIntX() + i;
int maxZ = chunkLocation.getIntZ() + i;
for (int z = minZ; z <= maxZ; z++) {
for (int x = minX; x <= maxX; x++) {
if ((z == minZ || z == maxZ) || (x == minX || x == maxX)) {
chunkDataPacket = new ChunkDataPacket();
chunkDataPacket.setX(x);
chunkDataPacket.setZ(z);
context.send(chunkDataPacket);
}
}
}
}
context.flushSending();
var playerPositionAndLookPacket = new SPlayerPositionAndLookPacket(); var playerPositionAndLookPacket = new SPlayerPositionAndLookPacket();
playerPositionAndLookPacket.setPosition(player.getLocation()); playerPositionAndLookPacket.setPosition(spawnLocation);
playerPositionAndLookPacket.setLook(new Look(0f, 0f)); playerPositionAndLookPacket.setLook(new Look(0f, 0f));
playerPositionAndLookPacket.setTeleportId(random.nextInt()); playerPositionAndLookPacket.setTeleportId(random.nextInt());
context.send(playerPositionAndLookPacket); context.send(playerPositionAndLookPacket);
KeepAlivePacket keepAlivePacket = new KeepAlivePacket(); PingPacket pingPacket = new PingPacket();
keepAlivePacket.setPayload(System.currentTimeMillis()); pingPacket.setPayload(System.currentTimeMillis());
context.send(keepAlivePacket); context.send(pingPacket);
context.flushSending();
// --- Эксперименты --- //
String scoreboardName = "Score::List";
var scoreboardObjectivePacket = new ScoreboardObjectivePacket();
scoreboardObjectivePacket.setObjectiveName(scoreboardName);
scoreboardObjectivePacket.setMode(ScoreboardObjectiveMode.CREATE);
scoreboardObjectivePacket.setObjectiveValue(scoreboardName);
scoreboardObjectivePacket.setType(ScoreboardObjectiveType.INTEGER);
context.send(scoreboardObjectivePacket);
var scoreboardDisplayPacket = new ScoreboardDisplayPacket();
scoreboardDisplayPacket.setPosition(ScoreboardPosition.SIDEBAR);
scoreboardDisplayPacket.setScoreName(scoreboardName);
context.send(scoreboardDisplayPacket);
var scoreboardUpdateScorePacket = new ScoreboardUpdateScorePacket();
scoreboardUpdateScorePacket.setEntityName(playerName);
scoreboardUpdateScorePacket.setAction(ScoreboardUpdateScoreAction.CREATE_OR_UPDATE);
scoreboardUpdateScorePacket.setObjective(scoreboardName);
scoreboardUpdateScorePacket.setValue(100500);
context.send(scoreboardUpdateScorePacket);
context.flushSending(); context.flushSending();
} }

View File

@@ -1,20 +0,0 @@
package mc.server;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import mc.protocol.api.ConnectionContext;
import mc.protocol.model.Location;
import mc.protocol.utils.GameMode;
import java.util.UUID;
@RequiredArgsConstructor
@Getter
public class Player {
private final ConnectionContext connectionContext;
private final UUID uuid;
private final String name;
private final GameMode gameMode;
private final Location location;
}

View File

@@ -15,7 +15,6 @@ public class Config {
private final Server server = new Server(); private final Server server = new Server();
private final Players players = new Players(); private final Players players = new Players();
private final World world = new World();
private String motd; private String motd;
private String disconnectReason; private String disconnectReason;
@@ -33,23 +32,7 @@ public class Config {
@Setter @Setter
@ToString @ToString
public static class Players { public static class Players {
private final FakeOnline fakeOnline = new FakeOnline();
private int maxOnlile; private int maxOnlile;
} private int onlile;
@Getter
@Setter
@ToString
public static class FakeOnline {
private boolean enable;
private int value;
}
@Getter
@Setter
@ToString
public static class World {
private int viewDistance;
} }
} }

View File

@@ -27,15 +27,10 @@ public class ConfigModule {
config.server().host(fromYamlPath("server/host", map, "127.0.0.1")); config.server().host(fromYamlPath("server/host", map, "127.0.0.1"));
config.server().port(fromYamlPath("server/port", map, 25565)); config.server().port(fromYamlPath("server/port", map, 25565));
config.motd(fromYamlPath("motd", map, "")); config.motd(fromYamlPath("motd", map, ""));
config.disconnectReason(fromYamlPath("disconnect-reason", map, "")); config.disconnectReason(fromYamlPath("disconnect-reason", map, ""));
config.players().maxOnlile(fromYamlPath("players/max-online", map, 0)); config.players().maxOnlile(fromYamlPath("players/max-online", map, 0));
config.players().fakeOnline().enable(fromYamlPath("players/fake-online/enable", map, false)); config.players().onlile(fromYamlPath("players/online", map, 0));
config.players().fakeOnline().value(fromYamlPath("players/fake-online/value", map, 0));
config.world().viewDistance(fromYamlPath("world/view-distance", map, 0));
if (Boolean.TRUE.equals(fromYamlPath("icon/enable", map, false))) { if (Boolean.TRUE.equals(fromYamlPath("icon/enable", map, false))) {
config.iconPath(Paths.get(fromYamlPath("icon/path", map, "favicon.png"))); config.iconPath(Paths.get(fromYamlPath("icon/path", map, "favicon.png")));

View File

@@ -2,16 +2,14 @@ package mc.server.di;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.Provides;
import mc.protocol.world.World;
import mc.server.PacketHandler; import mc.server.PacketHandler;
import mc.server.config.Config; import mc.server.config.Config;
import mc.server.service.PlayerManager;
@Module @Module
public class PacketHandlerModule { public class PacketHandlerModule {
@Provides @Provides
public PacketHandler providePacketHandler(Config config, World world, PlayerManager playerManager) { public PacketHandler providePacketHandler(Config config) {
return new PacketHandler(config, world, playerManager); return new PacketHandler(config);
} }
} }

View File

@@ -1,16 +0,0 @@
package mc.server.di;
import dagger.Module;
import dagger.Provides;
import mc.protocol.di.ServerScope;
import mc.server.service.PlayerManager;
@Module
public class PlayersModule {
@Provides
@ServerScope
PlayerManager providePlayerManager() {
return new PlayerManager();
}
}

View File

@@ -1,18 +1,14 @@
package mc.server.di; package mc.server.di;
import dagger.Component; import dagger.Component;
import mc.protocol.di.ServerScope;
import mc.server.PacketHandler; import mc.server.PacketHandler;
import mc.server.config.Config; import mc.server.config.Config;
import mc.server.service.PlayerManager;
@Component(modules = { @Component(modules = {
ConfigModule.class, PacketHandlerModule.class, WorldModule.class, PlayersModule.class ConfigModule.class, PacketHandlerModule.class
}) })
@ServerScope
public interface ServerComponent { public interface ServerComponent {
Config getConfig(); Config getConfig();
PacketHandler getPacketHandler(); PacketHandler getPacketHandler();
PlayerManager getPlayerManager();
} }

View File

@@ -1,17 +0,0 @@
package mc.server.di;
import dagger.Module;
import dagger.Provides;
import mc.protocol.di.ServerScope;
import mc.protocol.world.World;
import mc.server.world.VoidWorld;
@Module
public class WorldModule {
@Provides
@ServerScope
public World provideWorld() {
return new VoidWorld();
}
}

View File

@@ -1,29 +0,0 @@
package mc.server.service;
import mc.protocol.api.ConnectionContext;
import mc.protocol.model.Location;
import mc.protocol.utils.GameMode;
import mc.server.Player;
import java.util.LinkedList;
import java.util.UUID;
public class PlayerManager {
private final LinkedList<Player> players = new LinkedList<>();
public Player addAndCreate(ConnectionContext context, String name, GameMode gameMode, Location location) {
context.setUsedContext(true);
Player player = new Player(context, UUID.randomUUID(), name, gameMode, location);
players.add(player);
return player;
}
public void remove(Player player) {
players.remove(player);
}
public int online() {
return players.size();
}
}

View File

@@ -1,13 +0,0 @@
package mc.server.world;
import lombok.Getter;
import lombok.RequiredArgsConstructor;
import mc.protocol.world.Chunk;
@RequiredArgsConstructor
@Getter
public class VoidChunk implements Chunk {
private final int x;
private final int z;
}

View File

@@ -1,26 +0,0 @@
package mc.server.world;
import mc.protocol.model.Location;
import mc.protocol.utils.LevelType;
import mc.protocol.world.Chunk;
import mc.protocol.world.World;
public class VoidWorld implements World {
private static final Location spawn = new Location(7d, 130d, 7d);
@Override
public LevelType getLevelType() {
return LevelType.FLAT;
}
@Override
public Location getSpawn() {
return VoidWorld.spawn;
}
@Override
public Chunk getChunk(int x, int z) {
return new VoidChunk(x, z);
}
}

View File

@@ -10,14 +10,9 @@ disconnect-reason: '&4Server is not available.'
players: players:
max-online: 0 max-online: 0
fake-online: online: 0
enable: false
value: 0
# Размер значка: 64x64 px # Размер значка: 64x64 px
icon: icon:
enable: false enable: false
path: favicon.png path: favicon.png
world:
view-distance: 1