From 3b6b5676ed20a4ad564c11d88d77934ec1ce84b1 Mon Sep 17 00:00:00 2001 From: TheMode Date: Sun, 11 Aug 2019 13:57:23 +0200 Subject: [PATCH] Improved thread safety --- .../fr/themode/minestom/entity/Entity.java | 12 ----- .../minestom/entity/EntityCreature.java | 41 +++++++++++++++ .../minestom/entity/EntityManager.java | 51 +++++++++++-------- .../themode/minestom/entity/LivingEntity.java | 4 ++ .../fr/themode/minestom/entity/Player.java | 26 ++++------ .../minestom/entity/demo/ChickenCreature.java | 16 +++--- .../fr/themode/minestom/instance/Chunk.java | 6 +-- .../themode/minestom/instance/Instance.java | 13 +++-- .../minestom/net/ConnectionManager.java | 8 +++ .../packet/client/login/LoginStartPacket.java | 7 +-- .../ClientPlayerPositionAndLookPacket.java | 4 +- .../play/ClientPlayerPositionPacket.java | 4 +- .../server/play/EntityTeleportPacket.java | 2 +- 13 files changed, 118 insertions(+), 76 deletions(-) diff --git a/src/main/java/fr/themode/minestom/entity/Entity.java b/src/main/java/fr/themode/minestom/entity/Entity.java index 5a67e09c5..c44974e07 100644 --- a/src/main/java/fr/themode/minestom/entity/Entity.java +++ b/src/main/java/fr/themode/minestom/entity/Entity.java @@ -81,26 +81,14 @@ public class Entity { return x; } - public void setX(double x) { - this.x = x; - } - public double getY() { return y; } - public void setY(double y) { - this.y = y; - } - public double getZ() { return z; } - public void setZ(double z) { - this.z = z; - } - public void remove() { this.shouldRemove = true; } diff --git a/src/main/java/fr/themode/minestom/entity/EntityCreature.java b/src/main/java/fr/themode/minestom/entity/EntityCreature.java index ac6e777d7..e84357084 100644 --- a/src/main/java/fr/themode/minestom/entity/EntityCreature.java +++ b/src/main/java/fr/themode/minestom/entity/EntityCreature.java @@ -1,6 +1,9 @@ package fr.themode.minestom.entity; +import fr.themode.minestom.net.packet.server.ServerPacket; import fr.themode.minestom.net.packet.server.play.EntityPacket; +import fr.themode.minestom.net.packet.server.play.EntityRelativeMovePacket; +import fr.themode.minestom.net.packet.server.play.EntityTeleportPacket; import fr.themode.minestom.net.packet.server.play.SpawnMobPacket; import fr.themode.minestom.net.player.PlayerConnection; @@ -19,6 +22,40 @@ public abstract class EntityCreature extends LivingEntity { this.entityType = entityType; } + public void move(double x, double y, double z) { + double newX = getX() + x; + double newY = getY() + y; + double newZ = getZ() + z; + + if (chunkTest(newX, newZ)) + return; + + EntityRelativeMovePacket entityRelativeMovePacket = new EntityRelativeMovePacket(); + entityRelativeMovePacket.entityId = getEntityId(); + entityRelativeMovePacket.deltaX = (short) ((newX * 32 - getX() * 32) * 128); + entityRelativeMovePacket.deltaY = (short) ((newY * 32 - getY() * 32) * 128); + entityRelativeMovePacket.deltaZ = (short) ((newZ * 32 - getZ() * 32) * 128); + entityRelativeMovePacket.onGround = true; + sendPacketToViewers(entityRelativeMovePacket); + + refreshPosition(newX, newY, newZ); + } + + public void teleport(double x, double y, double z) { + if (chunkTest(x, z)) + return; + + EntityTeleportPacket entityTeleportPacket = new EntityTeleportPacket(); + entityTeleportPacket.entityId = getEntityId(); + entityTeleportPacket.x = x; + entityTeleportPacket.y = y; + entityTeleportPacket.z = z; + entityTeleportPacket.yaw = getYaw(); + entityTeleportPacket.pitch = getPitch(); + entityTeleportPacket.onGround = true; + sendPacketToViewers(entityTeleportPacket); + } + public void addViewer(Player player) { this.viewers.add(player); PlayerConnection playerConnection = player.getPlayerConnection(); @@ -48,6 +85,10 @@ public abstract class EntityCreature extends LivingEntity { } } + protected void sendPacketToViewers(ServerPacket packet) { + getViewers().forEach(player -> player.getPlayerConnection().sendPacket(packet)); + } + public Set getViewers() { return Collections.unmodifiableSet(viewers); } diff --git a/src/main/java/fr/themode/minestom/entity/EntityManager.java b/src/main/java/fr/themode/minestom/entity/EntityManager.java index 30f83b0ed..f600469c4 100644 --- a/src/main/java/fr/themode/minestom/entity/EntityManager.java +++ b/src/main/java/fr/themode/minestom/entity/EntityManager.java @@ -11,35 +11,42 @@ public class EntityManager { private static InstanceManager instanceManager = Main.getInstanceManager(); - private ExecutorService creaturesPool = Executors.newFixedThreadPool(3); + private ExecutorService creaturesPool = Executors.newFixedThreadPool(4); private ExecutorService playersPool = Executors.newFixedThreadPool(2); public void update() { - for (Instance instance : instanceManager.getInstances()) { - synchronized (instance) { - // Creatures - for (EntityCreature creature : instance.getCreatures()) { - creaturesPool.submit(() -> { + // Creatures + //long time = System.nanoTime(); + for (EntityCreature creature : instance.getCreatures()) { + creaturesPool.submit(() -> { + boolean shouldRemove = creature.shouldRemove(); + if (!shouldRemove) { creature.update(); - boolean shouldRemove = creature.shouldRemove(); - if (shouldRemove) { - instance.removeEntity(creature); - } - }); - } + } - // Players - for (Player player : instance.getPlayers()) { - playersPool.submit(() -> { - player.update(); - boolean shouldRemove = player.shouldRemove(); - if (shouldRemove) { - instance.removeEntity(player); - } - }); - } + if (creature.shouldRemove()) { + instance.removeEntity(creature); + } + }); + } + /*creaturesPool.shutdown(); + while (!creaturesPool.isTerminated()) { + + } + System.out.println("delay: " + (System.nanoTime() - time)); + creaturesPool = Executors.newFixedThreadPool(4);*/ + + // Players + for (Player player : instance.getPlayers()) { + playersPool.submit(() -> { + player.update(); + boolean shouldRemove = player.shouldRemove(); + if (shouldRemove) { + instance.removeEntity(player); + } + }); } } diff --git a/src/main/java/fr/themode/minestom/entity/LivingEntity.java b/src/main/java/fr/themode/minestom/entity/LivingEntity.java index 81de08902..189a6dbbf 100644 --- a/src/main/java/fr/themode/minestom/entity/LivingEntity.java +++ b/src/main/java/fr/themode/minestom/entity/LivingEntity.java @@ -11,6 +11,10 @@ public abstract class LivingEntity extends Entity { public abstract void update(); + public boolean chunkTest(double x, double z) { + return getInstance().getChunk((int) Math.floor(x / 16), (int) Math.floor(z / 16)) == null; + } + public float getYaw() { return yaw; } diff --git a/src/main/java/fr/themode/minestom/entity/Player.java b/src/main/java/fr/themode/minestom/entity/Player.java index 993848fdb..c910721c0 100644 --- a/src/main/java/fr/themode/minestom/entity/Player.java +++ b/src/main/java/fr/themode/minestom/entity/Player.java @@ -1,7 +1,6 @@ package fr.themode.minestom.entity; import fr.themode.minestom.Main; -import fr.themode.minestom.instance.Chunk; import fr.themode.minestom.net.packet.server.play.EntityTeleportPacket; import fr.themode.minestom.net.packet.server.play.PlayerPositionAndLookPacket; import fr.themode.minestom.net.packet.server.play.UpdateViewPositionPacket; @@ -44,21 +43,16 @@ public class Player extends LivingEntity { playerConnection.sendPacket(new UpdateViewPositionPacket(Math.floorDiv((int) x, 16), Math.floorDiv((int) z, 16))); } - public boolean chunkTest(double x, double z) { - Chunk newChunk = getInstance().getChunk((int) Math.floor(x / 16), (int) Math.floor(z / 16)); - if (newChunk == null) { - PlayerPositionAndLookPacket positionAndLookPacket = new PlayerPositionAndLookPacket(); - positionAndLookPacket.x = getX(); - positionAndLookPacket.y = getY(); - positionAndLookPacket.z = getZ(); - positionAndLookPacket.yaw = getYaw(); - positionAndLookPacket.pitch = getPitch(); - positionAndLookPacket.flags = 0x00; - positionAndLookPacket.teleportId = 67; - getPlayerConnection().sendPacket(positionAndLookPacket); - return true; - } - return false; + public void teleport(double x, double y, double z) { + PlayerPositionAndLookPacket positionAndLookPacket = new PlayerPositionAndLookPacket(); + positionAndLookPacket.x = x; + positionAndLookPacket.y = y; + positionAndLookPacket.z = z; + positionAndLookPacket.yaw = getYaw(); + positionAndLookPacket.pitch = getPitch(); + positionAndLookPacket.flags = 0x00; + positionAndLookPacket.teleportId = 67; + getPlayerConnection().sendPacket(positionAndLookPacket); } public String getUsername() { diff --git a/src/main/java/fr/themode/minestom/entity/demo/ChickenCreature.java b/src/main/java/fr/themode/minestom/entity/demo/ChickenCreature.java index 7a8019927..cab74ea7f 100644 --- a/src/main/java/fr/themode/minestom/entity/demo/ChickenCreature.java +++ b/src/main/java/fr/themode/minestom/entity/demo/ChickenCreature.java @@ -1,7 +1,6 @@ package fr.themode.minestom.entity.demo; import fr.themode.minestom.entity.EntityCreature; -import fr.themode.minestom.net.packet.server.play.EntityRelativeMovePacket; public class ChickenCreature extends EntityCreature { @@ -11,16 +10,13 @@ public class ChickenCreature extends EntityCreature { @Override public void update() { - onGround = true; - double speed = 0.075; - double newPos = getZ() + speed; - EntityRelativeMovePacket entityRelativeMovePacket = new EntityRelativeMovePacket(); - entityRelativeMovePacket.entityId = getEntityId(); - entityRelativeMovePacket.deltaZ = (short) ((newPos * 32 - getZ() * 32) * 128); - entityRelativeMovePacket.onGround = true; - getViewers().forEach(player -> player.getPlayerConnection().sendPacket(entityRelativeMovePacket)); - setZ(newPos); + /*Player player = Main.getConnectionManager().getPlayer("TheMode911"); + if (player != null) { + teleport(player.getX(), 5, player.getZ()); + }*/ + + move(0, 0, speed); } } diff --git a/src/main/java/fr/themode/minestom/instance/Chunk.java b/src/main/java/fr/themode/minestom/instance/Chunk.java index 4cbebd09e..d6cb16c58 100644 --- a/src/main/java/fr/themode/minestom/instance/Chunk.java +++ b/src/main/java/fr/themode/minestom/instance/Chunk.java @@ -7,13 +7,13 @@ import fr.themode.minestom.net.packet.server.play.ChunkDataPacket; import java.util.Collections; import java.util.HashMap; -import java.util.HashSet; import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; public class Chunk { - protected Set creatures = Collections.synchronizedSet(new HashSet<>()); - protected Set players = Collections.synchronizedSet(new HashSet<>()); + protected Set creatures = new CopyOnWriteArraySet<>(); + protected Set players = new CopyOnWriteArraySet<>(); private int chunkX, chunkZ; private Biome biome; private HashMap blocks = new HashMap<>(); diff --git a/src/main/java/fr/themode/minestom/instance/Instance.java b/src/main/java/fr/themode/minestom/instance/Instance.java index d54a7f64c..94f11f183 100644 --- a/src/main/java/fr/themode/minestom/instance/Instance.java +++ b/src/main/java/fr/themode/minestom/instance/Instance.java @@ -22,12 +22,11 @@ public class Instance { // TODO BlockBatch with pool public synchronized void setBlock(int x, int y, int z, Block block) { - int chunkX = Math.floorDiv(x, 16); - int chunkZ = Math.floorDiv(z, 16); + final int chunkX = Math.floorDiv(x, 16); + final int chunkZ = Math.floorDiv(z, 16); Chunk chunk = getChunk(chunkX, chunkZ); if (chunk == null) { - chunk = new Chunk(Biome.VOID, chunkX, chunkZ); - this.chunksSet.add(chunk); + chunk = createChunk(Biome.VOID, chunkX, chunkZ); } synchronized (chunk) { chunk.setBlock(x % 16, y, z % 16, block); @@ -59,7 +58,7 @@ public class Instance { } if (entity instanceof EntityCreature) { - // TODO based on distance with player + // TODO based on distance with players getPlayers().forEach(p -> ((EntityCreature) entity).addViewer(p)); } else if (entity instanceof Player) { getCreatures().forEach(entityCreature -> entityCreature.addViewer((Player) entity)); @@ -103,8 +102,8 @@ public class Instance { return uniqueId; } - private Chunk createChunk(int chunkX, int chunkZ) { - Chunk chunk = new Chunk(Biome.VOID, chunkX, chunkZ); + private Chunk createChunk(Biome biome, int chunkX, int chunkZ) { + Chunk chunk = new Chunk(biome, chunkX, chunkZ); this.chunksSet.add(chunk); return chunk; } diff --git a/src/main/java/fr/themode/minestom/net/ConnectionManager.java b/src/main/java/fr/themode/minestom/net/ConnectionManager.java index cbe3d81f6..4b1dd47ac 100644 --- a/src/main/java/fr/themode/minestom/net/ConnectionManager.java +++ b/src/main/java/fr/themode/minestom/net/ConnectionManager.java @@ -18,6 +18,14 @@ public class ConnectionManager { return Collections.unmodifiableCollection(players); } + public Player getPlayer(String username) { + for (Player player : getOnlinePlayers()) { + if (player.getUsername().equalsIgnoreCase(username)) + return player; + } + return null; + } + // Is only used at LoginStartPacket#process public void createPlayer(UUID uuid, String username, PlayerConnection connection) { Player player = new Player(uuid, username, connection); diff --git a/src/main/java/fr/themode/minestom/net/packet/client/login/LoginStartPacket.java b/src/main/java/fr/themode/minestom/net/packet/client/login/LoginStartPacket.java index f93d01849..4e6bf31cc 100644 --- a/src/main/java/fr/themode/minestom/net/packet/client/login/LoginStartPacket.java +++ b/src/main/java/fr/themode/minestom/net/packet/client/login/LoginStartPacket.java @@ -102,12 +102,13 @@ public class LoginStartPacket implements ClientPreplayPacket { playerInfoPacket.playerInfos.add(addPlayer); connection.sendPacket(playerInfoPacket); - for (int x = -2; x < 2; x++) - for (int z = -2; z < 2; z++) { + for (int x = -10; x < 10; x++) + for (int z = -10; z < 10; z++) { // TODO test entity ChickenCreature chickenCreature = new ChickenCreature(); chickenCreature.refreshPosition(0 + (double) x * 1, 5, 0 + (double) z * 1); - instance.addEntity(chickenCreature); + chickenCreature.setInstance(instance); + //instance.addEntity(chickenCreature); } diff --git a/src/main/java/fr/themode/minestom/net/packet/client/play/ClientPlayerPositionAndLookPacket.java b/src/main/java/fr/themode/minestom/net/packet/client/play/ClientPlayerPositionAndLookPacket.java index 4176a6940..860316bbe 100644 --- a/src/main/java/fr/themode/minestom/net/packet/client/play/ClientPlayerPositionAndLookPacket.java +++ b/src/main/java/fr/themode/minestom/net/packet/client/play/ClientPlayerPositionAndLookPacket.java @@ -13,8 +13,10 @@ public class ClientPlayerPositionAndLookPacket implements ClientPlayPacket { @Override public void process(Player player) { boolean chunkTest = player.chunkTest(x, z); - if (chunkTest) + if (chunkTest) { + player.teleport(player.getX(), player.getY(), player.getZ()); return; + } player.refreshPosition(x, y, z); player.refreshView(yaw, pitch); diff --git a/src/main/java/fr/themode/minestom/net/packet/client/play/ClientPlayerPositionPacket.java b/src/main/java/fr/themode/minestom/net/packet/client/play/ClientPlayerPositionPacket.java index c31afcc7b..6dff50b04 100644 --- a/src/main/java/fr/themode/minestom/net/packet/client/play/ClientPlayerPositionPacket.java +++ b/src/main/java/fr/themode/minestom/net/packet/client/play/ClientPlayerPositionPacket.java @@ -12,8 +12,10 @@ public class ClientPlayerPositionPacket implements ClientPlayPacket { @Override public void process(Player player) { boolean chunkTest = player.chunkTest(x, z); - if (chunkTest) + if (chunkTest) { + player.teleport(player.getX(), player.getY(), player.getZ()); return; + } player.refreshPosition(x, y, z); player.refreshOnGround(onGround); diff --git a/src/main/java/fr/themode/minestom/net/packet/server/play/EntityTeleportPacket.java b/src/main/java/fr/themode/minestom/net/packet/server/play/EntityTeleportPacket.java index 4a170b12a..794d84433 100644 --- a/src/main/java/fr/themode/minestom/net/packet/server/play/EntityTeleportPacket.java +++ b/src/main/java/fr/themode/minestom/net/packet/server/play/EntityTeleportPacket.java @@ -16,7 +16,7 @@ public class EntityTeleportPacket implements ServerPacket { Utils.writeVarInt(buffer, entityId); buffer.putDouble(x); buffer.putDouble(y); - buffer.putDouble(y); + buffer.putDouble(z); buffer.putByte((byte) (this.yaw * 256 / 360)); buffer.putByte((byte) (this.pitch * 256 / 360)); buffer.putBoolean(onGround);