diff --git a/build.gradle b/build.gradle index e6497e2d6..b8504cb17 100644 --- a/build.gradle +++ b/build.gradle @@ -20,4 +20,6 @@ dependencies { implementation 'com.github.luben:zstd-jni:1.4.3-1' implementation 'com.esotericsoftware:reflectasm:1.11.9' implementation 'com.github.LynnOwens:starlite:9971b899f7' + //implementation 'com.github.jhg023:SimpleNet:1.4.14' + } diff --git a/src/main/java/fr/themode/minestom/Main.java b/src/main/java/fr/themode/minestom/Main.java index 81a9993ed..10fdabbe7 100644 --- a/src/main/java/fr/themode/minestom/Main.java +++ b/src/main/java/fr/themode/minestom/Main.java @@ -5,6 +5,7 @@ import fr.adamaq01.ozao.net.server.Connection; import fr.adamaq01.ozao.net.server.Server; import fr.adamaq01.ozao.net.server.ServerHandler; import fr.adamaq01.ozao.net.server.backend.tcp.TCPServer; +import fr.themode.minestom.data.DataManager; import fr.themode.minestom.entity.EntityManager; import fr.themode.minestom.entity.Player; import fr.themode.minestom.instance.BlockManager; @@ -42,6 +43,7 @@ public class Main { private static InstanceManager instanceManager; private static BlockManager blockManager; private static EntityManager entityManager; + private static DataManager dataManager; public static void main(String[] args) { connectionManager = new ConnectionManager(); @@ -51,6 +53,7 @@ public class Main { instanceManager = new InstanceManager(); blockManager = new BlockManager(); entityManager = new EntityManager(); + dataManager = new DataManager(); blockManager.registerBlock(StoneBlock::new); @@ -142,6 +145,10 @@ public class Main { return entityManager; } + public static DataManager getDataManager() { + return dataManager; + } + public static ConnectionManager getConnectionManager() { return connectionManager; } diff --git a/src/main/java/fr/themode/minestom/entity/Entity.java b/src/main/java/fr/themode/minestom/entity/Entity.java index 1f9f86252..f557b3c8c 100644 --- a/src/main/java/fr/themode/minestom/entity/Entity.java +++ b/src/main/java/fr/themode/minestom/entity/Entity.java @@ -149,7 +149,7 @@ public abstract class Entity implements Viewable, DataContainer { @Override public void addViewer(Player player) { this.viewers.add(player); - player.viewableEntity.add(this); + player.viewableEntities.add(this); PlayerConnection playerConnection = player.getPlayerConnection(); playerConnection.sendPacket(getVelocityPacket()); playerConnection.sendPacket(getPassengersPacket()); @@ -165,7 +165,7 @@ public abstract class Entity implements Viewable, DataContainer { destroyEntitiesPacket.entityIds = new int[]{getEntityId()}; player.getPlayerConnection().sendPacket(destroyEntitiesPacket); } - player.viewableEntity.remove(this); + player.viewableEntities.remove(this); } @Override diff --git a/src/main/java/fr/themode/minestom/entity/EntityManager.java b/src/main/java/fr/themode/minestom/entity/EntityManager.java index c8a38d080..5faba8ca4 100644 --- a/src/main/java/fr/themode/minestom/entity/EntityManager.java +++ b/src/main/java/fr/themode/minestom/entity/EntityManager.java @@ -2,19 +2,14 @@ package fr.themode.minestom.entity; import fr.themode.minestom.Main; import fr.themode.minestom.event.PlayerLoginEvent; -import fr.themode.minestom.event.PlayerSpawnEvent; import fr.themode.minestom.instance.Chunk; import fr.themode.minestom.instance.Instance; import fr.themode.minestom.instance.InstanceManager; -import fr.themode.minestom.utils.ChunkUtils; -import fr.themode.minestom.utils.Position; import java.util.Set; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Consumer; public class EntityManager { @@ -41,35 +36,8 @@ public class EntityManager { PlayerLoginEvent loginEvent = new PlayerLoginEvent(); playerCache.callEvent(PlayerLoginEvent.class, loginEvent); Instance spawningInstance = loginEvent.getSpawningInstance() == null ? instanceManager.createInstanceContainer() : loginEvent.getSpawningInstance(); - Position position = playerCache.getPosition(); - long[] visibleChunks = ChunkUtils.getChunksInRange(position, Main.CHUNK_VIEW_DISTANCE); - int length = visibleChunks.length; - - AtomicInteger counter = new AtomicInteger(0); - - for (int i = 0; i < length; i++) { - int[] chunkPos = ChunkUtils.getChunkCoord(visibleChunks[i]); - int chunkX = chunkPos[0]; - int chunkZ = chunkPos[1]; - Consumer callback = (chunk) -> { - boolean isLast = counter.get() == length - 1; - if (isLast) { - // This is the last chunk to be loaded, spawn player - playerCache.spawned = true; - playerCache.setInstance(spawningInstance); - PlayerSpawnEvent spawnEvent = new PlayerSpawnEvent(); - playerCache.callEvent(PlayerSpawnEvent.class, spawnEvent); - playerCache.updateViewPosition(chunk); - } else { - // Increment the counter of current loaded chunks - counter.incrementAndGet(); - } - }; - - // WARNING: if auto load is disabled and no chunks are loaded beforehand, player will be stuck. - spawningInstance.loadChunk(chunkX, chunkZ, callback); - } + playerCache.setInstance(spawningInstance); }); } @@ -77,7 +45,6 @@ public class EntityManager { // TODO optimize for when there are too many entities on one chunk private void testTick2(Instance instance) { - for (Chunk chunk : instance.getChunks()) { Set entities = instance.getChunkEntities(chunk); diff --git a/src/main/java/fr/themode/minestom/entity/Player.java b/src/main/java/fr/themode/minestom/entity/Player.java index cce4914a3..a57344764 100644 --- a/src/main/java/fr/themode/minestom/entity/Player.java +++ b/src/main/java/fr/themode/minestom/entity/Player.java @@ -29,6 +29,8 @@ import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; public class Player extends LivingEntity { @@ -62,7 +64,8 @@ public class Player extends LivingEntity { System.out.println("Time to load all chunks: " + (System.currentTimeMillis() - time) + " ms"); } - protected Set viewableEntity = new CopyOnWriteArraySet<>(); + protected Set viewableEntities = new CopyOnWriteArraySet<>(); + protected Set viewableChunks = new CopyOnWriteArraySet<>(); private PlayerSettings settings; private float exp; @@ -87,8 +90,6 @@ public class Player extends LivingEntity { private boolean jump; private boolean unmount; - protected boolean spawned; - public Player(UUID uuid, String username, PlayerConnection playerConnection) { super(100); this.uuid = uuid; @@ -247,6 +248,9 @@ public class Player extends LivingEntity { } } + // Tick event + callEvent(PlayerTickEvent.class, new PlayerTickEvent()); + // Multiplayer sync Position position = getPosition(); @@ -324,7 +328,7 @@ public class Player extends LivingEntity { clearBossBars(); if (getOpenInventory() != null) getOpenInventory().removeViewer(this); - this.viewableEntity.forEach(entity -> entity.removeViewer(this)); + this.viewableEntities.forEach(entity -> entity.removeViewer(this)); super.remove(); } @@ -367,10 +371,46 @@ public class Player extends LivingEntity { @Override public void setInstance(Instance instance) { - if (!spawned) - throw new IllegalStateException("Player#setInstance is only available during and after PlayerSpawnEvent"); + if (instance == null) + throw new IllegalArgumentException("instance cannot be null!"); + if (this.instance == instance) + throw new IllegalArgumentException("Instance should be different than the current one"); - super.setInstance(instance); + for (Chunk viewableChunk : viewableChunks) { + viewableChunk.removeViewer(this); + } + viewableChunks.clear(); + + long[] visibleChunks = ChunkUtils.getChunksInRange(position, getChunkRange()); + int length = visibleChunks.length; + + AtomicInteger counter = new AtomicInteger(0); + + for (int i = 0; i < length; i++) { + int[] chunkPos = ChunkUtils.getChunkCoord(visibleChunks[i]); + int chunkX = chunkPos[0]; + int chunkZ = chunkPos[1]; + Consumer callback = (chunk) -> { + if (chunk != null) { + viewableChunks.add(chunk); + chunk.addViewer(this); + } + boolean isLast = counter.get() == length - 1; + if (isLast) { + // This is the last chunk to be loaded, spawn player + super.setInstance(instance); + PlayerSpawnEvent spawnEvent = new PlayerSpawnEvent(instance); + callEvent(PlayerSpawnEvent.class, spawnEvent); + updateViewPosition(chunk); + } else { + // Increment the counter of current loaded chunks + counter.incrementAndGet(); + } + }; + + // WARNING: if auto load is disabled and no chunks are loaded beforehand, player will be stuck. + instance.loadOptionalChunk(chunkX, chunkZ, callback); + } } @Override @@ -516,6 +556,10 @@ public class Player extends LivingEntity { unloadChunkPacket.chunkX = chunkPos[0]; unloadChunkPacket.chunkZ = chunkPos[1]; playerConnection.sendPacket(unloadChunkPacket); + + Chunk chunk = instance.getChunk(chunkPos[0], chunkPos[1]); + if (chunk != null) + chunk.removeViewer(this); } updateViewPosition(newChunk); @@ -529,6 +573,8 @@ public class Player extends LivingEntity { if (chunk == null) { return; // Cannot load chunk (auto load is not enabled) } + this.viewableChunks.add(chunk); + chunk.addViewer(this); instance.sendChunk(this, chunk); if (isFar && isLast) { updatePlayerPosition(); @@ -678,6 +724,10 @@ public class Player extends LivingEntity { inventory.update(); } + public Set getViewableChunks() { + return Collections.unmodifiableSet(viewableChunks); + } + public void clearBossBars() { this.bossBars.forEach(bossBar -> bossBar.removeViewer(this)); } @@ -767,6 +817,12 @@ public class Player extends LivingEntity { this.unmount = unmount; } + public int getChunkRange() { + int serverRange = Main.CHUNK_VIEW_DISTANCE; + int playerRange = getSettings().viewDistance; + return serverRange;//playerRange < serverRange ? playerRange : serverRange; + } + public long getLastKeepAlive() { return lastKeepAlive; } diff --git a/src/main/java/fr/themode/minestom/event/PlayerSpawnEvent.java b/src/main/java/fr/themode/minestom/event/PlayerSpawnEvent.java index d056e8f4f..e9fc84504 100644 --- a/src/main/java/fr/themode/minestom/event/PlayerSpawnEvent.java +++ b/src/main/java/fr/themode/minestom/event/PlayerSpawnEvent.java @@ -1,5 +1,16 @@ package fr.themode.minestom.event; +import fr.themode.minestom.instance.Instance; + public class PlayerSpawnEvent extends Event { + private Instance spawnInstance; + + public PlayerSpawnEvent(Instance spawnInstance) { + this.spawnInstance = spawnInstance; + } + + public Instance getSpawnInstance() { + return spawnInstance; + } } diff --git a/src/main/java/fr/themode/minestom/event/PlayerTickEvent.java b/src/main/java/fr/themode/minestom/event/PlayerTickEvent.java new file mode 100644 index 000000000..436de4b09 --- /dev/null +++ b/src/main/java/fr/themode/minestom/event/PlayerTickEvent.java @@ -0,0 +1,4 @@ +package fr.themode.minestom.event; + +public class PlayerTickEvent extends Event { +} diff --git a/src/main/java/fr/themode/minestom/instance/Chunk.java b/src/main/java/fr/themode/minestom/instance/Chunk.java index 66ee9fcd1..a35b5772f 100644 --- a/src/main/java/fr/themode/minestom/instance/Chunk.java +++ b/src/main/java/fr/themode/minestom/instance/Chunk.java @@ -3,6 +3,8 @@ package fr.themode.minestom.instance; import fr.adamaq01.ozao.net.Buffer; import fr.adamaq01.ozao.net.packet.Packet; import fr.themode.minestom.Main; +import fr.themode.minestom.Viewable; +import fr.themode.minestom.entity.Player; import fr.themode.minestom.net.packet.server.play.ChunkDataPacket; import fr.themode.minestom.utils.PacketUtils; import fr.themode.minestom.utils.SerializerUtils; @@ -10,10 +12,11 @@ import fr.themode.minestom.utils.SerializerUtils; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; +import java.util.Collections; import java.util.Set; import java.util.concurrent.CopyOnWriteArraySet; -public class Chunk { +public class Chunk implements Viewable { public static final int CHUNK_SIZE_X = 16; public static final int CHUNK_SIZE_Y = 256; @@ -29,6 +32,7 @@ public class Chunk { private Set blockEntities = new CopyOnWriteArraySet<>(); // Cache + private Set viewers = new CopyOnWriteArraySet<>(); private Buffer fullDataPacket; public Chunk(Biome biome, int chunkX, int chunkZ) { @@ -155,4 +159,21 @@ public class Chunk { public String toString() { return "Chunk[" + chunkX + ":" + chunkZ + "]"; } + + // UNSAFE + @Override + public void addViewer(Player player) { + this.viewers.add(player); + } + + // UNSAFE + @Override + public void removeViewer(Player player) { + this.viewers.remove(player); + } + + @Override + public Set getViewers() { + return Collections.unmodifiableSet(viewers); + } } diff --git a/src/main/java/fr/themode/minestom/instance/Instance.java b/src/main/java/fr/themode/minestom/instance/Instance.java index 47f30d32b..50ba0d2dc 100644 --- a/src/main/java/fr/themode/minestom/instance/Instance.java +++ b/src/main/java/fr/themode/minestom/instance/Instance.java @@ -54,6 +54,8 @@ public abstract class Instance implements BlockModifier { public abstract void sendChunkUpdate(Player player, Chunk chunk); + public abstract void sendChunkSectionUpdate(Chunk chunk, int section, Player player); + protected abstract void retrieveChunk(int chunkX, int chunkZ, Consumer callback); public abstract void createChunk(int chunkX, int chunkZ, Consumer callback); diff --git a/src/main/java/fr/themode/minestom/instance/InstanceContainer.java b/src/main/java/fr/themode/minestom/instance/InstanceContainer.java index 3c2e6dd85..55f98e4db 100644 --- a/src/main/java/fr/themode/minestom/instance/InstanceContainer.java +++ b/src/main/java/fr/themode/minestom/instance/InstanceContainer.java @@ -4,6 +4,8 @@ import fr.adamaq01.ozao.net.Buffer; import fr.themode.minestom.entity.Player; import fr.themode.minestom.event.PlayerBlockBreakEvent; import fr.themode.minestom.net.PacketWriterUtils; +import fr.themode.minestom.net.packet.server.play.BlockChangePacket; +import fr.themode.minestom.net.packet.server.play.ChunkDataPacket; import fr.themode.minestom.net.packet.server.play.ParticlePacket; import fr.themode.minestom.utils.BlockPosition; import fr.themode.minestom.utils.ChunkUtils; @@ -37,11 +39,11 @@ public class InstanceContainer extends Instance { public synchronized void setBlock(int x, int y, int z, short blockId) { Chunk chunk = getChunkAt(x, z); synchronized (chunk) { - chunk.setBlock((byte) (x % 16), (byte) y, (byte) (z % 16), blockId); - PacketWriterUtils.writeCallbackPacket(chunk.getFreshPartialDataPacket(), buffer -> { - chunk.setFullDataPacket(buffer); - sendChunkUpdate(chunk); - }); + byte chunkX = (byte) (x % 16); + byte chunkY = (byte) y; + byte chunkZ = (byte) (z % 16); + chunk.setBlock(chunkX, chunkY, chunkZ, blockId); + sendBlockChange(chunk, x, y, z, blockId); } } @@ -49,21 +51,21 @@ public class InstanceContainer extends Instance { public synchronized void setBlock(int x, int y, int z, String blockId) { Chunk chunk = getChunkAt(x, z); synchronized (chunk) { - chunk.setCustomBlock((byte) (x % 16), (byte) y, (byte) (z % 16), blockId); - PacketWriterUtils.writeCallbackPacket(chunk.getFreshPartialDataPacket(), buffer -> { - chunk.setFullDataPacket(buffer); - sendChunkUpdate(chunk); - }); + byte chunkX = (byte) (x % 16); + byte chunkY = (byte) y; + byte chunkZ = (byte) (z % 16); + chunk.setCustomBlock(chunkX, chunkY, chunkZ, blockId); + short id = chunk.getBlockId(chunkX, chunkY, chunkZ); + sendBlockChange(chunk, x, y, z, id); } } - // TODO deplace @Override public void breakBlock(Player player, BlockPosition blockPosition) { Chunk chunk = getChunkAt(blockPosition); short blockId = chunk.getBlockId((byte) (blockPosition.getX() % 16), (byte) blockPosition.getY(), (byte) (blockPosition.getZ() % 16)); if (blockId == 0) { - sendChunkUpdate(player, chunk); + sendChunkSectionUpdate(chunk, ChunkUtils.getSectionAt(blockPosition.getY()), player); return; } @@ -87,10 +89,9 @@ public class InstanceContainer extends Instance { particlePacket.particleData = 0.3f; particlePacket.particleCount = 100; particlePacket.blockId = blockId; - player.getPlayerConnection().sendPacket(particlePacket); - player.sendPacketToViewers(particlePacket); + chunk.sendPacketToViewers(particlePacket); } else { - sendChunkUpdate(player, chunk); + sendChunkSectionUpdate(chunk, ChunkUtils.getSectionAt(blockPosition.getY()), player); } } @@ -156,6 +157,17 @@ public class InstanceContainer extends Instance { chunkData.getData().resetReaderIndex(); } + @Override + public void sendChunkSectionUpdate(Chunk chunk, int section, Player player) { + ChunkDataPacket chunkDataPacket = new ChunkDataPacket(); + chunkDataPacket.fullChunk = false; + chunkDataPacket.chunk = chunk; + int[] sections = new int[16]; + sections[section] = 1; + chunkDataPacket.sections = sections; + PacketWriterUtils.writeAndSend(player, chunkDataPacket); + } + @Override protected void retrieveChunk(int chunkX, int chunkZ, Consumer callback) { if (folder != null) { @@ -184,9 +196,12 @@ public class InstanceContainer extends Instance { } public void sendChunkUpdate(Chunk chunk) { - + Set chunkViewers = chunk.getViewers(); + if (!chunkViewers.isEmpty()) { + sendChunkUpdate(chunkViewers, chunk); + } // Update for players in this instance - if (!getPlayers().isEmpty()) + /*if (!getPlayers().isEmpty()) sendChunkUpdate(getPlayers(), chunk); // Update for shared instances @@ -195,7 +210,7 @@ public class InstanceContainer extends Instance { Set instancePlayers = sharedInstance.getPlayers(); if (!instancePlayers.isEmpty()) sendChunkUpdate(instancePlayers, chunk); - }); + });*/ } @Override @@ -209,7 +224,7 @@ public class InstanceContainer extends Instance { public void sendChunk(Player player, Chunk chunk) { /*Buffer data = chunk.getFullDataPacket(); if(data == null) { - PacketWriter.writeCallbackPacket(chunk.getFreshFullDataPacket(), buffer -> { + PacketWriterUtils.writeCallbackPacket(chunk.getFreshFullDataPacket(), buffer -> { chunk.setFullDataPacket(buffer); sendChunkUpdate(player, chunk); }); @@ -256,4 +271,11 @@ public class InstanceContainer extends Instance { this.folder = folder; } + private void sendBlockChange(Chunk chunk, int x, int y, int z, short blockId) { + BlockChangePacket blockChangePacket = new BlockChangePacket(); + blockChangePacket.blockPosition = new BlockPosition(x, y, z); + blockChangePacket.blockId = blockId; + chunk.sendPacketToViewers(blockChangePacket); + } + } diff --git a/src/main/java/fr/themode/minestom/instance/SharedInstance.java b/src/main/java/fr/themode/minestom/instance/SharedInstance.java index 76a4f7ff2..f147f25a3 100644 --- a/src/main/java/fr/themode/minestom/instance/SharedInstance.java +++ b/src/main/java/fr/themode/minestom/instance/SharedInstance.java @@ -80,6 +80,11 @@ public class SharedInstance extends Instance { instanceContainer.sendChunkUpdate(player, chunk); } + @Override + public void sendChunkSectionUpdate(Chunk chunk, int section, Player player) { + instanceContainer.sendChunkSectionUpdate(chunk, section, player); + } + @Override public void retrieveChunk(int chunkX, int chunkZ, Consumer callback) { instanceContainer.retrieveChunk(chunkX, chunkZ, callback); diff --git a/src/main/java/fr/themode/minestom/listener/BlockPlacementListener.java b/src/main/java/fr/themode/minestom/listener/BlockPlacementListener.java index 6cc329bcb..282d9b88b 100644 --- a/src/main/java/fr/themode/minestom/listener/BlockPlacementListener.java +++ b/src/main/java/fr/themode/minestom/listener/BlockPlacementListener.java @@ -9,6 +9,7 @@ import fr.themode.minestom.item.ItemStack; import fr.themode.minestom.net.packet.client.play.ClientPlayerBlockPlacementPacket; import fr.themode.minestom.net.packet.client.play.ClientPlayerDiggingPacket; import fr.themode.minestom.utils.BlockPosition; +import fr.themode.minestom.utils.ChunkUtils; public class BlockPlacementListener { @@ -23,15 +24,17 @@ public class BlockPlacementListener { return; ItemStack usedItem = hand == Player.Hand.MAIN ? playerInventory.getItemInMainHand() : playerInventory.getItemInOffHand(); - if (!usedItem.getMaterial().isBlock()) + if (!usedItem.getMaterial().isBlock()) { + //instance.setBlock(blockPosition.clone().add(0, 1, 0), (short) 10); return; + } int offsetX = blockFace == ClientPlayerDiggingPacket.BlockFace.WEST ? -1 : blockFace == ClientPlayerDiggingPacket.BlockFace.EAST ? 1 : 0; int offsetY = blockFace == ClientPlayerDiggingPacket.BlockFace.BOTTOM ? -1 : blockFace == ClientPlayerDiggingPacket.BlockFace.TOP ? 1 : 0; int offsetZ = blockFace == ClientPlayerDiggingPacket.BlockFace.NORTH ? -1 : blockFace == ClientPlayerDiggingPacket.BlockFace.SOUTH ? 1 : 0; blockPosition.add(offsetX, offsetY, offsetZ); - boolean intersectPlayer = player.getBoundingBox().intersect(blockPosition); + boolean intersectPlayer = player.getBoundingBox().intersect(blockPosition); // TODO check if collide with nearby players if (!intersectPlayer) { PlayerBlockPlaceEvent playerBlockPlaceEvent = new PlayerBlockPlaceEvent((short) 10, blockPosition, packet.hand); player.callEvent(PlayerBlockPlaceEvent.class, playerBlockPlaceEvent); @@ -49,7 +52,7 @@ public class BlockPlacementListener { } } else { Chunk chunk = instance.getChunkAt(blockPosition); - instance.sendChunkUpdate(player, chunk); + instance.sendChunkSectionUpdate(chunk, ChunkUtils.getSectionAt(blockPosition.getY()), player); } } player.getInventory().refreshSlot(player.getHeldSlot()); diff --git a/src/main/java/fr/themode/minestom/listener/PlayerDiggingListener.java b/src/main/java/fr/themode/minestom/listener/PlayerDiggingListener.java index cea3f474c..93e753757 100644 --- a/src/main/java/fr/themode/minestom/listener/PlayerDiggingListener.java +++ b/src/main/java/fr/themode/minestom/listener/PlayerDiggingListener.java @@ -25,7 +25,7 @@ public class PlayerDiggingListener { if (player.getGameMode() == GameMode.CREATIVE) { Instance instance = player.getInstance(); if (instance != null) { - instance.setBlock(blockPosition.getX(), blockPosition.getY(), blockPosition.getZ(), (short) 0); + instance.breakBlock(player, blockPosition); } } else if (player.getGameMode() == GameMode.SURVIVAL) { Instance instance = player.getInstance(); diff --git a/src/main/java/fr/themode/minestom/net/PacketWriterUtils.java b/src/main/java/fr/themode/minestom/net/PacketWriterUtils.java index 422f9bc47..0faf7405b 100644 --- a/src/main/java/fr/themode/minestom/net/PacketWriterUtils.java +++ b/src/main/java/fr/themode/minestom/net/PacketWriterUtils.java @@ -5,6 +5,7 @@ import fr.adamaq01.ozao.net.packet.Packet; import fr.themode.minestom.Main; import fr.themode.minestom.entity.Player; import fr.themode.minestom.net.packet.server.ServerPacket; +import fr.themode.minestom.net.player.PlayerConnection; import fr.themode.minestom.utils.PacketUtils; import java.util.Collection; @@ -25,13 +26,13 @@ public class PacketWriterUtils { public static void writeAndSend(Collection players, ServerPacket serverPacket) { batchesPool.execute(() -> { - Packet p = PacketUtils.writePacket(serverPacket); - Buffer encoded = PacketUtils.encode(p); - - int size = players.size(); if (size == 0) return; + + Packet p = PacketUtils.writePacket(serverPacket); + Buffer encoded = PacketUtils.encode(p); + encoded.getData().retain(size).markReaderIndex(); for (Player player : players) { player.getPlayerConnection().writeUnencodedPacket(encoded); @@ -40,4 +41,16 @@ public class PacketWriterUtils { }); } + public static void writeAndSend(PlayerConnection playerConnection, ServerPacket serverPacket) { + batchesPool.execute(() -> { + Packet p = PacketUtils.writePacket(serverPacket); + Buffer encoded = PacketUtils.encode(p); + playerConnection.writeUnencodedPacket(encoded); + }); + } + + public static void writeAndSend(Player player, ServerPacket serverPacket) { + writeAndSend(player.getPlayerConnection(), serverPacket); + } + } diff --git a/src/main/java/fr/themode/minestom/net/packet/server/play/ChunkDataPacket.java b/src/main/java/fr/themode/minestom/net/packet/server/play/ChunkDataPacket.java index 435c65832..6b0e0e913 100644 --- a/src/main/java/fr/themode/minestom/net/packet/server/play/ChunkDataPacket.java +++ b/src/main/java/fr/themode/minestom/net/packet/server/play/ChunkDataPacket.java @@ -20,6 +20,7 @@ public class ChunkDataPacket implements ServerPacket { public boolean fullChunk; public Chunk chunk; + public int[] sections; @Override public void write(PacketWriter writer) { @@ -31,10 +32,11 @@ public class ChunkDataPacket implements ServerPacket { int mask = 0; Buffer blocks = Buffer.create(); for (int i = 0; i < 16; i++) { - // TODO if fullchunk is false then only send changed sections - mask |= 1 << i; - short[] section = getSection(chunk, i); - Utils.writeBlocks(blocks, section, 14); + if (fullChunk || (sections.length == 16 && sections[i] != 0)) { + mask |= 1 << i; + short[] section = getSection(chunk, i); + Utils.writeBlocks(blocks, section, 14); + } } // Biome data if (fullChunk) { diff --git a/src/main/java/fr/themode/minestom/net/player/PlayerConnection.java b/src/main/java/fr/themode/minestom/net/player/PlayerConnection.java index eeb592e64..ed4be7167 100644 --- a/src/main/java/fr/themode/minestom/net/player/PlayerConnection.java +++ b/src/main/java/fr/themode/minestom/net/player/PlayerConnection.java @@ -4,8 +4,8 @@ import fr.adamaq01.ozao.net.Buffer; import fr.adamaq01.ozao.net.packet.Packet; import fr.adamaq01.ozao.net.server.Connection; import fr.themode.minestom.net.ConnectionState; +import fr.themode.minestom.net.PacketWriterUtils; import fr.themode.minestom.net.packet.server.ServerPacket; -import fr.themode.minestom.utils.PacketUtils; import io.netty.channel.Channel; import io.netty.channel.socket.SocketChannel; @@ -27,9 +27,9 @@ public class PlayerConnection { // TODO make that proper (remove reflection) - private Field field; + private static Field field; - { + static { try { field = Class.forName("fr.adamaq01.ozao.net.server.backend.tcp.TCPConnection").getDeclaredField("channel"); } catch (NoSuchFieldException e) { @@ -49,7 +49,7 @@ public class PlayerConnection { } public void sendPacket(ServerPacket serverPacket) { - sendPacket(PacketUtils.writePacket(serverPacket)); + PacketWriterUtils.writeAndSend(this, serverPacket); } public void flush() { diff --git a/src/main/java/fr/themode/minestom/utils/ChunkUtils.java b/src/main/java/fr/themode/minestom/utils/ChunkUtils.java index 1f7120870..9a7597510 100644 --- a/src/main/java/fr/themode/minestom/utils/ChunkUtils.java +++ b/src/main/java/fr/themode/minestom/utils/ChunkUtils.java @@ -18,6 +18,10 @@ public class ChunkUtils { return new int[]{chunkX, chunkZ}; } + public static int getSectionAt(int y) { + return y / 16; + } + public static long[] getChunksInRange(final Position position, int range) { long[] visibleChunks = new long[MathUtils.square(range + 1)];