diff --git a/pom.xml b/pom.xml index c0f81fec..9039be89 100755 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 net.tridentsdk trident - 1.0-SNAPSHOT + 0.3-SNAPSHOT Trident http://www.tridentsdk.net/ jar diff --git a/src/main/java/net/tridentsdk/server/entity/TridentEntity.java b/src/main/java/net/tridentsdk/server/entity/TridentEntity.java index e55d0265..1a803433 100755 --- a/src/main/java/net/tridentsdk/server/entity/TridentEntity.java +++ b/src/main/java/net/tridentsdk/server/entity/TridentEntity.java @@ -35,6 +35,7 @@ import net.tridentsdk.server.packets.play.out.PacketPlayOutEntityVelocity; import net.tridentsdk.server.player.TridentPlayer; import net.tridentsdk.server.threads.ThreadsHandler; +import net.tridentsdk.server.world.TridentChunk; import net.tridentsdk.server.world.TridentWorld; import net.tridentsdk.util.Vector; import net.tridentsdk.util.WeakEntity; @@ -183,16 +184,6 @@ protected void encodeMetadata(ProtocolMetadata protocolMeta) { doEncodeMeta(protocolMeta); } - /** - * Moves the entity to the new coordinates. Not for teleportation. - * - * @param newCoords the new location for the entity - */ - public void doMove(Position newCoords) { - HANDLER.trackMovement(this, position(), newCoords); - this.setLocation(newCoords); - } - @Override public void teleport(double x, double y, double z) { this.teleport(Position.create(this.world(), x, y, z)); @@ -233,7 +224,14 @@ public Position position() { return this.loc; } - public void setLocation(Position loc) { + public void setPosition(Position loc) { + TridentChunk from = (TridentChunk) position().chunk(); + TridentChunk chunk = (TridentChunk) loc.chunk(); + if (!from.equals(chunk)) { + from.entitiesInternal().remove(this); + chunk.entitiesInternal().add(this); + } + this.loc = loc; } diff --git a/src/main/java/net/tridentsdk/server/packets/login/PacketLoginInStart.java b/src/main/java/net/tridentsdk/server/packets/login/PacketLoginInStart.java index 8d9883d5..86f6f926 100755 --- a/src/main/java/net/tridentsdk/server/packets/login/PacketLoginInStart.java +++ b/src/main/java/net/tridentsdk/server/packets/login/PacketLoginInStart.java @@ -120,9 +120,16 @@ public void handleReceived(ClientConnection connection) { // parse the response and set the ID JsonArray array = PacketLoginInEncryptionResponse.GSON.fromJson(sb.toString(), JsonArray.class); + JsonArray jsonArray = array.getAsJsonArray(); + if (jsonArray.size() == 0) { + // TODO more checks, session validity + connection.sendPacket( + new PacketLoginOutDisconnect().setJsonMessage("This server is in online-mode")); + return; + } id = UUID.fromString(PacketLoginInEncryptionResponse.idDash.matcher( - array.getAsJsonArray().get(0).getAsJsonObject().get("id").getAsString()) + jsonArray.get(0).getAsJsonObject().get("id").getAsString()) .replaceAll("$1-$2-$3-$4-$5")); if (TridentPlayer.getPlayer(id) != null) { diff --git a/src/main/java/net/tridentsdk/server/packets/play/in/PacketPlayInPlayerLook.java b/src/main/java/net/tridentsdk/server/packets/play/in/PacketPlayInPlayerLook.java index 27895938..a9cc364e 100755 --- a/src/main/java/net/tridentsdk/server/packets/play/in/PacketPlayInPlayerLook.java +++ b/src/main/java/net/tridentsdk/server/packets/play/in/PacketPlayInPlayerLook.java @@ -82,6 +82,6 @@ public void handleReceived(ClientConnection connection) { return; } - player.setLocation(to); + player.setPosition(to); } } diff --git a/src/main/java/net/tridentsdk/server/packets/play/in/PacketPlayInPlayerMove.java b/src/main/java/net/tridentsdk/server/packets/play/in/PacketPlayInPlayerMove.java index b3a80127..694a37ca 100755 --- a/src/main/java/net/tridentsdk/server/packets/play/in/PacketPlayInPlayerMove.java +++ b/src/main/java/net/tridentsdk/server/packets/play/in/PacketPlayInPlayerMove.java @@ -87,6 +87,6 @@ public void handleReceived(ClientConnection connection) { return; } - player.setLocation(to); + player.setPosition(to); } } diff --git a/src/main/java/net/tridentsdk/server/packets/play/out/PacketPlayOutEntityEquipment.java b/src/main/java/net/tridentsdk/server/packets/play/out/PacketPlayOutEntityEquipment.java index 46a1e844..b460cced 100755 --- a/src/main/java/net/tridentsdk/server/packets/play/out/PacketPlayOutEntityEquipment.java +++ b/src/main/java/net/tridentsdk/server/packets/play/out/PacketPlayOutEntityEquipment.java @@ -18,6 +18,7 @@ package net.tridentsdk.server.packets.play.out; import io.netty.buffer.ByteBuf; +import net.tridentsdk.server.data.Slot; import net.tridentsdk.server.netty.Codec; import net.tridentsdk.server.netty.packet.OutPacket; @@ -25,8 +26,7 @@ public class PacketPlayOutEntityEquipment extends OutPacket { protected int entityId; protected short slot; - protected long item; - // TODO: mojang slot shit + protected Slot item; @Override public int id() { @@ -41,10 +41,14 @@ public short slot() { return this.slot; } + public Slot item() { + return this.item; + } + @Override public void encode(ByteBuf buf) { Codec.writeVarInt32(buf, this.entityId); buf.writeShort((int) this.slot); - buf.writeLong(item); // TODO this is the way? + item.write(buf); } } diff --git a/src/main/java/net/tridentsdk/server/player/TridentPlayer.java b/src/main/java/net/tridentsdk/server/player/TridentPlayer.java index 15c587ac..a5a4628a 100755 --- a/src/main/java/net/tridentsdk/server/player/TridentPlayer.java +++ b/src/main/java/net/tridentsdk/server/player/TridentPlayer.java @@ -25,6 +25,7 @@ import net.tridentsdk.docs.InternalUseOnly; import net.tridentsdk.entity.Entity; import net.tridentsdk.entity.living.Player; +import net.tridentsdk.entity.types.EntityType; import net.tridentsdk.event.player.PlayerJoinEvent; import net.tridentsdk.factory.Factories; import net.tridentsdk.meta.ChatColor; @@ -36,7 +37,6 @@ import net.tridentsdk.server.netty.ClientConnection; import net.tridentsdk.server.netty.packet.Packet; import net.tridentsdk.server.packets.play.out.*; -import net.tridentsdk.server.threads.TaskGroup; import net.tridentsdk.server.threads.ThreadsHandler; import net.tridentsdk.server.world.TridentChunk; import net.tridentsdk.server.world.TridentWorld; @@ -117,7 +117,7 @@ public static TridentPlayer spawnPlayer(ClientConnection connection, UUID id, St p.abilities.canFly = 1; // DEBUG ===== - p.setLocation(new Position(p.world(), 0, 255, 0)); + p.setPosition(new Position(p.world(), 0, 255, 0)); p.spawnLocation = new Position(p.world(), 0, 255, 0); // ===== @@ -152,6 +152,7 @@ public static TridentPlayer spawnPlayer(ClientConnection connection, UUID id, St p.encodeMetadata(metadata); }); + p.spawn(); return p; } @@ -225,11 +226,6 @@ public void resumeLogin() { } } - @Override // Overridden to execute on the player handler instead - public void tick() { - ThreadsHandler.playerExecutor().execute(this::doTick); - } - @Override protected void doTick() { int distance = viewDistance(); @@ -245,29 +241,34 @@ protected void doTick() { ticksExisted.incrementAndGet(); } - public Set cleanChunks() { - if (knownChunks.size() > 441) { + public void cleanChunks() { + int toClean = knownChunks.size() - 441; + if (toClean > 0) { Position pos = position(); int x = (int) pos.x() / 16; int z = (int) pos.z() / 16; int viewDist = viewDistance(); - TaskGroup.process(knownChunks).every(4).with(ThreadsHandler.chunkExecutor()).using((location) -> { + int cleaned = 0; + for (ChunkLocation location : knownChunks) { int cx = location.x(); int cz = location.z(); int abs = Math.abs(cx - x); int abs1 = Math.abs(cz - z); - // TODO some possibility of deviating if (abs > viewDist || abs1 > viewDist) { - connection.sendPacket(new PacketPlayOutChunkData(new byte[512], location, true, (short) 0)); - knownChunks.remove(location); + boolean tried = ((TridentWorld) world()).loadedChunks.tryRemove(location); + if (tried) { + connection.sendPacket(new PacketPlayOutChunkData(new byte[0], location, true, (short) 0)); + knownChunks.remove(location); + cleaned++; + } } - }); - } - return knownChunks; + if (cleaned >= toClean) return; + } + } } @Override @@ -279,10 +280,7 @@ protected void doRemove() { } @Override - public void setLocation(Position loc) { - ProtocolMetadata metadata = new ProtocolMetadata(); - encodeMetadata(metadata); - + public void setPosition(Position loc) { players().stream() .filter((p) -> !p.equals(this)) .forEach((p) -> { @@ -292,7 +290,7 @@ public void setLocation(Position loc) { .set("onGround", onGround)); }); - super.setLocation(loc); + super.setPosition(loc); } /* @@ -446,4 +444,9 @@ public void setViewDistance(int viewDistance) { public int viewDistance() { return Math.min(viewDistance, MAX_VIEW); } + + @Override + public EntityType type() { + return EntityType.PLAYER; + } } diff --git a/src/main/java/net/tridentsdk/server/world/ChunkCache.java b/src/main/java/net/tridentsdk/server/world/ChunkCache.java index 8d57c70b..25ca4973 100644 --- a/src/main/java/net/tridentsdk/server/world/ChunkCache.java +++ b/src/main/java/net/tridentsdk/server/world/ChunkCache.java @@ -19,7 +19,7 @@ import com.google.common.collect.Lists; import net.tridentsdk.concurrent.HeldValueLatch; import net.tridentsdk.docs.AccessNoDoc; -import net.tridentsdk.server.threads.TaskGroup; +import net.tridentsdk.entity.types.EntityType; import net.tridentsdk.server.threads.ThreadsHandler; import net.tridentsdk.util.TridentLogger; import net.tridentsdk.world.Chunk; @@ -29,9 +29,10 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import java.util.concurrent.ExecutionException; @AccessNoDoc -class ChunkCache { +public class ChunkCache { private final ConcurrentMap> cachedChunks = new ConcurrentHashMap<>(); private final TridentWorld world; @@ -79,27 +80,38 @@ public TridentChunk get(ChunkLocation location, boolean gen) { return (TridentChunk) chunk; } - public void retain(Set locations) { - // Generate is much more appropriate thread pool - // not only because it is used only for loading bytes - // but because using the chunk executor interferes - // with other tasks and might cause livelocks for no - // reason - TaskGroup.process(keys()).every(100).with(ThreadsHandler.saver()).using((loc) -> { - HeldValueLatch chunk = cachedChunks.get(loc); - TridentChunk rem; - if (chunk != null && chunk.hasValue()) { - rem = chunk.get(); - } else { - // Remove the chunk once it is available - return; - } + public boolean tryRemove(ChunkLocation location) { + try { + return ThreadsHandler.genExecutor().submit(() -> { + HeldValueLatch chunk = cachedChunks.get(location); + if (chunk == null) { + return false; + } - if (!locations.contains(loc) && chunk.hasValue()) { - world.loader().saveChunk(rem); - cachedChunks.remove(loc); - } - }); + if (chunk.hasValue()) { // No value = needs to generate + Chunk c = chunk.get(); + if (c.entities() // Ensure there are no players + .stream() + .filter(e -> e.type().equals(EntityType.PLAYER)) + .count() == 0) { + remove(location); + c.unload(); + + return true; + } + } + + return false; + }).get(); + } catch (InterruptedException | ExecutionException e) { + e.printStackTrace(); + } + + return false; + } + + public void remove(ChunkLocation location) { + cachedChunks.remove(location); } public Set keys() { diff --git a/src/main/java/net/tridentsdk/server/world/TridentChunk.java b/src/main/java/net/tridentsdk/server/world/TridentChunk.java index 291d5e91..8c2b6dd4 100755 --- a/src/main/java/net/tridentsdk/server/world/TridentChunk.java +++ b/src/main/java/net/tridentsdk/server/world/TridentChunk.java @@ -17,11 +17,14 @@ package net.tridentsdk.server.world; +import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import net.tridentsdk.Position; import net.tridentsdk.base.Block; import net.tridentsdk.base.Substance; import net.tridentsdk.concurrent.TaskExecutor; +import net.tridentsdk.entity.Entity; +import net.tridentsdk.factory.Factories; import net.tridentsdk.meta.nbt.*; import net.tridentsdk.server.packets.play.out.PacketPlayOutChunkData; import net.tridentsdk.server.threads.ThreadsHandler; @@ -36,6 +39,7 @@ import java.io.IOException; import java.util.Arrays; import java.util.List; +import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.stream.Stream; @@ -43,6 +47,7 @@ public class TridentChunk implements Chunk { private final TridentWorld world; private final ChunkLocation location; private final TaskExecutor executor = ThreadsHandler.chunkExecutor().scaledThread(); + private final Set entities = Factories.collect().createSet(); public volatile ChunkSection[] sections; private volatile int lastFileAccess; private volatile long lastModified; @@ -78,6 +83,15 @@ protected void setLastFileAccess(int last) { this.lastFileAccess = last; } + @Override + public Set entities() { + return ImmutableSet.copyOf(entities); + } + + public Set entitiesInternal() { + return entities; + } + @Override public void generate() { executor.addTask(() -> { @@ -306,6 +320,12 @@ public void load(CompoundTag root) { this.inhabitedTime = inhabitedTime.value(); // Cumulative number of ticks player have been in the chunk } + @Override + public void unload() { + world.loader().saveChunk(this); + world.loadedChunks.remove(location); + } + public CompoundTag asNbt() { CompoundTag root = new CompoundTag("root"); CompoundTag level = new CompoundTag("Level"); diff --git a/src/main/java/net/tridentsdk/server/world/TridentChunkSnapshot.java b/src/main/java/net/tridentsdk/server/world/TridentChunkSnapshot.java index b8ffe1c8..b00cc06c 100755 --- a/src/main/java/net/tridentsdk/server/world/TridentChunkSnapshot.java +++ b/src/main/java/net/tridentsdk/server/world/TridentChunkSnapshot.java @@ -18,6 +18,7 @@ package net.tridentsdk.server.world; import net.tridentsdk.base.Block; +import net.tridentsdk.entity.Entity; import net.tridentsdk.meta.nbt.CompoundTag; import net.tridentsdk.world.Chunk; import net.tridentsdk.world.ChunkLocation; @@ -25,6 +26,7 @@ import net.tridentsdk.world.World; import java.util.List; +import java.util.Set; public class TridentChunkSnapshot implements ChunkSnapshot { private final TridentWorld world; @@ -59,6 +61,12 @@ public void load() { // TODO } + @Override + public Set entities() { + // TODO + return null; + } + @Override public void generate() { } @@ -92,4 +100,9 @@ public Block blockAt(int relX, int y, int relZ) { public ChunkSnapshot snapshot() { return this; } + + @Override + public void unload() { + throw new UnsupportedOperationException("Cannot unload a snapshot"); + } } diff --git a/src/main/java/net/tridentsdk/server/world/TridentWorld.java b/src/main/java/net/tridentsdk/server/world/TridentWorld.java index 8b994281..afd46bab 100755 --- a/src/main/java/net/tridentsdk/server/world/TridentWorld.java +++ b/src/main/java/net/tridentsdk/server/world/TridentWorld.java @@ -19,7 +19,6 @@ import com.google.common.collect.ImmutableSet; import com.google.common.io.ByteStreams; -import net.tridentsdk.Defaults; import net.tridentsdk.Difficulty; import net.tridentsdk.GameMode; import net.tridentsdk.Position; @@ -27,7 +26,6 @@ import net.tridentsdk.entity.Entity; import net.tridentsdk.entity.Projectile; import net.tridentsdk.entity.block.SlotProperties; -import net.tridentsdk.entity.living.Player; import net.tridentsdk.entity.living.ProjectileLauncher; import net.tridentsdk.entity.traits.EntityProperties; import net.tridentsdk.entity.types.EntityType; @@ -257,7 +255,7 @@ public void tick() { ThreadsHandler.worldExecutor().execute(() -> { redstoneTick = !redstoneTick; - if (time >= 2400) + if (time >= 24000) time = 0; if (time % 40 == 0) TridentPlayer.sendAll(new PacketPlayOutTimeUpdate().set("worldAge", existed).set("time", time)); @@ -275,21 +273,9 @@ public void tick() { thunderTime = ThreadLocalRandom.current().nextInt(); } - // Complex idiom saves a bit of time on large iterations #nanobenchmarking - if (time % Defaults.CHUNK_CLEAN_TICK_INTERVAL == 0) { - Set retain = Factories.collect().createSet(); - for (Entity entity : entities) { - ((TridentEntity) entity).tick(); - if (entity instanceof Player) { - retain.addAll(((TridentPlayer) entity).cleanChunks()); - } - } - loadedChunks.retain(retain); - } else { - for (Entity entity : entities) { - ((TridentEntity) entity).tick(); - } + for (Entity entity : entities) { + ((TridentEntity) entity).tick(); } time++; @@ -373,6 +359,15 @@ public Entity addEntity(Entity entity) { public void removeEntity(Entity entity) { this.entities.remove(entity); + + TridentChunk c = (TridentChunk) entity.position().chunk(); + if (!c.entitiesInternal().remove(entity)) { + for (Chunk chunk : loadedChunks.values()) { + // If we don't do this a simple concurrency miss + // can lead to a memory leak + if (((TridentChunk) chunk).entitiesInternal().remove(entity)) return; + } + } } @Override