Compare commits
15 Commits
dev/scoreb
...
1.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
325546a76d
|
|||
|
4215b5615e
|
|||
|
10af38e102
|
|||
|
b049352fe3
|
|||
|
c4767bd240
|
|||
|
2d4895fef0
|
|||
|
04316d9cbd
|
|||
|
20791ed881
|
|||
|
2b0ad9895b
|
|||
|
ab17160f9d
|
|||
|
8a6f37924e
|
|||
|
f10fb46d23
|
|||
|
c6669af651
|
|||
|
3984ab3fca
|
|||
|
bc2d5a7e75
|
@@ -1,12 +1,13 @@
|
||||
# MC-SERVER
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
Написанный с нуля сервер **Minecraft 1.12.2**.
|
||||
|
||||
На данный момент может только показывать информацию о себе. Подключение к серверу не возможно.
|
||||
На данный момент сервер может показывать о себе информацию в списке серверов (motd, онлайн, иконка) и позволять
|
||||
игрокам подключиться к себе. Загружается пустой мир.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
project.group=mc-project
|
||||
project.name=mc-server
|
||||
project.version=1.1-SNAPSHOT
|
||||
project.version=1.1
|
||||
@@ -2,12 +2,16 @@ package mc.protocol;
|
||||
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import lombok.experimental.Accessors;
|
||||
import mc.protocol.api.ConnectionContext;
|
||||
import mc.protocol.packets.ServerSidePacket;
|
||||
import mc.protocol.pool.Passivable;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
@EqualsAndHashCode
|
||||
public class NettyConnectionContext implements ConnectionContext, Passivable {
|
||||
|
||||
@@ -15,6 +19,14 @@ public class NettyConnectionContext implements ConnectionContext, Passivable {
|
||||
@Setter
|
||||
private ChannelHandlerContext ctx;
|
||||
|
||||
/**
|
||||
* @deprecated костыль
|
||||
*/
|
||||
@Deprecated
|
||||
@Getter
|
||||
@Setter
|
||||
private boolean usedContext;
|
||||
|
||||
@Override
|
||||
public State getState() {
|
||||
return ctx.channel().attr(NetworkAttributes.STATE).get();
|
||||
@@ -25,6 +37,27 @@ public class NettyConnectionContext implements ConnectionContext, Passivable {
|
||||
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
|
||||
public void send(ServerSidePacket packet) {
|
||||
ctx.write(packet);
|
||||
|
||||
@@ -3,8 +3,16 @@ package mc.protocol;
|
||||
import io.netty.util.AttributeKey;
|
||||
import lombok.experimental.UtilityClass;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
@UtilityClass
|
||||
public class NetworkAttributes {
|
||||
|
||||
public static final AttributeKey<State> STATE = AttributeKey.newInstance("STATE");
|
||||
|
||||
/**
|
||||
* @deprecated костыль
|
||||
*/
|
||||
@Deprecated
|
||||
public static final AttributeKey<Map<String, Object>> CUSTOM_PROPERTIES = AttributeKey.newInstance("CUSTOM_PROPERTIES");
|
||||
}
|
||||
|
||||
@@ -3,14 +3,20 @@ package mc.protocol;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import mc.protocol.packets.ClientSidePacket;
|
||||
import mc.protocol.event.EventBus;
|
||||
import mc.protocol.pool.PacketPool;
|
||||
import org.apache.commons.pool2.ObjectPool;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class PacketInboundHandler extends SimpleChannelInboundHandler<ClientSidePacket> {
|
||||
|
||||
private static final String CLIENT_FORCE_DISCONNECTED_IOEXCEPTION_MESSAGE_RU = "Программа на вашем хост-компьютере разорвала установленное подключение";
|
||||
|
||||
private final ObjectPool<NettyConnectionContext> poolNettyConnectionContext;
|
||||
private final PacketPool poolPackets;
|
||||
private final EventBus eventBus;
|
||||
@@ -22,7 +28,21 @@ public class PacketInboundHandler extends SimpleChannelInboundHandler<ClientSide
|
||||
NettyConnectionContext context = poolNettyConnectionContext.borrowObject().setCtx(ctx);
|
||||
eventBus.emit(state, context, packet);
|
||||
|
||||
poolNettyConnectionContext.returnObject(context);
|
||||
if (!context.isUsedContext()) {
|
||||
poolNettyConnectionContext.returnObject(context);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import mc.protocol.packets.ClientSidePacket;
|
||||
import mc.protocol.packets.Packet;
|
||||
import mc.protocol.packets.PingPacket;
|
||||
import mc.protocol.packets.KeepAlivePacket;
|
||||
import mc.protocol.packets.ServerSidePacket;
|
||||
import mc.protocol.packets.client.*;
|
||||
import mc.protocol.packets.server.*;
|
||||
@@ -24,12 +24,12 @@ public enum State {
|
||||
// client side
|
||||
Map.of(
|
||||
0x00, StatusServerRequestPacket.class,
|
||||
0x01, PingPacket.class
|
||||
0x01, KeepAlivePacket.class
|
||||
),
|
||||
// server side
|
||||
Map.of(
|
||||
StatusServerResponse.class, 0x00,
|
||||
PingPacket.class, 0x01
|
||||
KeepAlivePacket.class, 0x01
|
||||
)
|
||||
),
|
||||
LOGIN(2,
|
||||
@@ -47,23 +47,20 @@ public enum State {
|
||||
0x00, TeleportConfirmPacket.class,
|
||||
0x04, ClientSettingsPacket.class,
|
||||
0x09, PluginMessagePacket.class,
|
||||
0x0B, PingPacket.class,
|
||||
0x0B, KeepAlivePacket.class,
|
||||
0x0D, PlayerPositionPacket.class,
|
||||
0x0E, CPlayerPositionAndLookPacket.class,
|
||||
0x0F, PlayerLookPacket.class
|
||||
0x0F, PlayerLookPacket.class,
|
||||
0x15, EntityActionPacket.class
|
||||
),
|
||||
// client bound
|
||||
Map.of(
|
||||
PingPacket.class, 0x1F,
|
||||
JoinGamePacket.class, 0x23,
|
||||
ScoreboardDisplayPacket.class, 0x3B,
|
||||
ScoreboardObjectivePacket.class, 0x42,
|
||||
TeamsPacket.class, 0x44,
|
||||
ScoreboardUpdateScorePacket.class, 0x45,
|
||||
SpawnPositionPacket.class, 0x46,
|
||||
KeepAlivePacket.class, 0x1F,
|
||||
ChunkDataPacket.class, 0x20,
|
||||
JoinGamePacket.class, 0x23,
|
||||
PlayerAbilitiesPacket.class,0x2C,
|
||||
SPlayerPositionAndLookPacket.class, 0x2F
|
||||
SPlayerPositionAndLookPacket.class, 0x2F,
|
||||
SpawnPositionPacket.class, 0x46
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
@@ -3,11 +3,37 @@ package mc.protocol.api;
|
||||
import mc.protocol.State;
|
||||
import mc.protocol.packets.ServerSidePacket;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
public interface ConnectionContext {
|
||||
|
||||
/**
|
||||
* @deprecated костыль
|
||||
*/
|
||||
@Deprecated
|
||||
void setUsedContext(boolean value);
|
||||
|
||||
/**
|
||||
* @deprecated костыль
|
||||
*/
|
||||
@Deprecated
|
||||
boolean isUsedContext();
|
||||
|
||||
State getState();
|
||||
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 sendNow(ServerSidePacket packet);
|
||||
void flushSending();
|
||||
|
||||
@@ -99,11 +99,6 @@ public class NetByteBuf extends ByteBuf {
|
||||
}
|
||||
|
||||
public void writeString(String string) {
|
||||
if (string == null) {
|
||||
writeVarInt(0);
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] buf = string.getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
if (buf.length > Short.MAX_VALUE) {
|
||||
|
||||
@@ -17,6 +17,7 @@ import mc.protocol.pool.PacketPool;
|
||||
import org.apache.commons.pool2.ObjectPool;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
@@ -36,6 +37,8 @@ public class ProtocolDecoder extends ByteToMessageDecoder {
|
||||
|
||||
@Override
|
||||
public void channelActive(@Nonnull ChannelHandlerContext ctx) throws Exception {
|
||||
ctx.channel().attr(NetworkAttributes.CUSTOM_PROPERTIES).set(new HashMap<>());
|
||||
|
||||
NettyConnectionContext context = poolNettyConnectionContext.borrowObject().setCtx(ctx);
|
||||
consumerNewConnection.accept(context);
|
||||
|
||||
@@ -48,6 +51,9 @@ public class ProtocolDecoder extends ByteToMessageDecoder {
|
||||
NettyConnectionContext context = poolNettyConnectionContext.borrowObject().setCtx(ctx);
|
||||
consumerDisconnect.accept(context);
|
||||
|
||||
ctx.channel().attr(NetworkAttributes.CUSTOM_PROPERTIES).get().clear();
|
||||
ctx.channel().attr(NetworkAttributes.CUSTOM_PROPERTIES).set(null);
|
||||
|
||||
poolNettyConnectionContext.returnObject(context);
|
||||
super.channelInactive(ctx);
|
||||
}
|
||||
|
||||
@@ -9,4 +9,16 @@ public class Location {
|
||||
private double x;
|
||||
private double y;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
*/
|
||||
@Data
|
||||
public class PingPacket implements ClientSidePacket, ServerSidePacket {
|
||||
public class KeepAlivePacket implements ClientSidePacket, ServerSidePacket {
|
||||
|
||||
private Long payload;
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -28,13 +28,17 @@ import mc.protocol.packets.ServerSidePacket;
|
||||
@Data
|
||||
public class ChunkDataPacket implements ServerSidePacket {
|
||||
|
||||
private static NetByteBuf voidData;
|
||||
|
||||
private int x;
|
||||
private int z;
|
||||
|
||||
@SuppressWarnings("java:S125")
|
||||
@Override
|
||||
public void writeSelf(NetByteBuf netByteBuf) {
|
||||
netByteBuf.writeInt(x);
|
||||
netByteBuf.writeInt(z);
|
||||
/* Временное отключение кода
|
||||
netByteBuf.writeBoolean(true); // Is Full chunk
|
||||
netByteBuf.writeVarInt(0b11111111); // Available Sections
|
||||
|
||||
@@ -64,6 +68,41 @@ public class ChunkDataPacket implements ServerSidePacket {
|
||||
netByteBuf.writeVarInt(data.readableBytes()); // Size of Data
|
||||
netByteBuf.writeBytes(data); // Data
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,119 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
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;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
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;
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
package mc.protocol.utils;
|
||||
|
||||
public enum ScoreboardObjectiveType {
|
||||
INTEGER,
|
||||
HEARTS
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
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;
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
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;
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
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;
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
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;
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
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;
|
||||
}
|
||||
7
protocol/src/main/java/mc/protocol/world/Chunk.java
Normal file
7
protocol/src/main/java/mc/protocol/world/Chunk.java
Normal file
@@ -0,0 +1,7 @@
|
||||
package mc.protocol.world;
|
||||
|
||||
public interface Chunk {
|
||||
|
||||
int getX();
|
||||
int getZ();
|
||||
}
|
||||
13
protocol/src/main/java/mc/protocol/world/World.java
Normal file
13
protocol/src/main/java/mc/protocol/world/World.java
Normal file
@@ -0,0 +1,13 @@
|
||||
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);
|
||||
}
|
||||
@@ -12,7 +12,7 @@ import mc.protocol.api.Server;
|
||||
import mc.protocol.di.DaggerProtocolComponent;
|
||||
import mc.protocol.di.ProtocolComponent;
|
||||
import mc.protocol.di.ProtocolModule;
|
||||
import mc.protocol.packets.PingPacket;
|
||||
import mc.protocol.packets.KeepAlivePacket;
|
||||
import mc.protocol.packets.client.HandshakePacket;
|
||||
import mc.protocol.packets.client.LoginStartPacket;
|
||||
import mc.protocol.packets.client.StatusServerRequestPacket;
|
||||
@@ -20,6 +20,7 @@ import mc.server.config.Config;
|
||||
import mc.server.di.ConfigModule;
|
||||
import mc.server.di.DaggerServerComponent;
|
||||
import mc.server.di.ServerComponent;
|
||||
import mc.server.service.PlayerManager;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -48,6 +49,7 @@ public class Main {
|
||||
.build();
|
||||
|
||||
Config config = serverComponent.getConfig();
|
||||
PlayerManager playerManager = serverComponent.getPlayerManager();
|
||||
|
||||
ProtocolComponent protocolComponent = DaggerProtocolComponent.builder()
|
||||
.protocolModule(new ProtocolModule(true))
|
||||
@@ -57,13 +59,16 @@ public class Main {
|
||||
PacketHandler packetHandler = serverComponent.getPacketHandler();
|
||||
|
||||
server.onNewConnect(connectionContext -> connectionContext.setState(State.HANDSHAKING));
|
||||
server.onDisonnect(connectionContext -> connectionContext.setState(null));
|
||||
server.onDisonnect(connectionContext -> {
|
||||
connectionContext.setState(null);
|
||||
connectionContext.getCustomProperty("player", Player.class).ifPresent(playerManager::remove);
|
||||
});
|
||||
|
||||
server.listenPacket(State.HANDSHAKING, HandshakePacket.class, packetHandler::onHandshake);
|
||||
server.listenPacket(State.STATUS, PingPacket.class, packetHandler::onKeepAlive);
|
||||
server.listenPacket(State.STATUS, KeepAlivePacket.class, packetHandler::onKeepAlive);
|
||||
server.listenPacket(State.STATUS, StatusServerRequestPacket.class, packetHandler::onServerStatus);
|
||||
server.listenPacket(State.LOGIN, LoginStartPacket.class, packetHandler::onLoginStart);
|
||||
server.listenPacket(State.PLAY, PingPacket.class, packetHandler::onKeepAlivePlay);
|
||||
server.listenPacket(State.PLAY, KeepAlivePacket.class, packetHandler::onKeepAlivePlay);
|
||||
|
||||
server.bind(config.server().host(), config.server().port());
|
||||
}
|
||||
|
||||
@@ -7,14 +7,18 @@ import mc.protocol.api.ConnectionContext;
|
||||
import mc.protocol.model.Location;
|
||||
import mc.protocol.model.Look;
|
||||
import mc.protocol.model.ServerInfo;
|
||||
import mc.protocol.packets.PingPacket;
|
||||
import mc.protocol.packets.KeepAlivePacket;
|
||||
import mc.protocol.packets.client.HandshakePacket;
|
||||
import mc.protocol.packets.client.LoginStartPacket;
|
||||
import mc.protocol.packets.client.StatusServerRequestPacket;
|
||||
import mc.protocol.packets.server.*;
|
||||
import mc.protocol.serializer.TextSerializer;
|
||||
import mc.protocol.utils.*;
|
||||
import mc.protocol.utils.Difficulty;
|
||||
import mc.protocol.utils.GameMode;
|
||||
import mc.protocol.world.Chunk;
|
||||
import mc.protocol.world.World;
|
||||
import mc.server.config.Config;
|
||||
import mc.server.service.PlayerManager;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -23,7 +27,7 @@ import java.nio.file.Path;
|
||||
import java.util.Base64;
|
||||
import java.util.Collections;
|
||||
import java.util.Random;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
@@ -31,18 +35,27 @@ public class PacketHandler {
|
||||
|
||||
private final Random random = new Random(System.currentTimeMillis());
|
||||
private final Config config;
|
||||
private final World world;
|
||||
private final PlayerManager playerManager;
|
||||
|
||||
public void onHandshake(ConnectionContext context, HandshakePacket packet) {
|
||||
context.setState(packet.getNextState());
|
||||
}
|
||||
|
||||
public void onKeepAlive(ConnectionContext context, PingPacket packet) {
|
||||
public void onKeepAlive(ConnectionContext context, KeepAlivePacket packet) {
|
||||
context.sendNow(packet);
|
||||
context.disconnect();
|
||||
}
|
||||
|
||||
public void onKeepAlivePlay(ConnectionContext context, PingPacket packet) {
|
||||
context.sendNow(packet);
|
||||
public void onKeepAlivePlay(ConnectionContext context, KeepAlivePacket packet) {
|
||||
try {
|
||||
TimeUnit.MILLISECONDS.sleep(50);
|
||||
context.sendNow(packet);
|
||||
} catch (InterruptedException e) {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace("{}", e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@@ -51,7 +64,11 @@ public class PacketHandler {
|
||||
serverInfo.version().name(ProtocolConstant.PROTOCOL_NAME);
|
||||
serverInfo.version().protocol(ProtocolConstant.PROTOCOL_NUMBER);
|
||||
serverInfo.players().max(config.players().maxOnlile());
|
||||
serverInfo.players().online(config.players().onlile());
|
||||
if (config.players().fakeOnline().enable()) {
|
||||
serverInfo.players().online(config.players().fakeOnline().value());
|
||||
} else {
|
||||
serverInfo.players().online(playerManager.online());
|
||||
}
|
||||
serverInfo.players().sample(Collections.emptyList());
|
||||
serverInfo.description(TextSerializer.fromPlain(config.motd()));
|
||||
|
||||
@@ -65,31 +82,29 @@ public class PacketHandler {
|
||||
context.sendNow(response);
|
||||
}
|
||||
|
||||
@SuppressWarnings("java:S2589")
|
||||
public void onLoginStart(ConnectionContext context, LoginStartPacket loginStartPacket) {
|
||||
UUID playerUuid = UUID.randomUUID();
|
||||
int playerEid = random.nextInt();
|
||||
String playerName = loginStartPacket.getName();
|
||||
Player player = playerManager.addAndCreate(context, loginStartPacket.getName(), GameMode.SURVIVAL, world.getSpawn());
|
||||
context.setCustomProperty("player", player);
|
||||
|
||||
var loginSuccessPacket = new LoginSuccessPacket();
|
||||
loginSuccessPacket.setUuid(playerUuid);
|
||||
loginSuccessPacket.setName(playerName);
|
||||
loginSuccessPacket.setUuid(player.getUuid());
|
||||
loginSuccessPacket.setName(player.getName());
|
||||
|
||||
context.sendNow(loginSuccessPacket);
|
||||
context.setState(State.PLAY);
|
||||
|
||||
var joinGamePacket = new JoinGamePacket();
|
||||
joinGamePacket.setEntityId(playerEid);
|
||||
joinGamePacket.setGameMode(GameMode.SPECTATOR);
|
||||
joinGamePacket.setEntityId(random.nextInt());
|
||||
joinGamePacket.setGameMode(player.getGameMode());
|
||||
joinGamePacket.setDimension(0/*Overworld*/);
|
||||
joinGamePacket.setDifficulty(Difficulty.PEACEFUL);
|
||||
joinGamePacket.setLevelType(LevelType.FLAT);
|
||||
joinGamePacket.setLevelType(world.getLevelType());
|
||||
|
||||
context.send(joinGamePacket);
|
||||
|
||||
Location spawnLocation = new Location(0d, 63d, 0d);
|
||||
|
||||
var spawnPositionPacket = new SpawnPositionPacket();
|
||||
spawnPositionPacket.setSpawn(spawnLocation);
|
||||
spawnPositionPacket.setSpawn(player.getLocation());
|
||||
|
||||
context.send(spawnPositionPacket);
|
||||
|
||||
@@ -105,51 +120,47 @@ public class PacketHandler {
|
||||
|
||||
context.flushSending();
|
||||
|
||||
var chunkDataPacket = new ChunkDataPacket();
|
||||
chunkDataPacket.setX(0);
|
||||
chunkDataPacket.setZ(0);
|
||||
Location chunkLocation = player.getLocation().toChunkXZ();
|
||||
Chunk chunk = world.getChunk(chunkLocation.getIntX(), chunkLocation.getIntZ());
|
||||
|
||||
context.sendNow(chunkDataPacket);
|
||||
var chunkDataPacket = new ChunkDataPacket();
|
||||
chunkDataPacket.setX(chunk.getX());
|
||||
chunkDataPacket.setZ(chunk.getZ());
|
||||
|
||||
context.send(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();
|
||||
playerPositionAndLookPacket.setPosition(spawnLocation);
|
||||
playerPositionAndLookPacket.setPosition(player.getLocation());
|
||||
playerPositionAndLookPacket.setLook(new Look(0f, 0f));
|
||||
playerPositionAndLookPacket.setTeleportId(random.nextInt());
|
||||
|
||||
context.send(playerPositionAndLookPacket);
|
||||
|
||||
PingPacket pingPacket = new PingPacket();
|
||||
pingPacket.setPayload(System.currentTimeMillis());
|
||||
KeepAlivePacket keepAlivePacket = new KeepAlivePacket();
|
||||
keepAlivePacket.setPayload(System.currentTimeMillis());
|
||||
|
||||
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.send(keepAlivePacket);
|
||||
|
||||
context.flushSending();
|
||||
}
|
||||
|
||||
20
server/src/main/java/mc/server/Player.java
Normal file
20
server/src/main/java/mc/server/Player.java
Normal file
@@ -0,0 +1,20 @@
|
||||
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;
|
||||
}
|
||||
@@ -15,6 +15,7 @@ public class Config {
|
||||
|
||||
private final Server server = new Server();
|
||||
private final Players players = new Players();
|
||||
private final World world = new World();
|
||||
|
||||
private String motd;
|
||||
private String disconnectReason;
|
||||
@@ -32,7 +33,23 @@ public class Config {
|
||||
@Setter
|
||||
@ToString
|
||||
public static class Players {
|
||||
private final FakeOnline fakeOnline = new FakeOnline();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,10 +27,15 @@ public class ConfigModule {
|
||||
|
||||
config.server().host(fromYamlPath("server/host", map, "127.0.0.1"));
|
||||
config.server().port(fromYamlPath("server/port", map, 25565));
|
||||
|
||||
config.motd(fromYamlPath("motd", map, ""));
|
||||
config.disconnectReason(fromYamlPath("disconnect-reason", map, ""));
|
||||
|
||||
config.players().maxOnlile(fromYamlPath("players/max-online", map, 0));
|
||||
config.players().onlile(fromYamlPath("players/online", map, 0));
|
||||
config.players().fakeOnline().enable(fromYamlPath("players/fake-online/enable", map, false));
|
||||
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))) {
|
||||
config.iconPath(Paths.get(fromYamlPath("icon/path", map, "favicon.png")));
|
||||
|
||||
@@ -2,14 +2,16 @@ package mc.server.di;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
import mc.protocol.world.World;
|
||||
import mc.server.PacketHandler;
|
||||
import mc.server.config.Config;
|
||||
import mc.server.service.PlayerManager;
|
||||
|
||||
@Module
|
||||
public class PacketHandlerModule {
|
||||
|
||||
@Provides
|
||||
public PacketHandler providePacketHandler(Config config) {
|
||||
return new PacketHandler(config);
|
||||
public PacketHandler providePacketHandler(Config config, World world, PlayerManager playerManager) {
|
||||
return new PacketHandler(config, world, playerManager);
|
||||
}
|
||||
}
|
||||
|
||||
16
server/src/main/java/mc/server/di/PlayersModule.java
Normal file
16
server/src/main/java/mc/server/di/PlayersModule.java
Normal file
@@ -0,0 +1,16 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,18 @@
|
||||
package mc.server.di;
|
||||
|
||||
import dagger.Component;
|
||||
import mc.protocol.di.ServerScope;
|
||||
import mc.server.PacketHandler;
|
||||
import mc.server.config.Config;
|
||||
import mc.server.service.PlayerManager;
|
||||
|
||||
@Component(modules = {
|
||||
ConfigModule.class, PacketHandlerModule.class
|
||||
ConfigModule.class, PacketHandlerModule.class, WorldModule.class, PlayersModule.class
|
||||
})
|
||||
@ServerScope
|
||||
public interface ServerComponent {
|
||||
|
||||
Config getConfig();
|
||||
PacketHandler getPacketHandler();
|
||||
PlayerManager getPlayerManager();
|
||||
}
|
||||
|
||||
17
server/src/main/java/mc/server/di/WorldModule.java
Normal file
17
server/src/main/java/mc/server/di/WorldModule.java
Normal file
@@ -0,0 +1,17 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
29
server/src/main/java/mc/server/service/PlayerManager.java
Normal file
29
server/src/main/java/mc/server/service/PlayerManager.java
Normal file
@@ -0,0 +1,29 @@
|
||||
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();
|
||||
}
|
||||
}
|
||||
13
server/src/main/java/mc/server/world/VoidChunk.java
Normal file
13
server/src/main/java/mc/server/world/VoidChunk.java
Normal file
@@ -0,0 +1,13 @@
|
||||
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;
|
||||
}
|
||||
26
server/src/main/java/mc/server/world/VoidWorld.java
Normal file
26
server/src/main/java/mc/server/world/VoidWorld.java
Normal file
@@ -0,0 +1,26 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -10,9 +10,14 @@ disconnect-reason: '&4Server is not available.'
|
||||
|
||||
players:
|
||||
max-online: 0
|
||||
online: 0
|
||||
fake-online:
|
||||
enable: false
|
||||
value: 0
|
||||
|
||||
# Размер значка: 64x64 px
|
||||
icon:
|
||||
enable: false
|
||||
path: favicon.png
|
||||
path: favicon.png
|
||||
|
||||
world:
|
||||
view-distance: 1
|
||||
Reference in New Issue
Block a user