This commit is contained in:
Felix Cravic 2020-08-15 13:32:36 +02:00
parent b716d8a47a
commit 92f914ef2d
8 changed files with 301 additions and 30 deletions

View File

@ -9,9 +9,10 @@ import net.minestom.server.advancements.AdvancementManager;
import net.minestom.server.benchmark.BenchmarkManager; import net.minestom.server.benchmark.BenchmarkManager;
import net.minestom.server.command.CommandManager; import net.minestom.server.command.CommandManager;
import net.minestom.server.data.DataManager; import net.minestom.server.data.DataManager;
import net.minestom.server.data.DataType;
import net.minestom.server.data.SerializableData;
import net.minestom.server.entity.EntityManager; import net.minestom.server.entity.EntityManager;
import net.minestom.server.entity.EntityType; import net.minestom.server.entity.EntityType;
import net.minestom.server.entity.Player;
import net.minestom.server.extras.mojangAuth.MojangCrypt; import net.minestom.server.extras.mojangAuth.MojangCrypt;
import net.minestom.server.fluids.Fluid; import net.minestom.server.fluids.Fluid;
import net.minestom.server.gamedata.loottables.LootTableManager; import net.minestom.server.gamedata.loottables.LootTableManager;
@ -20,6 +21,8 @@ import net.minestom.server.instance.Biome;
import net.minestom.server.instance.InstanceManager; import net.minestom.server.instance.InstanceManager;
import net.minestom.server.instance.block.Block; import net.minestom.server.instance.block.Block;
import net.minestom.server.instance.block.BlockManager; import net.minestom.server.instance.block.BlockManager;
import net.minestom.server.instance.block.CustomBlock;
import net.minestom.server.instance.block.rule.BlockPlacementRule;
import net.minestom.server.item.Enchantment; import net.minestom.server.item.Enchantment;
import net.minestom.server.item.Material; import net.minestom.server.item.Material;
import net.minestom.server.listener.manager.PacketListenerManager; import net.minestom.server.listener.manager.PacketListenerManager;
@ -80,10 +83,10 @@ public class MinecraftServer {
public static final int CHUNK_VIEW_DISTANCE = 10; public static final int CHUNK_VIEW_DISTANCE = 10;
public static final int ENTITY_VIEW_DISTANCE = 5; public static final int ENTITY_VIEW_DISTANCE = 5;
public static final int COMPRESSION_THRESHOLD = 256; public static final int COMPRESSION_THRESHOLD = 256;
// Can be modified at performance cost when decreased // Can be modified at performance cost when increased
public static final int TICK_PER_SECOND = 20;
private static final int MS_TO_SEC = 1000; private static final int MS_TO_SEC = 1000;
public static final int TICK_MS = MS_TO_SEC / 20; public static final int TICK_MS = MS_TO_SEC / TICK_PER_SECOND;
public static final int TICK_PER_SECOND = MS_TO_SEC / TICK_MS;
//Extras //Extras
@Getter @Getter
@ -91,7 +94,6 @@ public class MinecraftServer {
private static boolean fixLighting = true; private static boolean fixLighting = true;
//Rate Limiting //Rate Limiting
@Getter @Setter
private static int rateLimit = 0; private static int rateLimit = 0;
// Networking // Networking
@ -208,96 +210,227 @@ public class MinecraftServer {
PacketWriterUtils.writeAndSend(connectionManager.getOnlinePlayers(), brandMessage); PacketWriterUtils.writeAndSend(connectionManager.getOnlinePlayers(), brandMessage);
} }
/**
* Get the max number of packets a client can send over 1 second
*
* @return the packet count limit over 1 second
*/
public static int getRateLimit() {
return rateLimit;
}
/**
* Change the number of packet a client can send over 1 second without being disconnected
*
* @param rateLimit the number of packet, 0 to disable
*/
public static void setRateLimit(int rateLimit) {
MinecraftServer.rateLimit = rateLimit;
}
/**
* Get the server difficulty showed in game option
*
* @return the server difficulty
*/
public static Difficulty getDifficulty() { public static Difficulty getDifficulty() {
return difficulty; return difficulty;
} }
/**
* Change the server difficulty and send the appropriate packet to all connected clients
*
* @param difficulty the new server difficulty
*/
public static void setDifficulty(Difficulty difficulty) { public static void setDifficulty(Difficulty difficulty) {
MinecraftServer.difficulty = difficulty; MinecraftServer.difficulty = difficulty;
for (Player player : connectionManager.getOnlinePlayers()) {
ServerDifficultyPacket serverDifficultyPacket = new ServerDifficultyPacket(); // The difficulty packet
serverDifficultyPacket.difficulty = difficulty; ServerDifficultyPacket serverDifficultyPacket = new ServerDifficultyPacket();
serverDifficultyPacket.locked = true; serverDifficultyPacket.difficulty = difficulty;
player.getPlayerConnection().sendPacket(serverDifficultyPacket); serverDifficultyPacket.locked = true; // Can only be modified on singleplayer
} // Send the packet to all online players
PacketWriterUtils.writeAndSend(connectionManager.getOnlinePlayers(), serverDifficultyPacket);
} }
/**
* Get the manager handling all incoming packets
*
* @return the packet listener manager
*/
public static PacketListenerManager getPacketListenerManager() { public static PacketListenerManager getPacketListenerManager() {
return packetListenerManager; return packetListenerManager;
} }
/**
* Get the netty server
*
* @return the netty server
*/
public static NettyServer getNettyServer() { public static NettyServer getNettyServer() {
return nettyServer; return nettyServer;
} }
/**
* Get the manager handling all registered instances
*
* @return the instance manager
*/
public static InstanceManager getInstanceManager() { public static InstanceManager getInstanceManager() {
return instanceManager; return instanceManager;
} }
/**
* Get the manager handling {@link CustomBlock} and {@link BlockPlacementRule}
*
* @return the block manager
*/
public static BlockManager getBlockManager() { public static BlockManager getBlockManager() {
return blockManager; return blockManager;
} }
/**
* Get the manager handling waiting players
*
* @return the entity manager
*/
public static EntityManager getEntityManager() { public static EntityManager getEntityManager() {
return entityManager; return entityManager;
} }
/**
* Get the manager handling commands
*
* @return the command manager
*/
public static CommandManager getCommandManager() { public static CommandManager getCommandManager() {
return commandManager; return commandManager;
} }
/**
* Get the manager handling recipes show to the clients
*
* @return the recipe manager
*/
public static RecipeManager getRecipeManager() { public static RecipeManager getRecipeManager() {
return recipeManager; return recipeManager;
} }
/**
* Get the manager handling storage
*
* @return the storage manager
*/
public static StorageManager getStorageManager() { public static StorageManager getStorageManager() {
return storageManager; return storageManager;
} }
/**
* Get the manager handling {@link DataType} used by {@link SerializableData}
*
* @return the data manager
*/
public static DataManager getDataManager() { public static DataManager getDataManager() {
return dataManager; return dataManager;
} }
/**
* Get the manager handling teams
*
* @return the team manager
*/
public static TeamManager getTeamManager() { public static TeamManager getTeamManager() {
return teamManager; return teamManager;
} }
/**
* Get the manager handling scheduled tasks
*
* @return the scheduler manager
*/
public static SchedulerManager getSchedulerManager() { public static SchedulerManager getSchedulerManager() {
return schedulerManager; return schedulerManager;
} }
/**
* Get the manager handling server monitoring
*
* @return the benchmark manager
*/
public static BenchmarkManager getBenchmarkManager() { public static BenchmarkManager getBenchmarkManager() {
return benchmarkManager; return benchmarkManager;
} }
/**
* Get the manager handling server connections
*
* @return the connection manager
*/
public static ConnectionManager getConnectionManager() { public static ConnectionManager getConnectionManager() {
return connectionManager; return connectionManager;
} }
/**
* Get the consumer executed to show server-list data
*
* @return the response data consumer
*/
public static ResponseDataConsumer getResponseDataConsumer() { public static ResponseDataConsumer getResponseDataConsumer() {
return responseDataConsumer; return responseDataConsumer;
} }
/**
* Get the manager handling loot tables
*
* @return the loot table manager
*/
public static LootTableManager getLootTableManager() { public static LootTableManager getLootTableManager() {
return lootTableManager; return lootTableManager;
} }
/**
* Get the manager handling dimensions
*
* @return the dimension manager
*/
public static DimensionTypeManager getDimensionTypeManager() { public static DimensionTypeManager getDimensionTypeManager() {
return dimensionTypeManager; return dimensionTypeManager;
} }
/**
* Get the manager handling advancements
*
* @return the advancement manager
*/
public static AdvancementManager getAdvancementManager() { public static AdvancementManager getAdvancementManager() {
return advancementManager; return advancementManager;
} }
/**
* Get the manager handling tags
*
* @return the tag manager
*/
public static TagManager getTagManager() { public static TagManager getTagManager() {
return tagManager; return tagManager;
} }
/**
* Get the manager handling the server ticks
*
* @return the update manager
*/
public static UpdateManager getUpdateManager() { public static UpdateManager getUpdateManager() {
return updateManager; return updateManager;
} }
/**
* Start the server
*
* @param address the server address
* @param port the server port
* @param responseDataConsumer the response data consumer, can be null
*/
public void start(String address, int port, ResponseDataConsumer responseDataConsumer) { public void start(String address, int port, ResponseDataConsumer responseDataConsumer) {
LOGGER.info("Starting Minestom server."); LOGGER.info("Starting Minestom server.");
MinecraftServer.responseDataConsumer = responseDataConsumer; MinecraftServer.responseDataConsumer = responseDataConsumer;
@ -306,6 +439,12 @@ public class MinecraftServer {
LOGGER.info("Minestom server started successfully."); LOGGER.info("Minestom server started successfully.");
} }
/**
* Start the server
*
* @param address the server address
* @param port the server port
*/
public void start(String address, int port) { public void start(String address, int port) {
start(address, port, null); start(address, port, null);
} }

View File

@ -14,7 +14,10 @@ import net.minestom.server.event.entity.EntitySpawnEvent;
import net.minestom.server.event.entity.EntityTickEvent; import net.minestom.server.event.entity.EntityTickEvent;
import net.minestom.server.event.entity.EntityVelocityEvent; import net.minestom.server.event.entity.EntityVelocityEvent;
import net.minestom.server.event.handler.EventHandler; import net.minestom.server.event.handler.EventHandler;
import net.minestom.server.instance.*; import net.minestom.server.instance.Chunk;
import net.minestom.server.instance.Instance;
import net.minestom.server.instance.InstanceManager;
import net.minestom.server.instance.WorldBorder;
import net.minestom.server.instance.block.CustomBlock; import net.minestom.server.instance.block.CustomBlock;
import net.minestom.server.network.packet.PacketWriter; import net.minestom.server.network.packet.PacketWriter;
import net.minestom.server.network.packet.server.play.*; import net.minestom.server.network.packet.server.play.*;
@ -634,13 +637,11 @@ public abstract class Entity implements Viewable, EventHandler, DataContainer {
* *
* @param instance the new instance of the entity * @param instance the new instance of the entity
* @throws NullPointerException if {@code instance} is null * @throws NullPointerException if {@code instance} is null
* @throws IllegalStateException if {@code instance} has not been registered in * @throws IllegalStateException if {@code instance} has not been registered in {@link InstanceManager}
* {@link InstanceManager#createInstanceContainer()} or
* {@link InstanceManager#createSharedInstance(InstanceContainer)}
*/ */
public void setInstance(Instance instance) { public void setInstance(Instance instance) {
Check.notNull(instance, "instance cannot be null!"); Check.notNull(instance, "instance cannot be null!");
Check.stateCondition(!MinecraftServer.getInstanceManager().getInstances().contains(instance), Check.stateCondition(!instance.isRegistered(),
"Instances need to be registered with InstanceManager#createInstanceContainer or InstanceManager#createSharedInstance"); "Instances need to be registered with InstanceManager#createInstanceContainer or InstanceManager#createSharedInstance");
if (this.instance != null) { if (this.instance != null) {

View File

@ -49,6 +49,8 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta
protected static final BlockManager BLOCK_MANAGER = MinecraftServer.getBlockManager(); protected static final BlockManager BLOCK_MANAGER = MinecraftServer.getBlockManager();
protected static final UpdateManager UPDATE_MANAGER = MinecraftServer.getUpdateManager(); protected static final UpdateManager UPDATE_MANAGER = MinecraftServer.getUpdateManager();
private boolean registered;
private DimensionType dimensionType; private DimensionType dimensionType;
private WorldBorder worldBorder; private WorldBorder worldBorder;
@ -306,6 +308,26 @@ public abstract class Instance implements BlockModifier, EventHandler, DataConta
// //
/**
* Get if the instance has been registered in {@link InstanceManager}
*
* @return true if the instance has been registered
*/
public boolean isRegistered() {
return registered;
}
/**
* Change the registered field
* <p>
* WARNING: should only be used by {@link InstanceManager}
*
* @param registered true to mark the instance as registered
*/
protected void setRegistered(boolean registered) {
this.registered = registered;
}
/** /**
* Get the instance dimension * Get the instance dimension
* *

View File

@ -518,6 +518,13 @@ public class InstanceContainer extends Instance {
return position.getY() < -64; return position.getY() < -64;
} }
/**
* Assign a {@link SharedInstance} to this container
* <p>
* Only used by {@link InstanceManager}
*
* @param sharedInstance the shared instance to assign to this container
*/
protected void addSharedInstance(SharedInstance sharedInstance) { protected void addSharedInstance(SharedInstance sharedInstance) {
this.sharedInstances.add(sharedInstance); this.sharedInstances.add(sharedInstance);
} }

View File

@ -5,54 +5,119 @@ import net.minestom.server.utils.validate.Check;
import net.minestom.server.world.DimensionType; import net.minestom.server.world.DimensionType;
import java.util.Collections; import java.util.Collections;
import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* Used to register instances
*/
public final class InstanceManager { public final class InstanceManager {
private Set<Instance> instances = Collections.synchronizedSet(new HashSet<>()); private final Set<Instance> instances = new CopyOnWriteArraySet<>();
public InstanceContainer createInstanceContainer(InstanceContainer instanceContainer) { /**
this.instances.add(instanceContainer); * Register an {@link InstanceContainer}
*
* @param instanceContainer the instance to register
* @return the registered {@link InstanceContainer}
*/
public InstanceContainer registerInstanceContainer(InstanceContainer instanceContainer) {
registerInstance(instanceContainer);
return instanceContainer; return instanceContainer;
} }
/**
* Create and register an {@link InstanceContainer} with the specified dimension and storage folder
*
* @param dimensionType the dimension of the instance
* @param storageFolder the storage folder of the instance, can be null
* @return the created {@link InstanceContainer}
*/
public InstanceContainer createInstanceContainer(DimensionType dimensionType, StorageFolder storageFolder) { public InstanceContainer createInstanceContainer(DimensionType dimensionType, StorageFolder storageFolder) {
InstanceContainer instance = new InstanceContainer(UUID.randomUUID(), dimensionType, storageFolder); final InstanceContainer instance = new InstanceContainer(UUID.randomUUID(), dimensionType, storageFolder);
return createInstanceContainer(instance); return registerInstanceContainer(instance);
} }
/**
* Create and register an {@link InstanceContainer} with the specified storage folder
*
* @param storageFolder the storage folder of the instance, can be null
* @return the created {@link InstanceContainer}
*/
public InstanceContainer createInstanceContainer(StorageFolder storageFolder) { public InstanceContainer createInstanceContainer(StorageFolder storageFolder) {
return createInstanceContainer(DimensionType.OVERWORLD, storageFolder); return createInstanceContainer(DimensionType.OVERWORLD, storageFolder);
} }
/**
* Create and register an {@link InstanceContainer} with the specified dimension
*
* @param dimensionType the dimension of the instance
* @return the created {@link InstanceContainer}
*/
public InstanceContainer createInstanceContainer(DimensionType dimensionType) { public InstanceContainer createInstanceContainer(DimensionType dimensionType) {
return createInstanceContainer(dimensionType, null); return createInstanceContainer(dimensionType, null);
} }
/**
* Create and register an {@link InstanceContainer}
*
* @return the created {@link InstanceContainer}
*/
public InstanceContainer createInstanceContainer() { public InstanceContainer createInstanceContainer() {
return createInstanceContainer(DimensionType.OVERWORLD); return createInstanceContainer(DimensionType.OVERWORLD);
} }
public SharedInstance createSharedInstance(SharedInstance sharedInstance) { /**
* Register a {@link SharedInstance}
* <p>
* WARNING: the shared instance needs to have an {@link InstanceContainer} assigned to it
*
* @param sharedInstance the instance to register
* @return the registered {@link SharedInstance}
* @throws NullPointerException if the shared instance doesn't have an {@link InstanceContainer} assigned to it
*/
public SharedInstance registerSharedInstance(SharedInstance sharedInstance) {
final InstanceContainer instanceContainer = sharedInstance.getInstanceContainer(); final InstanceContainer instanceContainer = sharedInstance.getInstanceContainer();
Check.notNull(instanceContainer, "SharedInstance needs to have an InstanceContainer to be created!"); Check.notNull(instanceContainer, "SharedInstance needs to have an InstanceContainer to be created!");
instanceContainer.addSharedInstance(sharedInstance); instanceContainer.addSharedInstance(sharedInstance);
this.instances.add(sharedInstance); registerInstance(sharedInstance);
return sharedInstance; return sharedInstance;
} }
/**
* Create and register a {@link SharedInstance}
*
* @param instanceContainer the container assigned to the shared instance
* @return the created {@link SharedInstance}
* @throws IllegalStateException if {@code instanceContainer} is not registered
*/
public SharedInstance createSharedInstance(InstanceContainer instanceContainer) { public SharedInstance createSharedInstance(InstanceContainer instanceContainer) {
Check.notNull(instanceContainer, "Instance container cannot be null when creating a SharedInstance!"); Check.notNull(instanceContainer, "Instance container cannot be null when creating a SharedInstance!");
Check.stateCondition(!instanceContainer.isRegistered(), "The container needs to be register in the InstanceManager");
final SharedInstance sharedInstance = new SharedInstance(UUID.randomUUID(), instanceContainer); final SharedInstance sharedInstance = new SharedInstance(UUID.randomUUID(), instanceContainer);
return createSharedInstance(sharedInstance); return registerSharedInstance(sharedInstance);
} }
/**
* Get all the registered instances
*
* @return an unmodifiable set containing all the registered instances
*/
public Set<Instance> getInstances() { public Set<Instance> getInstances() {
return Collections.unmodifiableSet(instances); return Collections.unmodifiableSet(instances);
} }
/**
* Register the instance internally
*
* @param instance the instance to register
*/
private void registerInstance(Instance instance) {
instance.setRegistered(true);
this.instances.add(instance);
}
} }

View File

@ -52,6 +52,13 @@ public class MinestomBasicChunkLoader implements IChunkLoader {
} }
} }
/**
* Get the chunk key used by the {@link StorageFolder}
*
* @param chunkX the chunk X
* @param chunkZ the chunk Z
* @return the chunk key
*/
private static String getChunkKey(int chunkX, int chunkZ) { private static String getChunkKey(int chunkX, int chunkZ) {
return chunkX + "." + chunkZ; return chunkX + "." + chunkZ;
} }

View File

@ -20,7 +20,7 @@ public class ClientChannel extends SimpleChannelInboundHandler<InboundPacket> {
@Override @Override
public void channelActive(ChannelHandlerContext ctx) { public void channelActive(ChannelHandlerContext ctx) {
System.out.println("CONNECTION"); //System.out.println("CONNECTION");
} }
@Override @Override
@ -28,7 +28,7 @@ public class ClientChannel extends SimpleChannelInboundHandler<InboundPacket> {
try { try {
packetProcessor.process(ctx, packet); packetProcessor.process(ctx, packet);
} finally { } finally {
int availableBytes = packet.body.readableBytes(); final int availableBytes = packet.body.readableBytes();
if (availableBytes > 0) { if (availableBytes > 0) {
// TODO log4j2 // TODO log4j2
@ -44,6 +44,7 @@ public class ClientChannel extends SimpleChannelInboundHandler<InboundPacket> {
public void channelInactive(ChannelHandlerContext ctx) { public void channelInactive(ChannelHandlerContext ctx) {
PlayerConnection playerConnection = packetProcessor.getPlayerConnection(ctx); PlayerConnection playerConnection = packetProcessor.getPlayerConnection(ctx);
if (playerConnection != null) { if (playerConnection != null) {
// Remove the connection
playerConnection.refreshOnline(false); playerConnection.refreshOnline(false);
Player player = playerConnection.getPlayer(); Player player = playerConnection.getPlayer();
if (player != null) { if (player != null) {

View File

@ -4,6 +4,7 @@ import io.netty.buffer.ByteBuf;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import net.minestom.server.MinecraftServer; import net.minestom.server.MinecraftServer;
import net.minestom.server.chat.ChatColor;
import net.minestom.server.chat.ColoredText; import net.minestom.server.chat.ColoredText;
import net.minestom.server.entity.Player; import net.minestom.server.entity.Player;
import net.minestom.server.network.ConnectionState; import net.minestom.server.network.ConnectionState;
@ -32,11 +33,13 @@ public abstract class PlayerConnection {
private ConnectionState connectionState; private ConnectionState connectionState;
private boolean online; private boolean online;
private static final ColoredText rateLimitKickMessage = ColoredText.of("Too Many Packets"); // Text used to kick client sending too many packets
private static final ColoredText rateLimitKickMessage = ColoredText.of(ChatColor.RED + "Too Many Packets");
//Connection Stats //Connection Stats
@Getter @Getter
private final AtomicInteger packetCounter = new AtomicInteger(0); private final AtomicInteger packetCounter = new AtomicInteger(0);
private final AtomicInteger lastPacketCounter = new AtomicInteger(0);
private short tickCounter = 0; private short tickCounter = 0;
public PlayerConnection() { public PlayerConnection() {
@ -44,14 +47,21 @@ public abstract class PlayerConnection {
this.connectionState = ConnectionState.UNKNOWN; this.connectionState = ConnectionState.UNKNOWN;
} }
/**
* Update values related to the network connection
*/
public void updateStats() { public void updateStats() {
// Check rate limit
if (MinecraftServer.getRateLimit() > 0) { if (MinecraftServer.getRateLimit() > 0) {
tickCounter++; tickCounter++;
if (tickCounter % 20 == 0 && tickCounter > 0) { if (tickCounter % MinecraftServer.TICK_PER_SECOND == 0 && tickCounter > 0) {
tickCounter = 0; tickCounter = 0;
// Retrieve the packet count
final int count = packetCounter.get(); final int count = packetCounter.get();
packetCounter.set(0); this.lastPacketCounter.set(count);
this.packetCounter.set(0);
if (count > MinecraftServer.getRateLimit()) { if (count > MinecraftServer.getRateLimit()) {
// Sent too many packets
if (connectionState == ConnectionState.LOGIN) { if (connectionState == ConnectionState.LOGIN) {
sendPacket(new LoginDisconnect("Too Many Packets")); sendPacket(new LoginDisconnect("Too Many Packets"));
} else { } else {
@ -96,6 +106,11 @@ public abstract class PlayerConnection {
*/ */
public abstract void flush(); public abstract void flush();
/**
* Get the remote address of the client
*
* @return the remote address
*/
public abstract SocketAddress getRemoteAddress(); public abstract SocketAddress getRemoteAddress();
/** /**
@ -140,7 +155,21 @@ public abstract class PlayerConnection {
this.connectionState = connectionState; this.connectionState = connectionState;
} }
/**
* Get the client connection state
*
* @return the client connection state
*/
public ConnectionState getConnectionState() { public ConnectionState getConnectionState() {
return connectionState; return connectionState;
} }
/**
* Get the number of packet the client sent over the last second
*
* @return the number of packet sent over the last second
*/
public int getLastPacketCounter() {
return lastPacketCounter.get();
}
} }