mirror of
https://github.com/Minestom/Minestom.git
synced 2024-12-31 21:48:08 +01:00
Merge branch 'master' into position-cleanup
This commit is contained in:
commit
f69b40109c
@ -5,7 +5,7 @@ plugins {
|
||||
id 'java'
|
||||
id 'maven-publish'
|
||||
id 'net.ltgt.apt' version '0.10'
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.4.21'
|
||||
id 'org.jetbrains.kotlin.jvm' version '1.5.0'
|
||||
id 'checkstyle'
|
||||
}
|
||||
|
||||
|
@ -78,13 +78,15 @@ public final class MinecraftServer {
|
||||
public static final String THREAD_NAME_TICK = "Ms-Tick";
|
||||
|
||||
public static final String THREAD_NAME_BLOCK_BATCH = "Ms-BlockBatchPool";
|
||||
public static final int THREAD_COUNT_BLOCK_BATCH = 4;
|
||||
public static final int THREAD_COUNT_BLOCK_BATCH = getThreadCount("minestom.block-thread-count",
|
||||
Runtime.getRuntime().availableProcessors() / 2);
|
||||
|
||||
public static final String THREAD_NAME_SCHEDULER = "Ms-SchedulerPool";
|
||||
public static final int THREAD_COUNT_SCHEDULER = 1;
|
||||
public static final int THREAD_COUNT_SCHEDULER = getThreadCount("minestom.scheduler-thread-count",
|
||||
Runtime.getRuntime().availableProcessors() / 2);
|
||||
|
||||
public static final String THREAD_NAME_PARALLEL_CHUNK_SAVING = "Ms-ParallelChunkSaving";
|
||||
public static final int THREAD_COUNT_PARALLEL_CHUNK_SAVING = 4;
|
||||
public static final int THREAD_COUNT_PARALLEL_CHUNK_SAVING = getThreadCount("minestom.save-thread-count", 2);
|
||||
|
||||
// Config
|
||||
// Can be modified at performance cost when increased
|
||||
@ -823,4 +825,8 @@ public final class MinecraftServer {
|
||||
"You cannot access the manager before MinecraftServer#init, " +
|
||||
"if you are developing an extension be sure to retrieve them at least after Extension#preInitialize");*/
|
||||
}
|
||||
|
||||
private static int getThreadCount(@NotNull String property, int count) {
|
||||
return Integer.getInteger(property, Math.min(1, count));
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
@ -26,45 +27,44 @@ public class ArgumentParser {
|
||||
private static final Map<String, Function<String, Argument<?>>> ARGUMENT_FUNCTION_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
static {
|
||||
ARGUMENT_FUNCTION_MAP.put("Literal", ArgumentLiteral::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("Boolean", ArgumentBoolean::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("Integer", ArgumentInteger::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("Double", ArgumentDouble::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("Float", ArgumentFloat::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("String", ArgumentString::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("Word", ArgumentWord::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("StringArray", ArgumentStringArray::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("Command", ArgumentCommand::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("literal", ArgumentLiteral::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("boolean", ArgumentBoolean::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("integer", ArgumentInteger::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("double", ArgumentDouble::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("float", ArgumentFloat::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("string", ArgumentString::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("word", ArgumentWord::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("stringarray", ArgumentStringArray::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("command", ArgumentCommand::new);
|
||||
// TODO enum
|
||||
ARGUMENT_FUNCTION_MAP.put("Color", ArgumentColor::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("Time", ArgumentTime::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("Enchantment", ArgumentEnchantment::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("Particle", ArgumentParticle::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("ResourceLocation", ArgumentResourceLocation::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("Potion", ArgumentPotionEffect::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("EntityType", ArgumentEntityType::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("BlockState", ArgumentBlockState::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("IntRange", ArgumentIntRange::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("FloatRange", ArgumentFloatRange::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("color", ArgumentColor::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("time", ArgumentTime::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("enchantment", ArgumentEnchantment::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("particle", ArgumentParticle::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("resourceLocation", ArgumentResourceLocation::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("potion", ArgumentPotionEffect::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("entityType", ArgumentEntityType::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("blockState", ArgumentBlockState::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("intrange", ArgumentIntRange::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("floatrange", ArgumentFloatRange::new);
|
||||
|
||||
ARGUMENT_FUNCTION_MAP.put("Entity", s -> new ArgumentEntity(s).singleEntity(true));
|
||||
ARGUMENT_FUNCTION_MAP.put("Entities", ArgumentEntity::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("Player", s -> new ArgumentEntity(s).singleEntity(true).onlyPlayers(true));
|
||||
ARGUMENT_FUNCTION_MAP.put("Players", s -> new ArgumentEntity(s).onlyPlayers(true));
|
||||
ARGUMENT_FUNCTION_MAP.put("entity", s -> new ArgumentEntity(s).singleEntity(true));
|
||||
ARGUMENT_FUNCTION_MAP.put("entities", ArgumentEntity::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("player", s -> new ArgumentEntity(s).singleEntity(true).onlyPlayers(true));
|
||||
ARGUMENT_FUNCTION_MAP.put("players", s -> new ArgumentEntity(s).onlyPlayers(true));
|
||||
|
||||
ARGUMENT_FUNCTION_MAP.put("ItemStack", ArgumentItemStack::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("Component", ArgumentComponent::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("UUID", ArgumentUUID::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("NBT", ArgumentNbtTag::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("NbtCompound", ArgumentNbtCompoundTag::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("RelativeBlockPosition", ArgumentRelativeBlockPosition::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("RelativeVec3", ArgumentRelativeVec3::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("RelativeVec2", ArgumentRelativeVec2::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("itemstack", ArgumentItemStack::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("component", ArgumentComponent::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("uuid", ArgumentUUID::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("nbt", ArgumentNbtTag::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("nbtcompound", ArgumentNbtCompoundTag::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("relativeblockposition", ArgumentRelativeBlockPosition::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("relativevec3", ArgumentRelativeVec3::new);
|
||||
ARGUMENT_FUNCTION_MAP.put("relativevec2", ArgumentRelativeVec2::new);
|
||||
}
|
||||
|
||||
@Beta
|
||||
@NotNull
|
||||
public static Argument<?>[] generate(@NotNull String format) {
|
||||
public static @NotNull Argument<?>[] generate(@NotNull String format) {
|
||||
List<Argument<?>> result = new ArrayList<>();
|
||||
|
||||
// 0 = no state
|
||||
@ -92,7 +92,7 @@ public class ArgumentParser {
|
||||
} else if (c == '<') {
|
||||
// Retrieve argument type
|
||||
final String argument = builder.toString();
|
||||
argumentFunction = ARGUMENT_FUNCTION_MAP.get(argument);
|
||||
argumentFunction = ARGUMENT_FUNCTION_MAP.get(argument.toLowerCase(Locale.ROOT));
|
||||
if (argumentFunction == null) {
|
||||
throw new IllegalArgumentException("error invalid argument name: " + argument);
|
||||
}
|
||||
|
@ -417,22 +417,18 @@ public abstract class Chunk implements Viewable, Tickable, DataContainer {
|
||||
UpdateLightPacket updateLightPacket = new UpdateLightPacket(getIdentifier(), getLastChangeTime());
|
||||
updateLightPacket.chunkX = getChunkX();
|
||||
updateLightPacket.chunkZ = getChunkZ();
|
||||
updateLightPacket.skyLightMask = 0x3FFF0;
|
||||
updateLightPacket.blockLightMask = 0x3F;
|
||||
updateLightPacket.emptySkyLightMask = 0x0F;
|
||||
updateLightPacket.emptyBlockLightMask = 0x3FFC0;
|
||||
updateLightPacket.skyLightMask = 0b111111111111111111;
|
||||
updateLightPacket.emptySkyLightMask = 0b000000000000000000;
|
||||
updateLightPacket.blockLightMask = 0b000000000000000000;
|
||||
updateLightPacket.emptyBlockLightMask = 0b111111111111111111;
|
||||
byte[] bytes = new byte[2048];
|
||||
Arrays.fill(bytes, (byte) 0xFF);
|
||||
List<byte[]> temp = new ArrayList<>(14);
|
||||
List<byte[]> temp2 = new ArrayList<>(6);
|
||||
for (int i = 0; i < 14; ++i) {
|
||||
final List<byte[]> temp = new ArrayList<>(18);
|
||||
for (int i = 0; i < 18; ++i) {
|
||||
temp.add(bytes);
|
||||
}
|
||||
for (int i = 0; i < 6; ++i) {
|
||||
temp2.add(bytes);
|
||||
}
|
||||
updateLightPacket.skyLight = temp;
|
||||
updateLightPacket.blockLight = temp2;
|
||||
updateLightPacket.blockLight = new ArrayList<>(0);
|
||||
|
||||
return updateLightPacket;
|
||||
}
|
||||
|
@ -26,6 +26,7 @@ import net.minestom.server.world.biomes.Biome;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.lang.ref.SoftReference;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@ -59,7 +60,7 @@ public class DynamicChunk extends Chunk {
|
||||
|
||||
private long lastChangeTime;
|
||||
|
||||
private ChunkDataPacket cachedPacket;
|
||||
private SoftReference<ChunkDataPacket> cachedPacket = new SoftReference<>(null);
|
||||
private long cachedPacketTime;
|
||||
|
||||
public DynamicChunk(@NotNull Instance instance, @Nullable Biome[] biomes, int chunkX, int chunkZ,
|
||||
@ -386,22 +387,22 @@ public class DynamicChunk extends Chunk {
|
||||
@NotNull
|
||||
@Override
|
||||
protected ChunkDataPacket createFreshPacket() {
|
||||
if (cachedPacket != null && cachedPacketTime == getLastChangeTime()) {
|
||||
return cachedPacket;
|
||||
ChunkDataPacket packet = cachedPacket.get();
|
||||
if (packet != null && cachedPacketTime == getLastChangeTime()) {
|
||||
return packet;
|
||||
}
|
||||
ChunkDataPacket fullDataPacket = new ChunkDataPacket(getIdentifier(), getLastChangeTime());
|
||||
fullDataPacket.biomes = biomes;
|
||||
fullDataPacket.chunkX = chunkX;
|
||||
fullDataPacket.chunkZ = chunkZ;
|
||||
fullDataPacket.paletteStorage = blockPalette.clone();
|
||||
fullDataPacket.customBlockPaletteStorage = customBlockPalette.clone();
|
||||
fullDataPacket.blockEntities = blockEntities.clone();
|
||||
fullDataPacket.blocksData = blocksData.clone();
|
||||
packet = new ChunkDataPacket(getIdentifier(), getLastChangeTime());
|
||||
packet.biomes = biomes;
|
||||
packet.chunkX = chunkX;
|
||||
packet.chunkZ = chunkZ;
|
||||
packet.paletteStorage = blockPalette.clone();
|
||||
packet.customBlockPaletteStorage = customBlockPalette.clone();
|
||||
packet.blockEntities = blockEntities.clone();
|
||||
packet.blocksData = blocksData.clone();
|
||||
|
||||
this.cachedPacketTime = getLastChangeTime();
|
||||
this.cachedPacket = fullDataPacket;
|
||||
|
||||
return fullDataPacket;
|
||||
this.cachedPacket = new SoftReference<>(packet);
|
||||
return packet;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
@ -793,6 +793,10 @@ public class InstanceContainer extends Instance {
|
||||
* Unsafe because it has to be done on the same thread as the instance/chunks tick update.
|
||||
*/
|
||||
protected void UNSAFE_unloadChunks() {
|
||||
if (scheduledChunksToRemove.isEmpty()) {
|
||||
// Fast exit
|
||||
return;
|
||||
}
|
||||
synchronized (scheduledChunksToRemove) {
|
||||
for (Chunk chunk : scheduledChunksToRemove) {
|
||||
final int chunkX = chunk.getChunkX();
|
||||
|
@ -272,38 +272,40 @@ public class PlayerInventory extends AbstractInventory implements EquipmentHandl
|
||||
|
||||
@Override
|
||||
public boolean leftClick(@NotNull Player player, int slot) {
|
||||
final int convertedSlot = convertPlayerInventorySlot(slot, OFFSET);
|
||||
final ItemStack cursor = getCursorItem();
|
||||
final ItemStack clicked = getItemStack(convertPlayerInventorySlot(slot, OFFSET));
|
||||
final ItemStack clicked = getItemStack(convertedSlot);
|
||||
|
||||
final InventoryClickResult clickResult = clickProcessor.leftClick(null, player, slot, clicked, cursor);
|
||||
final InventoryClickResult clickResult = clickProcessor.leftClick(null, player, convertedSlot, clicked, cursor);
|
||||
|
||||
if (clickResult.doRefresh())
|
||||
sendSlotRefresh((short) slot, clicked);
|
||||
|
||||
setItemStack(slot, OFFSET, clickResult.getClicked());
|
||||
setItemStack(convertedSlot, clickResult.getClicked());
|
||||
setCursorItem(clickResult.getCursor());
|
||||
|
||||
if (!clickResult.isCancel())
|
||||
callClickEvent(player, null, slot, ClickType.LEFT_CLICK, clicked, cursor);
|
||||
callClickEvent(player, null, convertedSlot, ClickType.LEFT_CLICK, clicked, cursor);
|
||||
|
||||
return !clickResult.isCancel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean rightClick(@NotNull Player player, int slot) {
|
||||
final int convertedSlot = convertPlayerInventorySlot(slot, OFFSET);
|
||||
final ItemStack cursor = getCursorItem();
|
||||
final ItemStack clicked = getItemStack(slot, OFFSET);
|
||||
final ItemStack clicked = getItemStack(convertedSlot);
|
||||
|
||||
final InventoryClickResult clickResult = clickProcessor.rightClick(null, player, slot, clicked, cursor);
|
||||
final InventoryClickResult clickResult = clickProcessor.rightClick(null, player, convertedSlot, clicked, cursor);
|
||||
|
||||
if (clickResult.doRefresh())
|
||||
sendSlotRefresh((short) slot, clicked);
|
||||
|
||||
setItemStack(slot, OFFSET, clickResult.getClicked());
|
||||
setItemStack(convertedSlot, clickResult.getClicked());
|
||||
setCursorItem(clickResult.getCursor());
|
||||
|
||||
if (!clickResult.isCancel())
|
||||
callClickEvent(player, null, slot, ClickType.RIGHT_CLICK, clicked, cursor);
|
||||
callClickEvent(player, null, convertedSlot, ClickType.RIGHT_CLICK, clicked, cursor);
|
||||
|
||||
return !clickResult.isCancel();
|
||||
}
|
||||
|
@ -99,13 +99,15 @@ public class BlockPlacementListener {
|
||||
blockPosition.add(offsetX, offsetY, offsetZ);
|
||||
|
||||
if (!canPlaceBlock) {
|
||||
//Send a block change with AIR as block to keep the client in sync,
|
||||
//using refreshChunk results in the client not being in sync
|
||||
//after rapid invalid block placements
|
||||
BlockChangePacket blockChangePacket = new BlockChangePacket();
|
||||
blockChangePacket.blockPosition = blockPosition;
|
||||
blockChangePacket.blockStateId = Block.AIR.getBlockId();
|
||||
player.getPlayerConnection().sendPacket(blockChangePacket);
|
||||
if (useMaterial.isBlock()) {
|
||||
//Send a block change with AIR as block to keep the client in sync,
|
||||
//using refreshChunk results in the client not being in sync
|
||||
//after rapid invalid block placements
|
||||
BlockChangePacket blockChangePacket = new BlockChangePacket();
|
||||
blockChangePacket.blockPosition = blockPosition;
|
||||
blockChangePacket.blockStateId = Block.AIR.getBlockId();
|
||||
player.getPlayerConnection().sendPacket(blockChangePacket);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -76,8 +76,8 @@ public final class BenchmarkManager {
|
||||
|
||||
stop = false;
|
||||
|
||||
}, MinecraftServer.THREAD_NAME_BENCHMARK, 0L);
|
||||
|
||||
}, MinecraftServer.THREAD_NAME_BENCHMARK);
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
|
||||
this.enabled = true;
|
||||
|
@ -13,8 +13,6 @@ import io.netty.channel.nio.NioEventLoopGroup;
|
||||
import io.netty.channel.socket.ServerSocketChannel;
|
||||
import io.netty.channel.socket.SocketChannel;
|
||||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.handler.traffic.GlobalChannelTrafficShapingHandler;
|
||||
import io.netty.handler.traffic.TrafficCounter;
|
||||
import io.netty.incubator.channel.uring.IOUring;
|
||||
import io.netty.incubator.channel.uring.IOUringEventLoopGroup;
|
||||
import io.netty.incubator.channel.uring.IOUringServerSocketChannel;
|
||||
@ -22,7 +20,6 @@ import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.network.PacketProcessor;
|
||||
import net.minestom.server.network.netty.channel.ClientChannel;
|
||||
import net.minestom.server.network.netty.codec.*;
|
||||
import net.minestom.server.ping.ResponseDataConsumer;
|
||||
import net.minestom.server.utils.validate.Check;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@ -30,22 +27,15 @@ import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
|
||||
public final class NettyServer {
|
||||
|
||||
public static final Logger LOGGER = LoggerFactory.getLogger(NettyServer.class);
|
||||
public static final int BUFFER_SIZE = Integer.getInteger("minestom.channel-buffer-size", 65535);
|
||||
|
||||
private static final WriteBufferWaterMark SERVER_WRITE_MARK = new WriteBufferWaterMark(1 << 20,
|
||||
1 << 21);
|
||||
|
||||
private static final long DEFAULT_COMPRESSED_CHANNEL_WRITE_LIMIT = 600_000L;
|
||||
private static final long DEFAULT_COMPRESSED_CHANNEL_READ_LIMIT = 100_000L;
|
||||
|
||||
private static final long DEFAULT_UNCOMPRESSED_CHANNEL_WRITE_LIMIT = 15_000_000L;
|
||||
private static final long DEFAULT_UNCOMPRESSED_CHANNEL_READ_LIMIT = 1_000_000L;
|
||||
|
||||
public static final String TRAFFIC_LIMITER_HANDLER_NAME = "traffic-limiter"; // Read/write
|
||||
public static final String LEGACY_PING_HANDLER_NAME = "legacy-ping"; // Read
|
||||
|
||||
public static final String ENCRYPT_HANDLER_NAME = "encrypt"; // Write
|
||||
@ -63,7 +53,6 @@ public final class NettyServer {
|
||||
private boolean initialized = false;
|
||||
|
||||
private final PacketProcessor packetProcessor;
|
||||
private final GlobalChannelTrafficShapingHandler globalTrafficHandler;
|
||||
|
||||
private EventLoopGroup boss, worker;
|
||||
private ServerBootstrap bootstrap;
|
||||
@ -73,27 +62,14 @@ public final class NettyServer {
|
||||
private String address;
|
||||
private int port;
|
||||
|
||||
/**
|
||||
* Scheduler used by {@code globalTrafficHandler}.
|
||||
*/
|
||||
private final ScheduledExecutorService trafficScheduler = Executors.newScheduledThreadPool(1);
|
||||
|
||||
public NettyServer(@NotNull PacketProcessor packetProcessor) {
|
||||
this.packetProcessor = packetProcessor;
|
||||
|
||||
this.globalTrafficHandler = new GlobalChannelTrafficShapingHandler(trafficScheduler, 1000) {
|
||||
@Override
|
||||
protected void doAccounting(TrafficCounter counter) {
|
||||
// TODO proper monitoring API
|
||||
//System.out.println("data " + counter.getRealWriteThroughput() / 1e6);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Inits the server by choosing which transport layer to use, number of threads, pipeline order, etc...
|
||||
* <p>
|
||||
* Called just before {@link #start(String, int)} in {@link MinecraftServer#start(String, int, ResponseDataConsumer)}.
|
||||
* Called just before {@link #start(String, int)}.
|
||||
*/
|
||||
public void init() {
|
||||
Check.stateCondition(initialized, "Netty server has already been initialized!");
|
||||
@ -146,14 +122,11 @@ public final class NettyServer {
|
||||
ChannelConfig config = ch.config();
|
||||
config.setOption(ChannelOption.TCP_NODELAY, true);
|
||||
config.setOption(ChannelOption.SO_KEEPALIVE, true);
|
||||
config.setOption(ChannelOption.SO_SNDBUF, 262_144);
|
||||
config.setOption(ChannelOption.SO_SNDBUF, BUFFER_SIZE);
|
||||
config.setAllocator(ByteBufAllocator.DEFAULT);
|
||||
|
||||
ChannelPipeline pipeline = ch.pipeline();
|
||||
|
||||
// TODO enable when properly implemented (dynamic limit based on the number of clients)
|
||||
//pipeline.addLast(TRAFFIC_LIMITER_HANDLER_NAME, globalTrafficHandler);
|
||||
|
||||
// First check should verify if the packet is a legacy ping (from 1.6 version and earlier)
|
||||
// Removed from the pipeline later in LegacyPingHandler if unnecessary (>1.6)
|
||||
pipeline.addLast(LEGACY_PING_HANDLER_NAME, new LegacyPingHandler());
|
||||
@ -185,18 +158,6 @@ public final class NettyServer {
|
||||
this.address = address;
|
||||
this.port = port;
|
||||
|
||||
// Setup traffic limiter
|
||||
{
|
||||
final boolean compression = MinecraftServer.getCompressionThreshold() != 0;
|
||||
if (compression) {
|
||||
this.globalTrafficHandler.setWriteChannelLimit(DEFAULT_COMPRESSED_CHANNEL_WRITE_LIMIT);
|
||||
this.globalTrafficHandler.setReadChannelLimit(DEFAULT_COMPRESSED_CHANNEL_READ_LIMIT);
|
||||
} else {
|
||||
this.globalTrafficHandler.setWriteChannelLimit(DEFAULT_UNCOMPRESSED_CHANNEL_WRITE_LIMIT);
|
||||
this.globalTrafficHandler.setReadChannelLimit(DEFAULT_UNCOMPRESSED_CHANNEL_READ_LIMIT);
|
||||
}
|
||||
}
|
||||
|
||||
// Bind address
|
||||
try {
|
||||
ChannelFuture cf = bootstrap.bind(new InetSocketAddress(address, port)).sync();
|
||||
@ -231,25 +192,15 @@ public final class NettyServer {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the traffic handler, used to control channel and global bandwidth.
|
||||
* <p>
|
||||
* The object can be modified as specified by Netty documentation.
|
||||
*
|
||||
* @return the global traffic handler
|
||||
*/
|
||||
@NotNull
|
||||
public GlobalChannelTrafficShapingHandler getGlobalTrafficHandler() {
|
||||
return globalTrafficHandler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the server and the various services.
|
||||
* Stops the server.
|
||||
*/
|
||||
public void stop() {
|
||||
this.worker.shutdownGracefully();
|
||||
this.boss.shutdownGracefully();
|
||||
|
||||
this.trafficScheduler.shutdown();
|
||||
this.globalTrafficHandler.release();
|
||||
try {
|
||||
this.boss.shutdownGracefully().sync();
|
||||
this.worker.shutdownGracefully().sync();
|
||||
this.serverChannel.closeFuture().sync();
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package net.minestom.server.network.packet.server.play;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
|
||||
import it.unimi.dsi.fastutil.ints.IntOpenHashSet;
|
||||
import it.unimi.dsi.fastutil.ints.IntSet;
|
||||
@ -13,7 +14,6 @@ import net.minestom.server.instance.palette.Section;
|
||||
import net.minestom.server.network.packet.server.ServerPacket;
|
||||
import net.minestom.server.network.packet.server.ServerPacketIdentifier;
|
||||
import net.minestom.server.utils.BlockPosition;
|
||||
import net.minestom.server.utils.BufUtils;
|
||||
import net.minestom.server.utils.Utils;
|
||||
import net.minestom.server.utils.binary.BinaryReader;
|
||||
import net.minestom.server.utils.binary.BinaryWriter;
|
||||
@ -82,7 +82,7 @@ public class ChunkDataPacket implements ServerPacket, CacheablePacket {
|
||||
writer.writeBoolean(fullChunk);
|
||||
|
||||
int mask = 0;
|
||||
ByteBuf blocks = BufUtils.getBuffer(MAX_BUFFER_SIZE);
|
||||
ByteBuf blocks = Unpooled.buffer(MAX_BUFFER_SIZE);
|
||||
for (byte i = 0; i < CHUNK_SECTION_COUNT; i++) {
|
||||
if (fullChunk || (sections.length == CHUNK_SECTION_COUNT && sections[i] != 0)) {
|
||||
final Section section = paletteStorage.getSections()[i];
|
||||
|
@ -62,7 +62,7 @@ public class NettyPlayerConnection extends PlayerConnection {
|
||||
private PlayerSkin bungeeSkin;
|
||||
|
||||
private final Object tickBufferLock = new Object();
|
||||
private volatile ByteBuf tickBuffer = BufUtils.getBuffer(true);
|
||||
private volatile ByteBuf tickBuffer = BufUtils.direct();
|
||||
|
||||
public NettyPlayerConnection(@NotNull SocketChannel channel) {
|
||||
super();
|
||||
|
@ -22,26 +22,31 @@ public class MinestomTerminal {
|
||||
|
||||
@ApiStatus.Internal
|
||||
public static void start() {
|
||||
try {
|
||||
terminal = TerminalBuilder.terminal();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
LineReader reader = LineReaderBuilder.builder()
|
||||
.terminal(terminal)
|
||||
.build();
|
||||
running = true;
|
||||
while (running) {
|
||||
String command;
|
||||
final Thread thread = new Thread(null, () -> {
|
||||
try {
|
||||
command = reader.readLine(PROMPT);
|
||||
COMMAND_MANAGER.execute(COMMAND_MANAGER.getConsoleSender(), command);
|
||||
} catch (UserInterruptException e) {
|
||||
// Ignore
|
||||
} catch (EndOfFileException e) {
|
||||
return;
|
||||
terminal = TerminalBuilder.terminal();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
LineReader reader = LineReaderBuilder.builder()
|
||||
.terminal(terminal)
|
||||
.build();
|
||||
running = true;
|
||||
|
||||
while (running) {
|
||||
String command;
|
||||
try {
|
||||
command = reader.readLine(PROMPT);
|
||||
COMMAND_MANAGER.execute(COMMAND_MANAGER.getConsoleSender(), command);
|
||||
} catch (UserInterruptException e) {
|
||||
// Ignore
|
||||
} catch (EndOfFileException e) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}, "Jline");
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
@ApiStatus.Internal
|
||||
|
@ -4,8 +4,6 @@ import net.minestom.server.MinecraftServer;
|
||||
import net.minestom.server.utils.validate.Check;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Queue;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.concurrent.locks.LockSupport;
|
||||
@ -50,10 +48,7 @@ public class TickThread extends Thread {
|
||||
private volatile boolean stop;
|
||||
private TickThread tickThread;
|
||||
|
||||
private volatile boolean inTick;
|
||||
private final AtomicReference<CountDownLatch> countDownLatch = new AtomicReference<>();
|
||||
|
||||
private final Queue<Runnable> queue = new ConcurrentLinkedQueue<>();
|
||||
private final AtomicReference<TickContext> tickContext = new AtomicReference<>();
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
@ -62,42 +57,38 @@ public class TickThread extends Thread {
|
||||
LockSupport.park(tickThread);
|
||||
if (stop)
|
||||
break;
|
||||
CountDownLatch localCountDownLatch = this.countDownLatch.get();
|
||||
|
||||
// The latch is necessary to control the tick rates
|
||||
if (localCountDownLatch == null) {
|
||||
TickContext localContext = this.tickContext.get();
|
||||
// The context is necessary to control the tick rates
|
||||
if (localContext == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
this.inTick = true;
|
||||
// Execute tick
|
||||
localContext.runnable.run();
|
||||
|
||||
// Execute all pending runnable
|
||||
Runnable runnable;
|
||||
while ((runnable = queue.poll()) != null) {
|
||||
runnable.run();
|
||||
}
|
||||
|
||||
localCountDownLatch.countDown();
|
||||
this.countDownLatch.compareAndSet(localCountDownLatch, null);
|
||||
|
||||
// Wait for the next notify (game tick)
|
||||
this.inTick = false;
|
||||
localContext.countDownLatch.countDown();
|
||||
this.tickContext.compareAndSet(localContext, null);
|
||||
}
|
||||
}
|
||||
|
||||
public void startTick(@NotNull CountDownLatch countDownLatch, @NotNull Runnable runnable) {
|
||||
this.countDownLatch.set(countDownLatch);
|
||||
this.queue.add(runnable);
|
||||
protected void startTick(@NotNull CountDownLatch countDownLatch, @NotNull Runnable runnable) {
|
||||
this.tickContext.set(new TickContext(countDownLatch, runnable));
|
||||
LockSupport.unpark(tickThread);
|
||||
}
|
||||
|
||||
public boolean isInTick() {
|
||||
return inTick;
|
||||
}
|
||||
|
||||
private void setLinkedThread(TickThread tickThread) {
|
||||
this.tickThread = tickThread;
|
||||
}
|
||||
}
|
||||
|
||||
private static class TickContext {
|
||||
private final CountDownLatch countDownLatch;
|
||||
private final Runnable runnable;
|
||||
|
||||
private TickContext(@NotNull CountDownLatch countDownLatch, @NotNull Runnable runnable) {
|
||||
this.countDownLatch = countDownLatch;
|
||||
this.runnable = runnable;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,28 +7,7 @@ public class BufUtils {
|
||||
|
||||
private static final PooledByteBufAllocator alloc = PooledByteBufAllocator.DEFAULT;
|
||||
|
||||
public static ByteBuf getBuffer() {
|
||||
return alloc.heapBuffer();
|
||||
public static ByteBuf direct() {
|
||||
return alloc.ioBuffer();
|
||||
}
|
||||
|
||||
public static ByteBuf getBuffer(boolean io) {
|
||||
return io ? alloc.ioBuffer() : alloc.heapBuffer();
|
||||
}
|
||||
|
||||
public static ByteBuf getBuffer(int initialCapacity) {
|
||||
return alloc.heapBuffer(initialCapacity);
|
||||
}
|
||||
|
||||
public static ByteBuf getBuffer(boolean io, int initialCapacity) {
|
||||
return io ? alloc.ioBuffer(initialCapacity) : alloc.heapBuffer(initialCapacity);
|
||||
}
|
||||
|
||||
public static ByteBuf getBuffer(int initialCapacity, int maxCapacity) {
|
||||
return alloc.heapBuffer(initialCapacity, maxCapacity);
|
||||
}
|
||||
|
||||
public static ByteBuf getBuffer(boolean io, int initialCapacity, int maxCapacity) {
|
||||
return io ? alloc.ioBuffer(initialCapacity, maxCapacity) : alloc.heapBuffer(initialCapacity, maxCapacity);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package net.minestom.server.utils;
|
||||
import com.velocitypowered.natives.compression.VelocityCompressor;
|
||||
import com.velocitypowered.natives.util.Natives;
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import net.kyori.adventure.audience.Audience;
|
||||
import net.kyori.adventure.audience.ForwardingAudience;
|
||||
import net.minestom.server.MinecraftServer;
|
||||
@ -21,7 +20,6 @@ import net.minestom.server.utils.callback.validator.PlayerValidator;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.Collection;
|
||||
import java.util.zip.DataFormatException;
|
||||
|
||||
@ -35,7 +33,6 @@ public final class PacketUtils {
|
||||
private static final ThreadLocal<VelocityCompressor> COMPRESSOR = ThreadLocal.withInitial(() -> Natives.compress.get().create(4));
|
||||
|
||||
private PacketUtils() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -54,7 +51,7 @@ public final class PacketUtils {
|
||||
* </ol>
|
||||
*
|
||||
* @param audience the audience
|
||||
* @param packet the packet
|
||||
* @param packet the packet
|
||||
*/
|
||||
@SuppressWarnings("OverrideOnly") // we need to access the audiences inside ForwardingAudience
|
||||
public static void sendPacket(@NotNull Audience audience, @NotNull ServerPacket packet) {
|
||||
@ -96,7 +93,7 @@ public final class PacketUtils {
|
||||
// Send grouped packet...
|
||||
final boolean success = PACKET_LISTENER_MANAGER.processServerPacket(packet, players);
|
||||
if (success) {
|
||||
final ByteBuf finalBuffer = createFramedPacket(packet, true);
|
||||
final ByteBuf finalBuffer = createFramedPacket(packet);
|
||||
final FramedPacket framedPacket = new FramedPacket(finalBuffer);
|
||||
|
||||
// Send packet to all players
|
||||
@ -282,14 +279,10 @@ public final class PacketUtils {
|
||||
* <p>
|
||||
* Can be used if you want to store a raw buffer and send it later without the additional writing cost.
|
||||
* Compression is applied if {@link MinecraftServer#getCompressionThreshold()} is greater than 0.
|
||||
*
|
||||
* @param serverPacket the server packet to write
|
||||
*/
|
||||
@NotNull
|
||||
public static ByteBuf createFramedPacket(@NotNull ServerPacket serverPacket, boolean directBuffer) {
|
||||
ByteBuf packetBuf = directBuffer ? BufUtils.getBuffer(true) : Unpooled.buffer();
|
||||
public static @NotNull ByteBuf createFramedPacket(@NotNull ServerPacket serverPacket) {
|
||||
ByteBuf packetBuf = BufUtils.direct();
|
||||
writeFramedPacket(packetBuf, serverPacket);
|
||||
return packetBuf;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -25,42 +25,34 @@ public final class Utils {
|
||||
? 4 : 5;
|
||||
}
|
||||
|
||||
public static void writeVarIntBuf(ByteBuf buffer, int value) {
|
||||
do {
|
||||
byte temp = (byte) (value & 0b01111111);
|
||||
value >>>= 7;
|
||||
if (value != 0) {
|
||||
temp |= 0b10000000;
|
||||
public static void writeVarIntBuf(ByteBuf buf, int value) {
|
||||
// Took from velocity
|
||||
if ((value & (0xFFFFFFFF << 7)) == 0) {
|
||||
buf.writeByte(value);
|
||||
} else if ((value & (0xFFFFFFFF << 14)) == 0) {
|
||||
int w = (value & 0x7F | 0x80) << 8 | (value >>> 7);
|
||||
buf.writeShort(w);
|
||||
} else if ((value & (0xFFFFFFFF << 21)) == 0) {
|
||||
int w = (value & 0x7F | 0x80) << 16 | ((value >>> 7) & 0x7F | 0x80) << 8 | (value >>> 14);
|
||||
buf.writeMedium(w);
|
||||
} else {
|
||||
// Naive loop
|
||||
while (true) {
|
||||
if ((value & 0xFFFFFF80) == 0) {
|
||||
buf.writeByte(value);
|
||||
return;
|
||||
}
|
||||
buf.writeByte(value & 0x7F | 0x80);
|
||||
value >>>= 7;
|
||||
}
|
||||
buffer.writeByte(temp);
|
||||
} while (value != 0);
|
||||
}
|
||||
|
||||
public static void writeVarInt(BinaryWriter writer, int value) {
|
||||
do {
|
||||
byte temp = (byte) (value & 0b01111111);
|
||||
value >>>= 7;
|
||||
if (value != 0) {
|
||||
temp |= 0b10000000;
|
||||
}
|
||||
writer.writeByte(temp);
|
||||
} while (value != 0);
|
||||
}
|
||||
}
|
||||
|
||||
public static void overrideVarIntHeader(@NotNull ByteBuf buffer, int startIndex, int value) {
|
||||
final int indexCache = buffer.writerIndex();
|
||||
buffer.writerIndex(startIndex);
|
||||
|
||||
for (int i = 0; i < VARINT_HEADER_SIZE; i++) {
|
||||
byte temp = (byte) (value & 0b01111111);
|
||||
value >>>= 7;
|
||||
if (value != 0 || i != VARINT_HEADER_SIZE - 1) {
|
||||
temp |= 0b10000000;
|
||||
}
|
||||
|
||||
buffer.writeByte(temp);
|
||||
}
|
||||
|
||||
final int w = (value & 0x7F | 0x80) << 16 | ((value >>> 7) & 0x7F | 0x80) << 8 | (value >>> 14);
|
||||
buffer.writeMedium(w);
|
||||
buffer.writerIndex(indexCache);
|
||||
}
|
||||
|
||||
|
@ -152,7 +152,7 @@ public class BinaryWriter extends OutputStream {
|
||||
* @param i the int to write
|
||||
*/
|
||||
public void writeVarInt(int i) {
|
||||
Utils.writeVarInt(this, i);
|
||||
Utils.writeVarIntBuf(buffer, i);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -63,7 +63,7 @@ public interface CacheablePacket {
|
||||
|
||||
if (shouldUpdate) {
|
||||
// Buffer freed by guava cache #removalListener
|
||||
final ByteBuf buffer = PacketUtils.createFramedPacket(serverPacket, true);
|
||||
final ByteBuf buffer = PacketUtils.createFramedPacket(serverPacket);
|
||||
timedBuffer = new TimedBuffer(buffer, timestamp);
|
||||
temporaryCache.cache(identifier, timedBuffer);
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ public class TestCommand extends Command {
|
||||
super("testcmd");
|
||||
setDefaultExecutor(this::usage);
|
||||
|
||||
addSyntax((sender, context) -> System.out.println("executed"), "test get");
|
||||
addSyntax((sender, context) -> System.out.println("executed"), "test Get integer<number>");
|
||||
}
|
||||
|
||||
private void usage(CommandSender sender, CommandContext context) {
|
||||
|
Loading…
Reference in New Issue
Block a user