diff --git a/src/main/java/ru/windcorp/progressia/common/world/WorldData.java b/src/main/java/ru/windcorp/progressia/common/world/WorldData.java index 6dec431..d719f20 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/WorldData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/WorldData.java @@ -25,6 +25,7 @@ import glm.vec._3.i.Vec3i; import gnu.trove.impl.sync.TSynchronizedLongObjectMap; import gnu.trove.map.TLongObjectMap; import gnu.trove.map.hash.TLongObjectHashMap; +import gnu.trove.set.TLongSet; import ru.windcorp.progressia.common.collision.CollisionModel; import ru.windcorp.progressia.common.util.CoordinatePacker; import ru.windcorp.progressia.common.world.block.BlockData; @@ -55,14 +56,16 @@ public class WorldData { } public void tmp_generate() { - final int size = 6; + final int size = 10; Vec3i cursor = new Vec3i(0, 0, 0); for (cursor.x = -(size / 2); cursor.x <= (size / 2); ++cursor.x) { for (cursor.y = -(size / 2); cursor.y <= (size / 2); ++cursor.y) { - ChunkData chunk = new ChunkData(cursor, this); - TestContent.generateChunk(chunk); - addChunk(chunk); + for (cursor.z = -(size / 2); cursor.z <= (size / 2); ++cursor.z) { + ChunkData chunk = new ChunkData(cursor, this); + TestContent.generateChunk(chunk); + addChunk(chunk); + } } } } @@ -140,6 +143,10 @@ public class WorldData { return chunks; } + public TLongSet getChunkKeys() { + return chunksByPos.keySet(); + } + public EntityData getEntity(long entityId) { return entitiesById.get(entityId); } diff --git a/src/main/java/ru/windcorp/progressia/common/world/entity/EntityData.java b/src/main/java/ru/windcorp/progressia/common/world/entity/EntityData.java index b1dfc42..c7eda54 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/entity/EntityData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/entity/EntityData.java @@ -2,9 +2,11 @@ package ru.windcorp.progressia.common.world.entity; import glm.vec._2.Vec2; import glm.vec._3.Vec3; +import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.common.collision.Collideable; import ru.windcorp.progressia.common.collision.CollisionModel; import ru.windcorp.progressia.common.state.StatefulObject; +import ru.windcorp.progressia.common.world.Coordinates; public class EntityData extends StatefulObject implements Collideable { @@ -27,6 +29,16 @@ public class EntityData extends StatefulObject implements Collideable { return position; } + public Vec3i getBlockInWorld(Vec3i output) { + if (output == null) output = new Vec3i(); + return position.round(output); + } + + public Vec3i getChunkCoords(Vec3i output) { + output = getBlockInWorld(output); + return Coordinates.convertInWorldToChunk(output, output); + } + public void setPosition(Vec3 position) { move(position.sub_(getPosition())); } diff --git a/src/main/java/ru/windcorp/progressia/server/ChunkLoadManager.java b/src/main/java/ru/windcorp/progressia/server/ChunkLoadManager.java new file mode 100644 index 0000000..99493bc --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/ChunkLoadManager.java @@ -0,0 +1,92 @@ +package ru.windcorp.progressia.server; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; + +import glm.vec._3.i.Vec3i; +import gnu.trove.set.TLongSet; +import gnu.trove.set.hash.TLongHashSet; +import ru.windcorp.progressia.common.util.CoordinatePacker; +import ru.windcorp.progressia.common.world.ChunkData; +import ru.windcorp.progressia.test.TestContent; + +public class ChunkLoadManager { + + private final Server server; + + private final Collection> allChunkLoaders = + Collections.synchronizedCollection(new ArrayList<>()); + + private final TLongSet requested = new TLongHashSet(); + private final TLongSet toLoad = new TLongHashSet(); + private final TLongSet toUnload = new TLongHashSet(); + + public ChunkLoadManager(Server server) { + this.server = server; + allChunkLoaders.add(server.getPlayerManager().getPlayers()); + } + + public void tick() { + gatherRequests(); + updateQueues(); + processQueues(); + } + + private void gatherRequests() { + requested.clear(); + + allChunkLoaders.forEach(collection -> { + collection.forEach(this::gatherRequests); + }); + } + + private void gatherRequests(ChunkLoader loader) { + loader.requestChunksToLoad(v -> requested.add(CoordinatePacker.pack3IntsIntoLong(v))); + } + + private void updateQueues() { + TLongSet loaded = getServer().getWorld().getData().getChunkKeys(); + + toLoad.clear(); + toLoad.addAll(requested); + toLoad.removeAll(loaded); + + toUnload.clear(); + toUnload.addAll(loaded); + toUnload.removeAll(requested); + } + + private void processQueues() { + Vec3i v = new Vec3i(); + + toLoad.forEach(key -> { + loadChunk(CoordinatePacker.unpack3IntsFromLong(key, v)); + return true; + }); + + toUnload.forEach(key -> { + unloadChunk(CoordinatePacker.unpack3IntsFromLong(key, v)); + return true; + }); + } + + public Server getServer() { + return server; + } + + public void loadChunk(Vec3i pos) { + + ChunkData chunk = new ChunkData(pos, getServer().getWorld().getData()); + TestContent.generateChunk(chunk); + getServer().getWorld().getData().addChunk(chunk); + + } + + public void unloadChunk(Vec3i pos) { + + getServer().getWorld().getData().removeChunk(getServer().getWorld().getData().getChunk(pos)); + + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/ChunkLoader.java b/src/main/java/ru/windcorp/progressia/server/ChunkLoader.java new file mode 100644 index 0000000..d5ae9e8 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/ChunkLoader.java @@ -0,0 +1,11 @@ +package ru.windcorp.progressia.server; + +import java.util.function.Consumer; + +import glm.vec._3.i.Vec3i; + +public interface ChunkLoader { + + void requestChunksToLoad(Consumer output); + +} diff --git a/src/main/java/ru/windcorp/progressia/server/Player.java b/src/main/java/ru/windcorp/progressia/server/Player.java index f5de3cf..8a6001e 100644 --- a/src/main/java/ru/windcorp/progressia/server/Player.java +++ b/src/main/java/ru/windcorp/progressia/server/Player.java @@ -9,7 +9,7 @@ import ru.windcorp.progressia.common.world.PlayerData; import ru.windcorp.progressia.common.world.entity.EntityData; import ru.windcorp.progressia.server.comms.ClientPlayer; -public class Player extends PlayerData { +public class Player extends PlayerData implements ChunkLoader { private final Server server; private final ClientPlayer client; @@ -28,7 +28,8 @@ public class Player extends PlayerData { return client; } - public void getRequestedChunks(Consumer chunkConsumer) { + @Override + public void requestChunksToLoad(Consumer chunkConsumer) { Vec3i start = getEntity().getPosition().round_(); Coordinates.convertInWorldToChunk(start, start); diff --git a/src/main/java/ru/windcorp/progressia/server/Server.java b/src/main/java/ru/windcorp/progressia/server/Server.java index 997dee0..ac59f00 100644 --- a/src/main/java/ru/windcorp/progressia/server/Server.java +++ b/src/main/java/ru/windcorp/progressia/server/Server.java @@ -112,8 +112,12 @@ public class Server { taskQueue.waitAndInvoke(task); } + public void schedule(Runnable task) { + taskQueue.schedule(task); + } + public void schedule(Consumer task) { - taskQueue.schedule(() -> task.accept(this)); + schedule(() -> task.accept(this)); } public void requestChange(Change change) { diff --git a/src/main/java/ru/windcorp/progressia/server/ServerThread.java b/src/main/java/ru/windcorp/progressia/server/ServerThread.java index 6350b81..ca9498b 100644 --- a/src/main/java/ru/windcorp/progressia/server/ServerThread.java +++ b/src/main/java/ru/windcorp/progressia/server/ServerThread.java @@ -5,6 +5,7 @@ import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import org.apache.logging.log4j.LogManager; +import ru.windcorp.progressia.common.util.crash.CrashReports; import ru.windcorp.progressia.server.world.ticking.TickerCoordinator; public class ServerThread implements Runnable { @@ -56,7 +57,7 @@ public class ServerThread implements Runnable { server.tick(); ticker.runOneTick(); } catch (Exception e) { - LogManager.getLogger(getClass()).error("Got an exception in server thread", e); + CrashReports.report(e, "Got an exception in the server thread"); } } diff --git a/src/main/java/ru/windcorp/progressia/test/TestChunkSender.java b/src/main/java/ru/windcorp/progressia/test/TestChunkSender.java index 345d8c5..672db8f 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestChunkSender.java +++ b/src/main/java/ru/windcorp/progressia/test/TestChunkSender.java @@ -2,12 +2,15 @@ package ru.windcorp.progressia.test; import java.io.IOException; +import glm.Glm; import ru.windcorp.progressia.common.comms.packets.PacketLoadChunk; +import ru.windcorp.progressia.common.comms.packets.PacketSetLocalPlayer; import ru.windcorp.progressia.common.io.ChunkIO; import ru.windcorp.progressia.common.util.crash.CrashReports; import ru.windcorp.progressia.common.world.ChunkData; import ru.windcorp.progressia.common.world.WorldData; import ru.windcorp.progressia.common.world.WorldDataListener; +import ru.windcorp.progressia.common.world.entity.EntityData; import ru.windcorp.progressia.server.Server; public class TestChunkSender implements WorldDataListener { @@ -21,12 +24,32 @@ public class TestChunkSender implements WorldDataListener { @Override public void onChunkLoaded(WorldData world, ChunkData chunk) { PacketLoadChunk packet = new PacketLoadChunk("Core:LoadChunk"); + + packet.getPosition().set( + chunk.getPosition().x, + chunk.getPosition().y, + chunk.getPosition().z + ); + try { ChunkIO.save(chunk, packet.getData().getOutputStream()); } catch (IOException e) { CrashReports.report(e, "TestChunkSender fjcked up. javahorse stupid"); } + server.getClientManager().broadcastGamePacket(packet); + + tmp_sendPlayerIfPossible(world, chunk); + } + + private void tmp_sendPlayerIfPossible(WorldData world, ChunkData chunk) { + EntityData e = world.getEntity(TestContent.PLAYER_ENTITY_ID); + if (e == null) return; + + if (Glm.equals(e.getChunkCoords(null), chunk.getPosition())) { + System.out.printf("TestChunkSender: player found in (%d; %d; %d)\n", e.getChunkCoords(null).x, e.getChunkCoords(null).y, e.getChunkCoords(null).z); + server.getClientManager().broadcastGamePacket(new PacketSetLocalPlayer(e.getEntityId())); + } } } diff --git a/src/main/java/ru/windcorp/progressia/test/TestContent.java b/src/main/java/ru/windcorp/progressia/test/TestContent.java index df425bd..6f64561 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestContent.java +++ b/src/main/java/ru/windcorp/progressia/test/TestContent.java @@ -28,6 +28,7 @@ import ru.windcorp.progressia.common.io.ChunkIO; import ru.windcorp.progressia.common.state.StatefulObjectRegistry.Factory; import ru.windcorp.progressia.common.util.Vectors; import ru.windcorp.progressia.common.world.ChunkData; +import ru.windcorp.progressia.common.world.Coordinates; import ru.windcorp.progressia.common.world.block.*; import ru.windcorp.progressia.common.world.entity.*; import ru.windcorp.progressia.common.world.tile.*; @@ -257,21 +258,33 @@ public class TestContent { TileData stones = TileDataRegistry.getInstance().get("Test:Stones"); TileData flowers = TileDataRegistry.getInstance().get("Test:YellowFlowers"); TileData sand = TileDataRegistry.getInstance().get("Test:Sand"); - - Vec3i aPoint = new Vec3i(5, 0, bpc + bpc/4).sub(chunk.getPosition().mul_(ChunkData.BLOCKS_PER_CHUNK)); + + final float maxHeight = 32; + final float rho = 2000; + + int[][] heightMap = new int[bpc][bpc]; + + for (int yic = 0; yic < heightMap.length; ++yic) { + int yiw = Coordinates.getInWorld(chunk.getY(), yic); + for (int xic = 0; xic < heightMap[yic].length; ++xic) { + int xiw = Coordinates.getInWorld(chunk.getX(), xic); + + int rsq = (xiw*xiw + yiw*yiw); + heightMap[xic][yic] = (int) (rsq / (rho + rsq) * maxHeight) - chunk.getZ()*bpc; + } + } + Vec3i pos = new Vec3i(); - for (int x = 0; x < bpc; ++x) { - for (int y = 0; y < bpc; ++y) { - for (int z = 0; z < bpc; ++z) { + for (pos.x = 0; pos.x < bpc; ++pos.x) { + for (pos.y = 0; pos.y < bpc; ++pos.y) { + for (pos.z = 0; pos.z < bpc; ++pos.z) { + + int layer = pos.z - heightMap[pos.x][pos.y]; - pos.set(x, y, z); - float f = aPoint.sub(pos, pos).length(); - pos.set(x, y, z); - - if (f > 17) { + if (layer < -4) { chunk.setBlock(pos, stone, false); - } else if (f > 14) { + } else if (layer < 0) { chunk.setBlock(pos, dirt, false); } else { chunk.setBlock(pos, air, false); @@ -283,45 +296,51 @@ public class TestContent { for (int x = 0; x < bpc; ++x) { for (int y = 0; y < bpc; ++y) { - pos.set(x, y, 0); - for (pos.z = bpc - 1; pos.z >= 0 && chunk.getBlock(pos) == air; --pos.z); - if (pos.z < 0) continue; +// int z = heightMap[x][y]; - chunk.getTiles(pos, BlockFace.TOP).add(grass); - for (BlockFace face : BlockFace.getFaces()) { - if (face.getVector().z != 0) continue; - pos.add(face.getVector()); + for (int z = 0; z < bpc; ++z) { + + pos.set(x, y, z); + int layer = pos.z - heightMap[x][y]; - if (!ChunkData.isInBounds(pos) || (chunk.getBlock(pos) == air)) { - pos.sub(face.getVector()); - chunk.getTiles(pos, face).add(grass); - } else { - pos.sub(face.getVector()); + if (layer == -1) { + chunk.getTiles(pos, BlockFace.TOP).add(grass); + for (BlockFace face : BlockFace.getFaces()) { + if (face.getVector().z != 0) continue; + pos.add(face.getVector()); + + if (!ChunkData.isInBounds(pos) || (chunk.getBlock(pos) == air)) { + pos.sub(face.getVector()); + chunk.getTiles(pos, face).add(grass); + } else { + pos.sub(face.getVector()); + } + } + + int hash = x*x * 19 ^ y*y * 41 ^ pos.z*pos.z * 147; + if (hash % 5 == 0) { + chunk.getTiles(pos, BlockFace.TOP).addFarthest(sand); + } + + hash = x*x * 13 ^ y*y * 37 ^ pos.z*pos.z * 129; + if (hash % 5 == 0) { + chunk.getTiles(pos, BlockFace.TOP).addFarthest(stones); + } + + hash = x*x * 17 ^ y*y * 39 ^ pos.z*pos.z * 131; + if (hash % 9 == 0) { + chunk.getTiles(pos, BlockFace.TOP).addFarthest(flowers); + } } } - - int hash = x*x * 19 ^ y*y * 41 ^ pos.z*pos.z * 147; - if (hash % 5 == 0) { - chunk.getTiles(pos, BlockFace.TOP).addFarthest(sand); - } - - hash = x*x * 13 ^ y*y * 37 ^ pos.z*pos.z * 129; - if (hash % 5 == 0) { - chunk.getTiles(pos, BlockFace.TOP).addFarthest(stones); - } - - hash = x*x * 17 ^ y*y * 39 ^ pos.z*pos.z * 131; - if (hash % 9 == 0) { - chunk.getTiles(pos, BlockFace.TOP).addFarthest(flowers); - } } } if (Glm.equals(chunk.getPosition(), Vectors.ZERO_3i)) { EntityData player = EntityDataRegistry.getInstance().create("Test:Player"); player.setEntityId(PLAYER_ENTITY_ID); - player.setPosition(new Vec3(-6, -6, 20)); + player.setPosition(new Vec3(8, 8, 8)); player.setDirection(new Vec2( (float) Math.toRadians(40), (float) Math.toRadians(45) ));