Improved thread safety

This commit is contained in:
TheMode 2019-08-11 13:57:23 +02:00
parent 87345f70ab
commit 3b6b5676ed
13 changed files with 118 additions and 76 deletions

View File

@ -81,26 +81,14 @@ public class Entity {
return x; return x;
} }
public void setX(double x) {
this.x = x;
}
public double getY() { public double getY() {
return y; return y;
} }
public void setY(double y) {
this.y = y;
}
public double getZ() { public double getZ() {
return z; return z;
} }
public void setZ(double z) {
this.z = z;
}
public void remove() { public void remove() {
this.shouldRemove = true; this.shouldRemove = true;
} }

View File

@ -1,6 +1,9 @@
package fr.themode.minestom.entity; 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.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.packet.server.play.SpawnMobPacket;
import fr.themode.minestom.net.player.PlayerConnection; import fr.themode.minestom.net.player.PlayerConnection;
@ -19,6 +22,40 @@ public abstract class EntityCreature extends LivingEntity {
this.entityType = entityType; 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) { public void addViewer(Player player) {
this.viewers.add(player); this.viewers.add(player);
PlayerConnection playerConnection = player.getPlayerConnection(); 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<Player> getViewers() { public Set<Player> getViewers() {
return Collections.unmodifiableSet(viewers); return Collections.unmodifiableSet(viewers);
} }

View File

@ -11,35 +11,42 @@ public class EntityManager {
private static InstanceManager instanceManager = Main.getInstanceManager(); private static InstanceManager instanceManager = Main.getInstanceManager();
private ExecutorService creaturesPool = Executors.newFixedThreadPool(3); private ExecutorService creaturesPool = Executors.newFixedThreadPool(4);
private ExecutorService playersPool = Executors.newFixedThreadPool(2); private ExecutorService playersPool = Executors.newFixedThreadPool(2);
public void update() { public void update() {
for (Instance instance : instanceManager.getInstances()) { for (Instance instance : instanceManager.getInstances()) {
synchronized (instance) { // Creatures
// Creatures //long time = System.nanoTime();
for (EntityCreature creature : instance.getCreatures()) { for (EntityCreature creature : instance.getCreatures()) {
creaturesPool.submit(() -> { creaturesPool.submit(() -> {
boolean shouldRemove = creature.shouldRemove();
if (!shouldRemove) {
creature.update(); creature.update();
boolean shouldRemove = creature.shouldRemove(); }
if (shouldRemove) {
instance.removeEntity(creature);
}
});
}
// Players if (creature.shouldRemove()) {
for (Player player : instance.getPlayers()) { instance.removeEntity(creature);
playersPool.submit(() -> { }
player.update(); });
boolean shouldRemove = player.shouldRemove(); }
if (shouldRemove) { /*creaturesPool.shutdown();
instance.removeEntity(player); 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);
}
});
} }
} }

View File

@ -11,6 +11,10 @@ public abstract class LivingEntity extends Entity {
public abstract void update(); 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() { public float getYaw() {
return yaw; return yaw;
} }

View File

@ -1,7 +1,6 @@
package fr.themode.minestom.entity; package fr.themode.minestom.entity;
import fr.themode.minestom.Main; 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.EntityTeleportPacket;
import fr.themode.minestom.net.packet.server.play.PlayerPositionAndLookPacket; import fr.themode.minestom.net.packet.server.play.PlayerPositionAndLookPacket;
import fr.themode.minestom.net.packet.server.play.UpdateViewPositionPacket; 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))); playerConnection.sendPacket(new UpdateViewPositionPacket(Math.floorDiv((int) x, 16), Math.floorDiv((int) z, 16)));
} }
public boolean chunkTest(double x, double z) { public void teleport(double x, double y, double z) {
Chunk newChunk = getInstance().getChunk((int) Math.floor(x / 16), (int) Math.floor(z / 16)); PlayerPositionAndLookPacket positionAndLookPacket = new PlayerPositionAndLookPacket();
if (newChunk == null) { positionAndLookPacket.x = x;
PlayerPositionAndLookPacket positionAndLookPacket = new PlayerPositionAndLookPacket(); positionAndLookPacket.y = y;
positionAndLookPacket.x = getX(); positionAndLookPacket.z = z;
positionAndLookPacket.y = getY(); positionAndLookPacket.yaw = getYaw();
positionAndLookPacket.z = getZ(); positionAndLookPacket.pitch = getPitch();
positionAndLookPacket.yaw = getYaw(); positionAndLookPacket.flags = 0x00;
positionAndLookPacket.pitch = getPitch(); positionAndLookPacket.teleportId = 67;
positionAndLookPacket.flags = 0x00; getPlayerConnection().sendPacket(positionAndLookPacket);
positionAndLookPacket.teleportId = 67;
getPlayerConnection().sendPacket(positionAndLookPacket);
return true;
}
return false;
} }
public String getUsername() { public String getUsername() {

View File

@ -1,7 +1,6 @@
package fr.themode.minestom.entity.demo; package fr.themode.minestom.entity.demo;
import fr.themode.minestom.entity.EntityCreature; import fr.themode.minestom.entity.EntityCreature;
import fr.themode.minestom.net.packet.server.play.EntityRelativeMovePacket;
public class ChickenCreature extends EntityCreature { public class ChickenCreature extends EntityCreature {
@ -11,16 +10,13 @@ public class ChickenCreature extends EntityCreature {
@Override @Override
public void update() { public void update() {
onGround = true;
double speed = 0.075; double speed = 0.075;
double newPos = getZ() + speed;
EntityRelativeMovePacket entityRelativeMovePacket = new EntityRelativeMovePacket(); /*Player player = Main.getConnectionManager().getPlayer("TheMode911");
entityRelativeMovePacket.entityId = getEntityId(); if (player != null) {
entityRelativeMovePacket.deltaZ = (short) ((newPos * 32 - getZ() * 32) * 128); teleport(player.getX(), 5, player.getZ());
entityRelativeMovePacket.onGround = true; }*/
getViewers().forEach(player -> player.getPlayerConnection().sendPacket(entityRelativeMovePacket));
setZ(newPos); move(0, 0, speed);
} }
} }

View File

@ -7,13 +7,13 @@ import fr.themode.minestom.net.packet.server.play.ChunkDataPacket;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet;
import java.util.Set; import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
public class Chunk { public class Chunk {
protected Set<EntityCreature> creatures = Collections.synchronizedSet(new HashSet<>()); protected Set<EntityCreature> creatures = new CopyOnWriteArraySet<>();
protected Set<Player> players = Collections.synchronizedSet(new HashSet<>()); protected Set<Player> players = new CopyOnWriteArraySet<>();
private int chunkX, chunkZ; private int chunkX, chunkZ;
private Biome biome; private Biome biome;
private HashMap<Short, Block> blocks = new HashMap<>(); private HashMap<Short, Block> blocks = new HashMap<>();

View File

@ -22,12 +22,11 @@ public class Instance {
// TODO BlockBatch with pool // TODO BlockBatch with pool
public synchronized void setBlock(int x, int y, int z, Block block) { public synchronized void setBlock(int x, int y, int z, Block block) {
int chunkX = Math.floorDiv(x, 16); final int chunkX = Math.floorDiv(x, 16);
int chunkZ = Math.floorDiv(z, 16); final int chunkZ = Math.floorDiv(z, 16);
Chunk chunk = getChunk(chunkX, chunkZ); Chunk chunk = getChunk(chunkX, chunkZ);
if (chunk == null) { if (chunk == null) {
chunk = new Chunk(Biome.VOID, chunkX, chunkZ); chunk = createChunk(Biome.VOID, chunkX, chunkZ);
this.chunksSet.add(chunk);
} }
synchronized (chunk) { synchronized (chunk) {
chunk.setBlock(x % 16, y, z % 16, block); chunk.setBlock(x % 16, y, z % 16, block);
@ -59,7 +58,7 @@ public class Instance {
} }
if (entity instanceof EntityCreature) { if (entity instanceof EntityCreature) {
// TODO based on distance with player // TODO based on distance with players
getPlayers().forEach(p -> ((EntityCreature) entity).addViewer(p)); getPlayers().forEach(p -> ((EntityCreature) entity).addViewer(p));
} else if (entity instanceof Player) { } else if (entity instanceof Player) {
getCreatures().forEach(entityCreature -> entityCreature.addViewer((Player) entity)); getCreatures().forEach(entityCreature -> entityCreature.addViewer((Player) entity));
@ -103,8 +102,8 @@ public class Instance {
return uniqueId; return uniqueId;
} }
private Chunk createChunk(int chunkX, int chunkZ) { private Chunk createChunk(Biome biome, int chunkX, int chunkZ) {
Chunk chunk = new Chunk(Biome.VOID, chunkX, chunkZ); Chunk chunk = new Chunk(biome, chunkX, chunkZ);
this.chunksSet.add(chunk); this.chunksSet.add(chunk);
return chunk; return chunk;
} }

View File

@ -18,6 +18,14 @@ public class ConnectionManager {
return Collections.unmodifiableCollection(players); 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 // Is only used at LoginStartPacket#process
public void createPlayer(UUID uuid, String username, PlayerConnection connection) { public void createPlayer(UUID uuid, String username, PlayerConnection connection) {
Player player = new Player(uuid, username, connection); Player player = new Player(uuid, username, connection);

View File

@ -102,12 +102,13 @@ public class LoginStartPacket implements ClientPreplayPacket {
playerInfoPacket.playerInfos.add(addPlayer); playerInfoPacket.playerInfos.add(addPlayer);
connection.sendPacket(playerInfoPacket); connection.sendPacket(playerInfoPacket);
for (int x = -2; x < 2; x++) for (int x = -10; x < 10; x++)
for (int z = -2; z < 2; z++) { for (int z = -10; z < 10; z++) {
// TODO test entity // TODO test entity
ChickenCreature chickenCreature = new ChickenCreature(); ChickenCreature chickenCreature = new ChickenCreature();
chickenCreature.refreshPosition(0 + (double) x * 1, 5, 0 + (double) z * 1); chickenCreature.refreshPosition(0 + (double) x * 1, 5, 0 + (double) z * 1);
instance.addEntity(chickenCreature); chickenCreature.setInstance(instance);
//instance.addEntity(chickenCreature);
} }

View File

@ -13,8 +13,10 @@ public class ClientPlayerPositionAndLookPacket implements ClientPlayPacket {
@Override @Override
public void process(Player player) { public void process(Player player) {
boolean chunkTest = player.chunkTest(x, z); boolean chunkTest = player.chunkTest(x, z);
if (chunkTest) if (chunkTest) {
player.teleport(player.getX(), player.getY(), player.getZ());
return; return;
}
player.refreshPosition(x, y, z); player.refreshPosition(x, y, z);
player.refreshView(yaw, pitch); player.refreshView(yaw, pitch);

View File

@ -12,8 +12,10 @@ public class ClientPlayerPositionPacket implements ClientPlayPacket {
@Override @Override
public void process(Player player) { public void process(Player player) {
boolean chunkTest = player.chunkTest(x, z); boolean chunkTest = player.chunkTest(x, z);
if (chunkTest) if (chunkTest) {
player.teleport(player.getX(), player.getY(), player.getZ());
return; return;
}
player.refreshPosition(x, y, z); player.refreshPosition(x, y, z);
player.refreshOnGround(onGround); player.refreshOnGround(onGround);

View File

@ -16,7 +16,7 @@ public class EntityTeleportPacket implements ServerPacket {
Utils.writeVarInt(buffer, entityId); Utils.writeVarInt(buffer, entityId);
buffer.putDouble(x); buffer.putDouble(x);
buffer.putDouble(y); buffer.putDouble(y);
buffer.putDouble(y); buffer.putDouble(z);
buffer.putByte((byte) (this.yaw * 256 / 360)); buffer.putByte((byte) (this.yaw * 256 / 360));
buffer.putByte((byte) (this.pitch * 256 / 360)); buffer.putByte((byte) (this.pitch * 256 / 360));
buffer.putBoolean(onGround); buffer.putBoolean(onGround);