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;
}
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;
}

View File

@ -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<Player> getViewers() {
return Collections.unmodifiableSet(viewers);
}

View File

@ -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);
}
});
}
}

View File

@ -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;
}

View File

@ -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() {

View File

@ -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);
}
}

View File

@ -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<EntityCreature> creatures = Collections.synchronizedSet(new HashSet<>());
protected Set<Player> players = Collections.synchronizedSet(new HashSet<>());
protected Set<EntityCreature> creatures = new CopyOnWriteArraySet<>();
protected Set<Player> players = new CopyOnWriteArraySet<>();
private int chunkX, chunkZ;
private Biome biome;
private HashMap<Short, Block> blocks = new HashMap<>();

View File

@ -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;
}

View File

@ -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);

View File

@ -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);
}

View File

@ -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);

View File

@ -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);

View File

@ -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);