diff --git a/src/main/java/ru/windcorp/progressia/client/Client.java b/src/main/java/ru/windcorp/progressia/client/Client.java new file mode 100644 index 0000000..b257e58 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/Client.java @@ -0,0 +1,41 @@ +package ru.windcorp.progressia.client; + +import glm.vec._3.Vec3; +import ru.windcorp.progressia.client.comms.DefaultClientCommsListener; +import ru.windcorp.progressia.client.comms.ServerCommsChannel; +import ru.windcorp.progressia.client.graphics.world.Camera; +import ru.windcorp.progressia.client.world.WorldRender; +import ru.windcorp.progressia.common.world.WorldData; + +public class Client { + + private final WorldRender world; + + private final Camera camera = new Camera( + new Vec3(-6, -6, 20), + (float) Math.toRadians(-40), (float) Math.toRadians(-45), + (float) Math.toRadians(70) + ); + + private final ServerCommsChannel comms; + + public Client(WorldData world, ServerCommsChannel comms) { + this.world = new WorldRender(world); + this.comms = comms; + + comms.addListener(new DefaultClientCommsListener(this)); + } + + public WorldRender getWorld() { + return world; + } + + public Camera getCamera() { + return camera; + } + + public ServerCommsChannel getComms() { + return comms; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/client/ClientProxy.java b/src/main/java/ru/windcorp/progressia/client/ClientProxy.java index 9115390..caa6f52 100644 --- a/src/main/java/ru/windcorp/progressia/client/ClientProxy.java +++ b/src/main/java/ru/windcorp/progressia/client/ClientProxy.java @@ -18,20 +18,15 @@ package ru.windcorp.progressia.client; import ru.windcorp.progressia.Proxy; -import ru.windcorp.progressia.client.graphics.GUI; import ru.windcorp.progressia.client.graphics.backend.GraphicsBackend; import ru.windcorp.progressia.client.graphics.backend.RenderTaskQueue; import ru.windcorp.progressia.client.graphics.flat.FlatRenderProgram; -import ru.windcorp.progressia.client.graphics.flat.LayerTestUI; import ru.windcorp.progressia.client.graphics.font.GNUUnifontLoader; import ru.windcorp.progressia.client.graphics.font.Typefaces; -import ru.windcorp.progressia.client.graphics.gui.LayerTestGUI; import ru.windcorp.progressia.client.graphics.texture.Atlases; -import ru.windcorp.progressia.client.graphics.world.LayerWorld; import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram; -import ru.windcorp.progressia.client.world.renders.BlockRenders; -import ru.windcorp.progressia.client.world.renders.TileRenders; import ru.windcorp.progressia.common.resource.ResourceManager; +import ru.windcorp.progressia.server.ServerState; public class ClientProxy implements Proxy { @@ -47,13 +42,12 @@ public class ClientProxy implements Proxy { e.printStackTrace(); } - BlockRenders.registerTest(); - TileRenders.registerTest(); + TestContent.registerContent(); + Atlases.loadAllAtlases(); - GUI.addBottomLayer(new LayerWorld()); - GUI.addTopLayer(new LayerTestUI()); - GUI.addTopLayer(new LayerTestGUI()); + ServerState.startServer(); + ClientState.connectToLocalServer(); } } diff --git a/src/main/java/ru/windcorp/progressia/client/ClientState.java b/src/main/java/ru/windcorp/progressia/client/ClientState.java new file mode 100644 index 0000000..1a3eb73 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/ClientState.java @@ -0,0 +1,40 @@ +package ru.windcorp.progressia.client; + +import ru.windcorp.progressia.client.comms.localhost.LocalServerCommsChannel; +import ru.windcorp.progressia.client.graphics.GUI; +import ru.windcorp.progressia.client.graphics.flat.LayerTestUI; +import ru.windcorp.progressia.client.graphics.gui.LayerTestGUI; +import ru.windcorp.progressia.client.graphics.world.LayerWorld; +import ru.windcorp.progressia.common.world.WorldData; +import ru.windcorp.progressia.server.ServerState; + +public class ClientState { + + private static Client instance; + + public static Client getInstance() { + return instance; + } + + public static void setInstance(Client instance) { + ClientState.instance = instance; + } + + public static void connectToLocalServer() { + + WorldData world = new WorldData(); + Client client = new Client(world, new LocalServerCommsChannel( + ServerState.getInstance() + )); + + setInstance(client); + + GUI.addBottomLayer(new LayerWorld(client)); + GUI.addTopLayer(new LayerTestUI()); + GUI.addTopLayer(new LayerTestGUI()); + + } + + private ClientState() {} + +} diff --git a/src/main/java/ru/windcorp/progressia/client/TestContent.java b/src/main/java/ru/windcorp/progressia/client/TestContent.java new file mode 100644 index 0000000..9d64b54 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/TestContent.java @@ -0,0 +1,101 @@ +package ru.windcorp.progressia.client; + +import static ru.windcorp.progressia.client.world.renders.BlockRenders.register; +import static ru.windcorp.progressia.client.world.renders.BlockRenders.getBlockTexture; +import static ru.windcorp.progressia.client.world.renders.TileRenders.register; +import static ru.windcorp.progressia.client.world.renders.TileRenders.getTileTexture; + +import static ru.windcorp.progressia.common.block.BlockDataRegistry.register; +import static ru.windcorp.progressia.common.block.TileDataRegistry.register; + +import static ru.windcorp.progressia.server.block.BlockLogicRegistry.register; + +import org.lwjgl.glfw.GLFW; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.client.comms.controls.ControlTriggerOnKeyPress; +import ru.windcorp.progressia.client.comms.controls.ControlTriggerRegistry; +import ru.windcorp.progressia.client.graphics.input.KeyMatcher; +import ru.windcorp.progressia.client.world.renders.*; +import ru.windcorp.progressia.common.block.*; +import ru.windcorp.progressia.common.comms.controls.ControlData; +import ru.windcorp.progressia.common.comms.controls.ControlDataRegistry; +import ru.windcorp.progressia.common.comms.controls.PacketControl; +import ru.windcorp.progressia.common.world.ChunkData; +import ru.windcorp.progressia.server.Server; +import ru.windcorp.progressia.server.block.*; +import ru.windcorp.progressia.server.comms.Client; +import ru.windcorp.progressia.server.comms.controls.ControlLogic; +import ru.windcorp.progressia.server.comms.controls.ControlLogicRegistry; + +public class TestContent { + + public static void registerContent() { + registerWorldContent(); + regsiterControls(); + } + + private static void registerWorldContent() { + registerBlocks(); + registerTiles(); + } + + private static void registerBlocks() { + register(new BlockData("Test", "Air")); + register(new BlockRenderNone("Test", "Air")); + register(new BlockLogic("Test", "Air")); + + register(new BlockData("Test", "Dirt")); + register(new BlockRenderOpaqueCube("Test", "Dirt", getBlockTexture("dirt"))); + register(new BlockLogic("Test", "Dirt")); + + register(new BlockData("Test", "Stone")); + register(new BlockRenderOpaqueCube("Test", "Stone", getBlockTexture("stone"))); + register(new BlockLogic("Test", "Stone")); + + register(new BlockData("Test", "Compass")); + register(new BlockRenderOpaqueCube("Test", "Compass", getBlockTexture("compass"))); + register(new BlockLogic("Test", "Compass")); + + register(new BlockData("Test", "Glass")); + register(new BlockRenderTransparentCube("Test", "Glass", getBlockTexture("glass_clear"))); + register(new BlockLogic("Test", "Glass")); + } + + private static void registerTiles() { + register(new TileData("Test", "Grass")); + register(new TileRenderGrass("Test", "Grass", getTileTexture("grass_top"), getTileTexture("grass_side"))); + + register(new TileData("Test", "Stones")); + register(new TileRenderSimple("Test", "Stones", getTileTexture("stones"))); + + register(new TileData("Test", "YellowFlowers")); + register(new TileRenderSimple("Test", "YellowFlowers", getTileTexture("yellow_flowers"))); + + register(new TileData("Test", "Sand")); + register(new TileRenderSimple("Test", "Sand", getTileTexture("sand"))); + } + + private static void regsiterControls() { + ControlDataRegistry.getInstance().register(new ControlData("Test", "Switch000")); + ControlTriggerRegistry.getInstance().register(new ControlTriggerOnKeyPress("Test", "Switch000", new KeyMatcher(GLFW.GLFW_KEY_G, new int[0], 0)::matches)); + ControlLogicRegistry.getInstance().register(new ControlLogic("Test", "Switch000") { + @Override + public void apply(Server server, PacketControl packet, Client client) { + Vec3i z000 = new Vec3i(0, 0, 0); + + ChunkData data = server.getWorld().getChunk(z000).getData(); + + BlockData block; + if (data.getBlock(z000).getId().equals("Test:Stone")) { + block = BlockDataRegistry.get("Test:Glass"); + } else { + block = BlockDataRegistry.get("Test:Stone"); + } + + server.getAdHocChanger().setBlock(z000, block); + } + }); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/client/comms/DefaultClientCommsListener.java b/src/main/java/ru/windcorp/progressia/client/comms/DefaultClientCommsListener.java new file mode 100644 index 0000000..134bfb1 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/comms/DefaultClientCommsListener.java @@ -0,0 +1,43 @@ +package ru.windcorp.progressia.client.comms; + +import java.io.IOException; + +import ru.windcorp.progressia.client.Client; +import ru.windcorp.progressia.client.world.ChunkRender; +import ru.windcorp.progressia.common.comms.CommsListener; +import ru.windcorp.progressia.common.comms.packets.Packet; +import ru.windcorp.progressia.common.comms.packets.PacketWorldChange; + +public class DefaultClientCommsListener implements CommsListener { + + private final Client client; + + public DefaultClientCommsListener(Client client) { + this.client = client; + } + + @Override + public void onPacketReceived(Packet packet) { + if (packet instanceof PacketWorldChange) { + ((PacketWorldChange) packet).apply( + getClient().getWorld().getData() + ); + + tmp_reassembleWorld(); + } + } + + private void tmp_reassembleWorld() { + getClient().getWorld().getChunks().forEach(ChunkRender::markForUpdate); + } + + @Override + public void onIOError(IOException reason) { + // TODO implement + } + + public Client getClient() { + return client; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/client/comms/ServerCommsChannel.java b/src/main/java/ru/windcorp/progressia/client/comms/ServerCommsChannel.java new file mode 100644 index 0000000..e0cbdd3 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/comms/ServerCommsChannel.java @@ -0,0 +1,11 @@ +package ru.windcorp.progressia.client.comms; + +import ru.windcorp.progressia.common.comms.CommsChannel; + +public abstract class ServerCommsChannel extends CommsChannel { + + public ServerCommsChannel(Role... roles) { + super(roles); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTrigger.java b/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTrigger.java new file mode 100644 index 0000000..b755284 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTrigger.java @@ -0,0 +1,11 @@ +package ru.windcorp.progressia.client.comms.controls; + +import ru.windcorp.progressia.common.util.Namespaced; + +public abstract class ControlTrigger extends Namespaced { + + public ControlTrigger(String namespace, String name) { + super(namespace, name); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggerInputBased.java b/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggerInputBased.java new file mode 100644 index 0000000..abdc3a4 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggerInputBased.java @@ -0,0 +1,14 @@ +package ru.windcorp.progressia.client.comms.controls; + +import ru.windcorp.progressia.client.graphics.input.InputEvent; +import ru.windcorp.progressia.common.comms.controls.PacketControl; + +public abstract class ControlTriggerInputBased extends ControlTrigger { + + public ControlTriggerInputBased(String namespace, String name) { + super(namespace, name); + } + + public abstract PacketControl onInputEvent(InputEvent event); + +} diff --git a/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggerOnKeyPress.java b/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggerOnKeyPress.java new file mode 100644 index 0000000..c830318 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggerOnKeyPress.java @@ -0,0 +1,39 @@ +package ru.windcorp.progressia.client.comms.controls; + +import java.util.function.Predicate; + +import ru.windcorp.progressia.client.graphics.input.InputEvent; +import ru.windcorp.progressia.client.graphics.input.KeyEvent; +import ru.windcorp.progressia.common.comms.controls.ControlDataRegistry; +import ru.windcorp.progressia.common.comms.controls.PacketControl; + +public class ControlTriggerOnKeyPress extends ControlTriggerInputBased { + + private final Predicate predicate; + private final PacketControl packet; + + public ControlTriggerOnKeyPress( + String namespace, String name, + Predicate predicate + ) { + super(namespace, name); + this.predicate = predicate; + this.packet = new PacketControl( + getNamespace(), "ControlKeyPress" + getName(), + ControlDataRegistry.getInstance().get(getId()) + ); + } + + @Override + public PacketControl onInputEvent(InputEvent event) { + if (!(event instanceof KeyEvent)) return null; + + KeyEvent keyEvent = (KeyEvent) event; + + if (!keyEvent.isPress()) return null; + if (!predicate.test(keyEvent)) return null; + + return packet; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggerRegistry.java b/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggerRegistry.java new file mode 100644 index 0000000..b695325 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/comms/controls/ControlTriggerRegistry.java @@ -0,0 +1,14 @@ +package ru.windcorp.progressia.client.comms.controls; + +import ru.windcorp.progressia.common.util.NamespacedRegistry; + +public class ControlTriggerRegistry extends NamespacedRegistry { + + private static final ControlTriggerRegistry INSTANCE = + new ControlTriggerRegistry(); + + public static ControlTriggerRegistry getInstance() { + return INSTANCE; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/client/comms/controls/InputBasedControls.java b/src/main/java/ru/windcorp/progressia/client/comms/controls/InputBasedControls.java new file mode 100644 index 0000000..a072e39 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/comms/controls/InputBasedControls.java @@ -0,0 +1,33 @@ +package ru.windcorp.progressia.client.comms.controls; + +import ru.windcorp.progressia.client.Client; +import ru.windcorp.progressia.client.graphics.input.bus.Input; +import ru.windcorp.progressia.common.comms.packets.Packet; + +public class InputBasedControls { + + private final Client client; + + private final ControlTriggerInputBased[] controls; + + public InputBasedControls(Client client) { + this.client = client; + + this.controls = ControlTriggerRegistry.getInstance().values().stream() + .filter(ControlTriggerInputBased.class::isInstance) + .toArray(ControlTriggerInputBased[]::new); + } + + public void handleInput(Input input) { + for (ControlTriggerInputBased c : controls) { + Packet packet = c.onInputEvent(input.getEvent()); + + if (packet != null) { + input.consume(); + client.getComms().sendPacket(packet); + break; + } + } + } + +} diff --git a/src/main/java/ru/windcorp/progressia/client/comms/localhost/LocalClient.java b/src/main/java/ru/windcorp/progressia/client/comms/localhost/LocalClient.java new file mode 100644 index 0000000..37235b0 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/comms/localhost/LocalClient.java @@ -0,0 +1,33 @@ +package ru.windcorp.progressia.client.comms.localhost; + +import java.io.IOException; + +import ru.windcorp.progressia.common.comms.packets.Packet; +import ru.windcorp.progressia.server.comms.Client; + +public class LocalClient extends Client { + + private final LocalServerCommsChannel serverComms; + + public LocalClient(int id, LocalServerCommsChannel serverComms) { + super(id, Role.GAME, Role.CHAT); + setState(State.CONNECTED); + + this.serverComms = serverComms; + } + + @Override + protected void doSendPacket(Packet packet) throws IOException { + this.serverComms.relayPacketToClient(packet); + } + + public void relayPacketToServer(Packet packet) { + onPacketReceived(packet); + } + + @Override + public void disconnect() { + // Do nothing + } + +} diff --git a/src/main/java/ru/windcorp/progressia/client/comms/localhost/LocalServerCommsChannel.java b/src/main/java/ru/windcorp/progressia/client/comms/localhost/LocalServerCommsChannel.java new file mode 100644 index 0000000..478f6f4 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/comms/localhost/LocalServerCommsChannel.java @@ -0,0 +1,37 @@ +package ru.windcorp.progressia.client.comms.localhost; + +import ru.windcorp.progressia.client.comms.ServerCommsChannel; +import ru.windcorp.progressia.common.comms.packets.Packet; +import ru.windcorp.progressia.server.Server; + +public class LocalServerCommsChannel extends ServerCommsChannel { + + private final LocalClient localClient; + + public LocalServerCommsChannel(Server server) { + super(Role.GAME, Role.CHAT); + setState(State.CONNECTED); + + this.localClient = new LocalClient( + server.getClientManager().grabClientId(), + this + ); + + server.getClientManager().addClient(localClient); + } + + @Override + protected void doSendPacket(Packet packet) { + localClient.relayPacketToServer(packet); + } + + public void relayPacketToClient(Packet packet) { + onPacketReceived(packet); + } + + @Override + public void disconnect() { + // Do nothing + } + +} diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/backend/RenderTaskQueue.java b/src/main/java/ru/windcorp/progressia/client/graphics/backend/RenderTaskQueue.java index a30168a..e49934c 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/backend/RenderTaskQueue.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/backend/RenderTaskQueue.java @@ -17,91 +17,30 @@ *******************************************************************************/ package ru.windcorp.progressia.client.graphics.backend; -import java.util.Iterator; -import java.util.Queue; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicReference; - import ru.windcorp.jputil.functions.ThrowingRunnable; +import ru.windcorp.progressia.common.util.TaskQueue; public class RenderTaskQueue { - private static final Queue QUEUE = new ConcurrentLinkedQueue<>(); - - private RenderTaskQueue() {} - + private static final TaskQueue HANDLER = + new TaskQueue(GraphicsInterface::isRenderThread); + public static void invokeLater(Runnable task) { - QUEUE.add(task); + HANDLER.invokeLater(task); } - + public static void invokeNow(Runnable task) { - if (GraphicsInterface.isRenderThread()) { - task.run(); - } else { - invokeLater(task); - } + HANDLER.invokeNow(task); } - - private static final Object WAIT_AND_INVOKE_MONITOR = new Object(); - - @SuppressWarnings("unchecked") + public static void waitAndInvoke( ThrowingRunnable task - ) throws InterruptedException, E { - - if (GraphicsInterface.isRenderThread()) { - task.run(); - return; - } - - final AtomicBoolean flag = - new AtomicBoolean(false); - final AtomicReference thrownContainer = - new AtomicReference<>(null); - - invokeLater(() -> { - - try { - task.run(); - } catch (Throwable t) { - thrownContainer.set(t); - } - - flag.set(true); - - synchronized (WAIT_AND_INVOKE_MONITOR) { - WAIT_AND_INVOKE_MONITOR.notifyAll(); - } - }); - - while (!flag.get()) { - synchronized (WAIT_AND_INVOKE_MONITOR) { - WAIT_AND_INVOKE_MONITOR.wait(); - } - } - - Throwable thrown = thrownContainer.get(); - if (thrown != null) { - if (thrown instanceof RuntimeException) { - throw (RuntimeException) thrown; - } - - if (thrown instanceof Error) { - throw (Error) thrown; - } - - throw (E) thrown; // Guaranteed - } + ) throws InterruptedException, E { + HANDLER.waitAndInvoke(task); } - + public static void runTasks() { - Iterator tasks = QUEUE.iterator(); - - while (tasks.hasNext()) { - tasks.next().run(); - tasks.remove(); - } + HANDLER.runTasks(); } } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/backend/RenderThread.java b/src/main/java/ru/windcorp/progressia/client/graphics/backend/RenderThread.java index ccdf674..fbb6330 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/backend/RenderThread.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/backend/RenderThread.java @@ -43,6 +43,8 @@ class RenderThread extends Thread { waitForFrame(); GraphicsBackend.endFrame(); } + + System.exit(0); } private void render() { diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/flat/LayerTestUI.java b/src/main/java/ru/windcorp/progressia/client/graphics/flat/LayerTestUI.java index 80c01e5..cf2cb90 100755 --- a/src/main/java/ru/windcorp/progressia/client/graphics/flat/LayerTestUI.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/flat/LayerTestUI.java @@ -22,6 +22,7 @@ import org.lwjgl.glfw.GLFW; import com.google.common.eventbus.Subscribe; import glm.mat._4.Mat4; +import ru.windcorp.progressia.client.ClientState; import ru.windcorp.progressia.client.graphics.Colors; import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface; import ru.windcorp.progressia.client.graphics.input.KeyEvent; @@ -29,7 +30,6 @@ import ru.windcorp.progressia.client.graphics.input.bus.Input; import ru.windcorp.progressia.client.graphics.model.LambdaModel; import ru.windcorp.progressia.client.graphics.texture.SimpleTextures; import ru.windcorp.progressia.client.graphics.texture.Texture; -import ru.windcorp.progressia.client.graphics.world.LayerWorld; public class LayerTestUI extends AssembledFlatLayer { @@ -73,7 +73,7 @@ public class LayerTestUI extends AssembledFlatLayer { target.addCustomRenderer(new LambdaModel(LambdaModel.lambdaBuilder() .addDynamicPart( target.createRectagle(0, 0, texSize, texSize, 0xFFFFFF, compassFg), - mat -> mat.translate(texSize/2, texSize/2, 0).rotateZ(LayerWorld.tmp_the_camera.getYaw()).translate(-texSize/2, -texSize/2, 0) + mat -> mat.translate(texSize/2, texSize/2, 0).rotateZ(ClientState.getInstance().getCamera().getYaw()).translate(-texSize/2, -texSize/2, 0) ) )); target.popTransform(); diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java b/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java index 95d02c9..651dac5 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/world/LayerWorld.java @@ -21,7 +21,8 @@ import org.lwjgl.glfw.GLFW; import glm.mat._3.Mat3; import glm.vec._3.Vec3; -import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.client.Client; +import ru.windcorp.progressia.client.comms.controls.InputBasedControls; import ru.windcorp.progressia.client.graphics.Layer; import ru.windcorp.progressia.client.graphics.backend.GraphicsBackend; import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface; @@ -29,22 +30,10 @@ import ru.windcorp.progressia.client.graphics.input.CursorMoveEvent; import ru.windcorp.progressia.client.graphics.input.InputEvent; import ru.windcorp.progressia.client.graphics.input.KeyEvent; import ru.windcorp.progressia.client.graphics.input.bus.Input; -import ru.windcorp.progressia.client.world.WorldRender; -import ru.windcorp.progressia.common.block.BlockDataRegistry; import ru.windcorp.progressia.common.util.Vectors; -import ru.windcorp.progressia.common.world.ChunkData; -import ru.windcorp.progressia.common.world.WorldData; public class LayerWorld extends Layer { - public static Camera tmp_the_camera; - - private final Camera camera = new Camera( - new Vec3(-6, -6, 20), - (float) Math.toRadians(-40), (float) Math.toRadians(-45), - (float) Math.toRadians(70) - ); - private final Vec3 velocity = new Vec3(); private final Mat3 angMat = new Mat3(); @@ -55,11 +44,13 @@ public class LayerWorld extends Layer { private final WorldRenderHelper helper = new WorldRenderHelper(); - private final WorldRender world = new WorldRender(new WorldData()); + private final Client client; + private final InputBasedControls inputBasedControls; - public LayerWorld() { + public LayerWorld(Client client) { super("World"); - tmp_the_camera = camera; + this.client = client; + this.inputBasedControls = new InputBasedControls(client); } @Override @@ -75,11 +66,11 @@ public class LayerWorld extends Layer { @Override protected void doRender() { - camera.apply(helper); + client.getCamera().apply(helper); renderWorld(); helper.reset(); - angMat.set().rotateZ(-camera.getYaw()); + angMat.set().rotateZ(-client.getCamera().getYaw()); Vec3 movement = Vectors.grab3(); @@ -97,13 +88,17 @@ public class LayerWorld extends Layer { Vec3 velCopy = Vectors.grab3().set(velocity); velCopy.mul((float) (GraphicsInterface.getFrameLength() * 60)); - camera.move(velCopy); + client.getCamera().move(velCopy); Vectors.release(velCopy); + + if (GraphicsBackend.getFramesRendered() % 60 == 0) { + System.out.println(GraphicsInterface.getFPS()); + } } private void renderWorld() { - world.render(helper); + this.client.getWorld().render(helper); } @Override @@ -113,22 +108,23 @@ public class LayerWorld extends Layer { InputEvent event = input.getEvent(); if (event instanceof KeyEvent) { - onKeyEvent((KeyEvent) event); - input.consume(); + if (onKeyEvent((KeyEvent) event)) { + input.consume(); + } } else if (event instanceof CursorMoveEvent) { onMouseMoved((CursorMoveEvent) event); input.consume(); } - } - - public Camera getCamera() { - return camera; + + if (!input.isConsumed()) { + inputBasedControls.handleInput(input); + } } private boolean flag = true; - private void onKeyEvent(KeyEvent event) { - if (event.isRepeat()) return; + private boolean onKeyEvent(KeyEvent event) { + if (event.isRepeat()) return false; int multiplier = event.isPress() ? 1 : -1; @@ -153,7 +149,7 @@ public class LayerWorld extends Layer { break; case GLFW.GLFW_KEY_ESCAPE: - if (!event.isPress()) return; + if (!event.isPress()) return false; if (flag) { GLFW.glfwSetInputMode(GraphicsBackend.getWindowHandle(), GLFW.GLFW_CURSOR, GLFW.GLFW_CURSOR_NORMAL); @@ -164,25 +160,11 @@ public class LayerWorld extends Layer { flag = !flag; break; - case GLFW.GLFW_KEY_G: - if (!event.isPress()) return; - - Vec3i pos = Vectors.grab3i().set(0, 0, 0); - Vec3i chunkPos = Vectors.grab3i().set(0, 0, 0); - - ChunkData chunk = world.getData().getChunk(chunkPos); - if (chunk.getBlock(pos).getId().equals("Test:Stone")) { - chunk.setBlock(pos, BlockDataRegistry.get("Test:Glass")); - } else { - chunk.setBlock(pos, BlockDataRegistry.get("Test:Stone")); - } - world.getChunk(chunkPos).markForUpdate(); - - Vectors.release(pos); - Vectors.release(chunkPos); - - break; + default: + return false; } + + return true; } private void onMouseMoved(CursorMoveEvent event) { @@ -191,7 +173,7 @@ public class LayerWorld extends Layer { final float yawScale = 0.002f; final float pitchScale = yawScale; - camera.turn( + client.getCamera().turn( (float) (event.getChangeY() * pitchScale), (float) (event.getChangeX() * yawScale) ); diff --git a/src/main/java/ru/windcorp/progressia/client/world/renders/BlockRenders.java b/src/main/java/ru/windcorp/progressia/client/world/renders/BlockRenders.java index 5c48a36..5505e1d 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/renders/BlockRenders.java +++ b/src/main/java/ru/windcorp/progressia/client/world/renders/BlockRenders.java @@ -33,24 +33,9 @@ public class BlockRenders { private static final AtlasGroup BLOCKS_ATLAS_GROUP = new AtlasGroup("Blocks", 1 << 12); - - private static Texture dirt = getTexture("dirt"); - private static Texture stone = getTexture("stone"); - private static Texture glass = getTexture("glass_clear"); - private static Texture compass = getTexture("compass"); private BlockRenders() {} - public static void registerTest() { - register(new BlockRenderOpaqueCube("Test", "Dirt", dirt, dirt, dirt, dirt, dirt, dirt)); - register(new BlockRenderOpaqueCube("Test", "Stone", stone, stone, stone, stone, stone, stone)); - - register(new BlockRenderOpaqueCube("Test", "Compass", compass, compass, getTexture("side_north"), getTexture("side_south"), getTexture("side_east"), getTexture("side_west"))); - - register(new BlockRenderNone("Test", "Air")); - register(new BlockRenderTransparentCube("Test", "Glass", glass, glass, glass, glass, glass, glass)); - } - public static BlockRender get(String name) { return BLOCK_RENDERS.get(name); } @@ -59,7 +44,7 @@ public class BlockRenders { BLOCK_RENDERS.put(blockRender.getId(), blockRender); } - public static Texture getTexture(String name) { + public static Texture getBlockTexture(String name) { return new SimpleTexture( Atlases.getSprite( ResourceManager.getTextureResource("blocks/" + name), diff --git a/src/main/java/ru/windcorp/progressia/client/world/renders/TileRenders.java b/src/main/java/ru/windcorp/progressia/client/world/renders/TileRenders.java index 94a995c..a02f55f 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/renders/TileRenders.java +++ b/src/main/java/ru/windcorp/progressia/client/world/renders/TileRenders.java @@ -36,14 +36,6 @@ public class TileRenders { private TileRenders() {} - public static void registerTest() { - register(new TileRenderGrass("Test", "Grass", getTexture("grass_top"), getTexture("grass_side"))); - - register(new TileRenderSimple("Test", "Stones", getTexture("stones"))); - register(new TileRenderSimple("Test", "YellowFlowers", getTexture("yellow_flowers"))); - register(new TileRenderSimple("Test", "Sand", getTexture("sand"))); - } - public static TileRender get(String name) { return TILE_RENDERS.get(name); } @@ -52,7 +44,7 @@ public class TileRenders { TILE_RENDERS.put(tileRender.getId(), tileRender); } - public static Texture getTexture(String name) { + public static Texture getTileTexture(String name) { return new SimpleTexture( Atlases.getSprite( ResourceManager.getTextureResource("tiles/" + name), diff --git a/src/main/java/ru/windcorp/progressia/common/block/BlockDataRegistry.java b/src/main/java/ru/windcorp/progressia/common/block/BlockDataRegistry.java index d7e67a4..6bd57e6 100644 --- a/src/main/java/ru/windcorp/progressia/common/block/BlockDataRegistry.java +++ b/src/main/java/ru/windcorp/progressia/common/block/BlockDataRegistry.java @@ -24,13 +24,6 @@ public class BlockDataRegistry { private static final Map REGISTRY = new HashMap<>(); - static { - register(new BlockData("Test", "Dirt")); - register(new BlockData("Test", "Stone")); - register(new BlockData("Test", "Air")); - register(new BlockData("Test", "Glass")); - } - public static BlockData get(String name) { return REGISTRY.get(name); } diff --git a/src/main/java/ru/windcorp/progressia/common/block/TileDataRegistry.java b/src/main/java/ru/windcorp/progressia/common/block/TileDataRegistry.java index 2769546..42abceb 100644 --- a/src/main/java/ru/windcorp/progressia/common/block/TileDataRegistry.java +++ b/src/main/java/ru/windcorp/progressia/common/block/TileDataRegistry.java @@ -24,13 +24,6 @@ public class TileDataRegistry { private static final Map REGISTRY = new HashMap<>(); - static { - register(new TileData("Test", "Grass")); - register(new TileData("Test", "Stones")); - register(new TileData("Test", "YellowFlowers")); - register(new TileData("Test", "Sand")); - } - public static TileData get(String name) { return REGISTRY.get(name); } diff --git a/src/main/java/ru/windcorp/progressia/common/comms/CommsChannel.java b/src/main/java/ru/windcorp/progressia/common/comms/CommsChannel.java new file mode 100644 index 0000000..bd443b1 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/comms/CommsChannel.java @@ -0,0 +1,136 @@ +package ru.windcorp.progressia.common.comms; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Set; + +import com.google.common.collect.Sets; + +import ru.windcorp.progressia.common.comms.packets.Packet; + +public abstract class CommsChannel { + + public static enum State { + /** + * Client is currently establishing connection. + */ + CONNECTING, + + /** + * Client is ready to receive and send packets. + */ + CONNECTED, + + /** + * Client is being disconnected. + */ + DISCONNECTING, + + /** + * Communication is not possible. The client may have been disconnected + * after connecting or may have never connected. + */ + DISCONNECTED + } + + public static enum Role { + GAME, + CHAT, + RCON + // TODO create role for that thingy that only connects to get server status + } + + private State state = State.CONNECTING; + + protected final Set roles; + + private final Collection listeners = + Collections.synchronizedCollection(new ArrayList<>()); + + public CommsChannel(Role... roles) { + this.roles = Sets.immutableEnumSet(Arrays.asList(roles)); + } + + protected abstract void doSendPacket(Packet packet) throws IOException; + + private synchronized void sendPacket( + Packet packet, + State expectedState, String errorMessage + ) { + if (getState() != expectedState) { + throw new IllegalStateException( + String.format(errorMessage, this, getState()) + ); + } + + try { + doSendPacket(packet); + } catch (IOException e) { + onIOError(e, "Could not send packet"); + } + } + + public synchronized void sendPacket(Packet packet) { + sendPacket( + packet, + State.CONNECTED, + "Client %s is in state %s and cannot receive packets normally" + ); + } + + public synchronized void sendConnectingPacket(Packet packet) { + sendPacket( + packet, + State.CONNECTING, + "Client %s is in state %s and is no longer connecting" + ); + } + + public synchronized void sendDisconnectingPacket(Packet packet) { + sendPacket( + packet, + State.CONNECTING, + "Client %s is in state %s and is no longer disconnecting" + ); + } + + public abstract void disconnect(); + + protected void onPacketReceived(Packet packet) { + listeners.forEach(l -> l.onPacketReceived(packet)); + } + + public void addListener(CommsListener listener) { + listeners.add(listener); + } + + public void removeListener(CommsListener listener) { + listeners.remove(listener); + } + + protected void onIOError(IOException e, String string) { + // TODO implement + e.printStackTrace(); + listeners.forEach(l -> l.onIOError(e)); + } + + public synchronized State getState() { + return state; + } + + public Set getRoles() { + return roles; + } + + public boolean isReady() { + return getState() == State.CONNECTED; + } + + public synchronized void setState(State state) { + this.state = state; + } + +} \ No newline at end of file diff --git a/src/main/java/ru/windcorp/progressia/common/comms/CommsListener.java b/src/main/java/ru/windcorp/progressia/common/comms/CommsListener.java new file mode 100644 index 0000000..5ad2c72 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/comms/CommsListener.java @@ -0,0 +1,13 @@ +package ru.windcorp.progressia.common.comms; + +import java.io.IOException; + +import ru.windcorp.progressia.common.comms.packets.Packet; + +public interface CommsListener { + + void onPacketReceived(Packet packet); + + void onIOError(IOException reason); + +} diff --git a/src/main/java/ru/windcorp/progressia/common/comms/controls/ControlData.java b/src/main/java/ru/windcorp/progressia/common/comms/controls/ControlData.java new file mode 100644 index 0000000..065bbdf --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/comms/controls/ControlData.java @@ -0,0 +1,11 @@ +package ru.windcorp.progressia.common.comms.controls; + +import ru.windcorp.progressia.common.util.Namespaced; + +public class ControlData extends Namespaced { + + public ControlData(String namespace, String name) { + super(namespace, name); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/comms/controls/ControlDataRegistry.java b/src/main/java/ru/windcorp/progressia/common/comms/controls/ControlDataRegistry.java new file mode 100644 index 0000000..8c9146a --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/comms/controls/ControlDataRegistry.java @@ -0,0 +1,13 @@ +package ru.windcorp.progressia.common.comms.controls; + +import ru.windcorp.progressia.common.util.NamespacedRegistry; + +public class ControlDataRegistry extends NamespacedRegistry { + + private static final ControlDataRegistry INSTANCE = new ControlDataRegistry(); + + public static ControlDataRegistry getInstance() { + return INSTANCE; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/comms/controls/PacketControl.java b/src/main/java/ru/windcorp/progressia/common/comms/controls/PacketControl.java new file mode 100644 index 0000000..20fbd24 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/comms/controls/PacketControl.java @@ -0,0 +1,18 @@ +package ru.windcorp.progressia.common.comms.controls; + +import ru.windcorp.progressia.common.comms.packets.Packet; + +public class PacketControl extends Packet { + + private final ControlData control; + + public PacketControl(String namespace, String name, ControlData control) { + super(namespace, name); + this.control = control; + } + + public ControlData getControl() { + return control; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/comms/packets/Packet.java b/src/main/java/ru/windcorp/progressia/common/comms/packets/Packet.java new file mode 100644 index 0000000..114aa18 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/comms/packets/Packet.java @@ -0,0 +1,11 @@ +package ru.windcorp.progressia.common.comms.packets; + +import ru.windcorp.progressia.common.util.Namespaced; + +public class Packet extends Namespaced { + + public Packet(String namespace, String name) { + super(namespace, name); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/comms/packets/PacketWorldChange.java b/src/main/java/ru/windcorp/progressia/common/comms/packets/PacketWorldChange.java new file mode 100644 index 0000000..32bd02c --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/comms/packets/PacketWorldChange.java @@ -0,0 +1,13 @@ +package ru.windcorp.progressia.common.comms.packets; + +import ru.windcorp.progressia.common.world.WorldData; + +public abstract class PacketWorldChange extends Packet { + + public PacketWorldChange(String namespace, String name) { + super(namespace, name); + } + + public abstract void apply(WorldData world); + +} diff --git a/src/main/java/ru/windcorp/progressia/common/util/NamespacedRegistry.java b/src/main/java/ru/windcorp/progressia/common/util/NamespacedRegistry.java new file mode 100644 index 0000000..5aa457c --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/util/NamespacedRegistry.java @@ -0,0 +1,107 @@ +package ru.windcorp.progressia.common.util; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import com.google.errorprone.annotations.DoNotCall; + +public class NamespacedRegistry +implements Map { + + private final Map backingMap = + Collections.synchronizedMap(new HashMap<>()); + + public void register(E element) { + backingMap.put(element.getId(), element); + } + + public void registerAll(Collection elements) { + for (E element : elements) { + register(element); + } + } + + @Override + public int size() { + return backingMap.size(); + } + + @Override + public boolean isEmpty() { + return backingMap.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return backingMap.containsKey(key); + } + + public boolean has(String id) { + return backingMap.containsKey(id); + } + + @Override + public boolean containsValue(Object value) { + return backingMap.containsValue(value); + } + + public boolean isRegistered(E element) { + return has(element.getId()); + } + + @Override + public E get(Object key) { + return backingMap.get(key); + } + + /** + * Use {@link #register(E)}. + */ + @Override + @DoNotCall @Deprecated + public E put(String key, E value) { + throw new UnsupportedOperationException( + "Use NamespacedRegistry.register(E)" + ); + } + + @Override + public E remove(Object key) { + return backingMap.remove(key); + } + + /** + * Use {@link #registerAll(Collection)}. + */ + @Override + @DoNotCall @Deprecated + public void putAll(Map m) { + throw new UnsupportedOperationException( + "Use NamespacedRegistry.registerAll(Collection)" + ); + } + + @Override + public void clear() { + backingMap.clear(); + } + + @Override + public Set keySet() { + return backingMap.keySet(); + } + + @Override + public Collection values() { + return backingMap.values(); + } + + @Override + public Set> entrySet() { + return backingMap.entrySet(); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/util/TaskQueue.java b/src/main/java/ru/windcorp/progressia/common/util/TaskQueue.java new file mode 100644 index 0000000..62d208b --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/util/TaskQueue.java @@ -0,0 +1,111 @@ +/******************************************************************************* + * Progressia + * Copyright (C) 2020 Wind Corporation + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + *******************************************************************************/ +package ru.windcorp.progressia.common.util; + +import java.util.Iterator; +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.BooleanSupplier; + +import ru.windcorp.jputil.functions.ThrowingRunnable; + +public class TaskQueue { + + private final Queue queue = new ConcurrentLinkedQueue<>(); + private final BooleanSupplier runNow; + + public TaskQueue(BooleanSupplier runNow) { + this.runNow = runNow; + } + + public void invokeLater(Runnable task) { + queue.add(task); + } + + public void invokeNow(Runnable task) { + if (runNow.getAsBoolean()) { + task.run(); + } else { + invokeLater(task); + } + } + + private final Object waitAndInvokeMonitor = new Object(); + + @SuppressWarnings("unchecked") + public void waitAndInvoke( + ThrowingRunnable task + ) throws InterruptedException, E { + + if (runNow.getAsBoolean()) { + task.run(); + return; + } + + final AtomicBoolean flag = + new AtomicBoolean(false); + final AtomicReference thrownContainer = + new AtomicReference<>(null); + + invokeLater(() -> { + + try { + task.run(); + } catch (Throwable t) { + thrownContainer.set(t); + } + + flag.set(true); + + synchronized (waitAndInvokeMonitor) { + waitAndInvokeMonitor.notifyAll(); + } + }); + + while (!flag.get()) { + synchronized (waitAndInvokeMonitor) { + waitAndInvokeMonitor.wait(); + } + } + + Throwable thrown = thrownContainer.get(); + if (thrown != null) { + if (thrown instanceof RuntimeException) { + throw (RuntimeException) thrown; + } + + if (thrown instanceof Error) { + throw (Error) thrown; + } + + throw (E) thrown; // Guaranteed + } + } + + public void runTasks() { + Iterator tasks = queue.iterator(); + + while (tasks.hasNext()) { + tasks.next().run(); + tasks.remove(); + } + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/util/VectorUtil.java b/src/main/java/ru/windcorp/progressia/common/util/VectorUtil.java new file mode 100644 index 0000000..d84e2f1 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/util/VectorUtil.java @@ -0,0 +1,30 @@ +package ru.windcorp.progressia.common.util; + +import java.util.function.Consumer; + +import glm.vec._3.i.Vec3i; + +public class VectorUtil { + + public static void forEachVectorInCuboid( + int x0, int y0, int z0, + int x1, int y1, int z1, + Consumer action + ) { + Vec3i cursor = Vectors.grab3i(); + + for (int x = x0; x < x1; ++x) { + for (int y = y0; y < y1; ++y) { + for (int z = z0; z < z1; ++z) { + cursor.set(x, y, z); + action.accept(cursor); + } + } + } + + Vectors.release(cursor); + } + + private VectorUtil() {} + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java b/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java index 6e7cfed..ba2ae79 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java @@ -21,6 +21,7 @@ import static ru.windcorp.progressia.common.block.BlockFace.*; import java.util.ArrayList; import java.util.List; +import java.util.function.Consumer; import com.google.common.collect.Lists; @@ -31,11 +32,12 @@ import ru.windcorp.progressia.common.block.BlockFace; import ru.windcorp.progressia.common.block.TileData; import ru.windcorp.progressia.common.block.TileDataRegistry; import ru.windcorp.progressia.common.util.SizeLimitedList; +import ru.windcorp.progressia.common.util.VectorUtil; import ru.windcorp.progressia.common.util.Vectors; public class ChunkData { - public static final int BLOCKS_PER_CHUNK = 16; + public static final int BLOCKS_PER_CHUNK = Coordinates.CHUNK_SIZE; public static final int TILES_PER_FACE = 8; private final Vec3i position = new Vec3i(); @@ -239,6 +241,14 @@ public class ChunkData { (blockInChunk.z == min && face == BOTTOM) || (blockInChunk.z == max && face == TOP ); } + + public void forEachBlock(Consumer action) { + VectorUtil.forEachVectorInCuboid( + 0, 0, 0, + BLOCKS_PER_CHUNK, BLOCKS_PER_CHUNK, BLOCKS_PER_CHUNK, + action + ); + } public int getX() { return position.x; diff --git a/src/main/java/ru/windcorp/progressia/common/world/Coordinates.java b/src/main/java/ru/windcorp/progressia/common/world/Coordinates.java new file mode 100644 index 0000000..76a3f95 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/Coordinates.java @@ -0,0 +1,141 @@ +package ru.windcorp.progressia.common.world; + +import static ru.windcorp.progressia.common.world.ChunkData.BLOCKS_PER_CHUNK; + +import glm.vec._3.i.Vec3i; + +/** + * Contains static methods to convert world block coordinates, chunk block + * coordinates and chunk coordinates. + *

+ * Three types of coordinates are used in Progressia: + *

    + * + *
  • World coordinates, in code referred to as {@code blockInWorld} - + * coordinates relative to world origin. Every block in the world has unique + * world coordinates.
  • + * + *
  • Chunk coordinates, in code referred to as {@code blockInChunk} - + * coordinates relative some chunk's origin. Every block in the chunk has unique + * chunk coordinates, but blocks in different chunks may have identical chunk + * coordinates. These coordinates are only useful in combination with a chunk + * reference. Chunk coordinates are always [0; {@link #BLOCKS_PER_CHUNK}) + * .
  • + * + *
  • Coordinates of chunk, in code referred to as {@code chunk} - + * chunk coordinates relative to world origin. Every chunk in the world has + * unique coordinates of chunk.
  • + * + *
+ */ +public class Coordinates { + + public static final int BITS_IN_CHUNK_COORDS = 4; + public static final int CHUNK_SIZE = 1 << BITS_IN_CHUNK_COORDS; + public static final int CHUNK_COORDS_MASK = CHUNK_SIZE - 1; + + /** + * Computes the coordinate of the chunk that the block specified by the + * provided world coordinate belongs to. + * + * @param blockInWorld world coordinate of the block + * @return the corresponding coordinate of the chunk containing the block + * + * @see #convertInWorldToChunk(Vec3i, Vec3i) + */ + public static int convertInWorldToChunk(int blockInWorld) { + return blockInWorld >> BITS_IN_CHUNK_COORDS; + } + + /** + * Computes the coordinates of the chunk that the block specified by the + * provided world coordinates belongs to. + * + * @param blockInWorld world coordinates of the block + * @param output a {@link Vec3i} to store the result in + * @return {@code output} + * + * @see #convertInWorldToChunk(int) + */ + public static Vec3i convertInWorldToChunk( + Vec3i blockInWorld, + Vec3i output + ) { + output.x = convertInWorldToChunk(blockInWorld.x); + output.y = convertInWorldToChunk(blockInWorld.y); + output.z = convertInWorldToChunk(blockInWorld.z); + + return output; + } + + /** + * Computes the chunk coordinate that the block specified by the + * provided world coordinate has in its chunk. + * + * @param blockInWorld world coordinate of the block + * @return the corresponding chunk coordinate of the block + * + * @see #convertInWorldToInChunk(Vec3i, Vec3i) + */ + public static int convertInWorldToInChunk(int blockInWorld) { + return blockInWorld & CHUNK_COORDS_MASK; + } + + /** + * Computes the chunk coordinates that the block specified by the + * provided world coordinates has in its chunk. + * + * @param blockInWorld world coordinates of the block + * @param output a {@link Vec3i} to store the result in + * @return {@code output} + * + * @see #convertInWorldToInChunk(int) + */ + public static Vec3i convertInWorldToInChunk( + Vec3i blockInWorld, + Vec3i output + ) { + output.x = convertInWorldToInChunk(blockInWorld.x); + output.y = convertInWorldToInChunk(blockInWorld.y); + output.z = convertInWorldToInChunk(blockInWorld.z); + + return output; + } + + /** + * Computes the world coordinate of the block specified by its chunk + * coordinate and the coordinate of its chunk. + * + * @param chunk coordinate of chunk + * @param blockInChunk chunk coordinate of block + * @return corresponding world coordinate + * + * @see #getInWorld(int, int) + */ + public static int getInWorld(int chunk, int blockInChunk) { + return blockInChunk | (chunk << BITS_IN_CHUNK_COORDS); + } + + /** + * Computes the world coordinates of the block specified by its chunk + * coordinates and the coordinates of its chunk. + * + * @param chunk coordinate of chunk + * @param blockInChunk chunk coordinate of block + * @param output a {@link Vec3i} to store the result in + * @return {@code output} + * + * @see #getInWorld(int) + */ + public static Vec3i getInWorld( + Vec3i chunk, Vec3i blockInChunk, + Vec3i output + ) { + output.x = getInWorld(chunk.x, blockInChunk.x); + output.y = getInWorld(chunk.y, blockInChunk.y); + output.z = getInWorld(chunk.z, blockInChunk.z); + + return output; + } + +} 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 93113fc..6e35b8b 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/WorldData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/WorldData.java @@ -24,6 +24,7 @@ import glm.vec._3.i.Vec3i; import gnu.trove.map.TLongObjectMap; import gnu.trove.map.hash.TLongObjectHashMap; import ru.windcorp.progressia.common.util.CoordinatePacker; +import ru.windcorp.progressia.common.util.Vectors; public class WorldData { @@ -44,6 +45,14 @@ public class WorldData { return chunks.get(key); } + public ChunkData getChunkByBlock(Vec3i blockInWorld) { + Vec3i chunkPos = Vectors.grab3i(); + Coordinates.convertInWorldToChunk(blockInWorld, chunkPos); + ChunkData result = getChunk(chunkPos); + Vectors.release(chunkPos); + return result; + } + public Collection getChunks() { return Collections.unmodifiableCollection(chunks.valueCollection()); } diff --git a/src/main/java/ru/windcorp/progressia/server/Server.java b/src/main/java/ru/windcorp/progressia/server/Server.java new file mode 100644 index 0000000..66c81a8 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/Server.java @@ -0,0 +1,78 @@ +package ru.windcorp.progressia.server; + +import ru.windcorp.jputil.functions.ThrowingRunnable; +import ru.windcorp.progressia.common.util.TaskQueue; +import ru.windcorp.progressia.common.world.WorldData; +import ru.windcorp.progressia.server.comms.ClientManager; +import ru.windcorp.progressia.server.world.Changer; +import ru.windcorp.progressia.server.world.ImplementedChangeTracker; +import ru.windcorp.progressia.server.world.WorldLogic; + +public class Server { + + public static Server getCurrentServer() { + return ServerThread.getCurrentServer(); + } + + private final WorldLogic world; + private final ImplementedChangeTracker adHocChanger = + new ImplementedChangeTracker(); + + private final ServerThread serverThread; + + private final ClientManager clientManager = new ClientManager(this); + + private final TaskQueue taskQueue = new TaskQueue(this::isServerThread); + + public Server(WorldData world) { + this.world = new WorldLogic(world); + this.serverThread = new ServerThread(this); + } + + public WorldLogic getWorld() { + return world; + } + + /** + * Do not use in ticks + */ + public Changer getAdHocChanger() { + return adHocChanger; + } + + public ClientManager getClientManager() { + return clientManager; + } + + public boolean isServerThread() { + return getCurrentServer() == this; + } + + public void invokeLater(Runnable task) { + taskQueue.invokeLater(task); + } + + public void invokeNow(Runnable task) { + taskQueue.invokeNow(task); + } + + public void waitAndInvoke( + ThrowingRunnable task + ) throws InterruptedException, E { + taskQueue.waitAndInvoke(task); + } + + public void start() { + this.serverThread.start(); + } + + public void tick() { + taskQueue.runTasks(); + adHocChanger.applyChanges(this); + } + + public void shutdown(String message) { + // Do nothing + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/ServerState.java b/src/main/java/ru/windcorp/progressia/server/ServerState.java new file mode 100644 index 0000000..d8071f6 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/ServerState.java @@ -0,0 +1,25 @@ +package ru.windcorp.progressia.server; + +import ru.windcorp.progressia.common.world.WorldData; + +public class ServerState { + + private static Server instance = null; + + public static Server getInstance() { + return instance; + } + + public static void setInstance(Server instance) { + ServerState.instance = instance; + } + + public static void startServer() { + Server server = new Server(new WorldData()); + setInstance(server); + server.start(); + } + + private ServerState() {} + +} diff --git a/src/main/java/ru/windcorp/progressia/server/ServerThread.java b/src/main/java/ru/windcorp/progressia/server/ServerThread.java new file mode 100644 index 0000000..b6f7b17 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/ServerThread.java @@ -0,0 +1,61 @@ +package ru.windcorp.progressia.server; + +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; + +import ru.windcorp.progressia.server.world.Ticker; + +public class ServerThread implements Runnable { + + private static final ThreadLocal SERVER_THREADS_MAP = + new ThreadLocal<>(); + + public static Server getCurrentServer() { + return SERVER_THREADS_MAP.get(); + } + + private class ServerThreadTracker implements Runnable { + + private final Runnable payload; + + public ServerThreadTracker(Runnable payload) { + this.payload = payload; + } + + @Override + public void run() { + SERVER_THREADS_MAP.set(getServer()); + payload.run(); + } + + } + + private final Server server; + private final ScheduledExecutorService executor = + Executors.newSingleThreadScheduledExecutor( + r -> new Thread(new ServerThreadTracker(r), "Server thread") + ); + + private final Ticker ticker; + + public ServerThread(Server server) { + this.server = server; + this.ticker = new Ticker(server); + } + + public void start() { + executor.scheduleAtFixedRate(this, 0, 1000 / 20, TimeUnit.MILLISECONDS); + } + + @Override + public void run() { + server.tick(); + ticker.run(); + } + + public Server getServer() { + return server; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/block/BlockLogic.java b/src/main/java/ru/windcorp/progressia/server/block/BlockLogic.java new file mode 100644 index 0000000..df52c73 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/block/BlockLogic.java @@ -0,0 +1,11 @@ +package ru.windcorp.progressia.server.block; + +import ru.windcorp.progressia.common.util.Namespaced; + +public class BlockLogic extends Namespaced { + + public BlockLogic(String namespace, String name) { + super(namespace, name); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/block/BlockLogicRegistry.java b/src/main/java/ru/windcorp/progressia/server/block/BlockLogicRegistry.java new file mode 100644 index 0000000..9a9c59d --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/block/BlockLogicRegistry.java @@ -0,0 +1,18 @@ +package ru.windcorp.progressia.server.block; + +import java.util.HashMap; +import java.util.Map; + +public class BlockLogicRegistry { + + private static final Map REGISTRY = new HashMap<>(); + + public static BlockLogic get(String name) { + return REGISTRY.get(name); + } + + public static void register(BlockLogic blockLogic) { + REGISTRY.put(blockLogic.getId(), blockLogic); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/block/BlockTickContext.java b/src/main/java/ru/windcorp/progressia/server/block/BlockTickContext.java new file mode 100644 index 0000000..f5894ad --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/block/BlockTickContext.java @@ -0,0 +1,29 @@ +package ru.windcorp.progressia.server.block; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.block.BlockData; +import ru.windcorp.progressia.server.world.ChunkTickContext; + +public interface BlockTickContext extends ChunkTickContext { + + /** + * Returns the current world coordinates. + * @return the world coordinates of the block being ticked + */ + Vec3i getCoords(); + + /** + * Returns the current chunk coordinates. + * @return the chunk coordinates of the block being ticked + */ + Vec3i getChunkCoords(); + + default BlockLogic getBlock() { + return getChunk().getBlock(getChunkCoords()); + } + + default BlockData getBlockData() { + return getChunkData().getBlock(getChunkCoords()); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/block/ForwardingBlockTickContext.java b/src/main/java/ru/windcorp/progressia/server/block/ForwardingBlockTickContext.java new file mode 100644 index 0000000..e6870c4 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/block/ForwardingBlockTickContext.java @@ -0,0 +1,71 @@ +package ru.windcorp.progressia.server.block; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.block.BlockData; +import ru.windcorp.progressia.common.world.ChunkData; +import ru.windcorp.progressia.common.world.WorldData; +import ru.windcorp.progressia.server.Server; +import ru.windcorp.progressia.server.world.ChunkLogic; +import ru.windcorp.progressia.server.world.WorldLogic; + +public class ForwardingBlockTickContext { + + private BlockTickContext parent; + + public ForwardingBlockTickContext(BlockTickContext parent) { + setParent(parent); + } + + public BlockTickContext getParent() { + return parent; + } + + public void setParent(BlockTickContext parent) { + this.parent = parent; + } + + public ChunkLogic getChunk() { + return parent.getChunk(); + } + + public ChunkData getChunkData() { + return parent.getChunkData(); + } + + public double getTickLength() { + return parent.getTickLength(); + } + + public Server getServer() { + return parent.getServer(); + } + + public Vec3i getCoords() { + return parent.getCoords(); + } + + public WorldLogic getWorld() { + return parent.getWorld(); + } + + public WorldData getWorldData() { + return parent.getWorldData(); + } + + public Vec3i getChunkCoords() { + return parent.getChunkCoords(); + } + + public void requestBlockTick(Vec3i blockInWorld) { + parent.requestBlockTick(blockInWorld); + } + + public BlockLogic getBlock() { + return parent.getBlock(); + } + + public BlockData getBlockData() { + return parent.getBlockData(); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/block/Tickable.java b/src/main/java/ru/windcorp/progressia/server/block/Tickable.java new file mode 100644 index 0000000..4d1be93 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/block/Tickable.java @@ -0,0 +1,17 @@ +package ru.windcorp.progressia.server.block; + +import ru.windcorp.progressia.server.world.Changer; + +public interface Tickable { + + void tick(BlockTickContext context, Changer changer); + + default boolean doesTickRegularly(BlockTickContext context) { + return false; + } + + default boolean doesTickRandomly(BlockTickContext context) { + return false; + } + +} \ No newline at end of file diff --git a/src/main/java/ru/windcorp/progressia/server/block/Updatable.java b/src/main/java/ru/windcorp/progressia/server/block/Updatable.java new file mode 100644 index 0000000..e3ca677 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/block/Updatable.java @@ -0,0 +1,9 @@ +package ru.windcorp.progressia.server.block; + +import ru.windcorp.progressia.server.world.Changer; + +public interface Updatable { + + void update(BlockTickContext context, Changer changer); + +} diff --git a/src/main/java/ru/windcorp/progressia/server/comms/Client.java b/src/main/java/ru/windcorp/progressia/server/comms/Client.java new file mode 100644 index 0000000..94c2ed4 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/comms/Client.java @@ -0,0 +1,23 @@ +package ru.windcorp.progressia.server.comms; + +import ru.windcorp.progressia.common.comms.CommsChannel; + +public abstract class Client extends CommsChannel { + + private final int id; + + public Client(int id, Role... roles) { + super(roles); + this.id = id; + } + + public int getId() { + return id; + } + + @Override + public String toString() { + return getClass().getSimpleName() + " " + id; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java b/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java new file mode 100644 index 0000000..cfff2b0 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java @@ -0,0 +1,66 @@ +package ru.windcorp.progressia.server.comms; + +import java.util.Collection; +import java.util.Collections; +import java.util.concurrent.atomic.AtomicInteger; + +import gnu.trove.TCollections; +import gnu.trove.map.TIntObjectMap; +import gnu.trove.map.hash.TIntObjectHashMap; +import ru.windcorp.progressia.common.comms.CommsChannel.Role; +import ru.windcorp.progressia.common.comms.CommsChannel.State; +import ru.windcorp.progressia.common.comms.packets.Packet; +import ru.windcorp.progressia.server.Server; + +public class ClientManager { + + private final Server server; + + private final TIntObjectMap clientsById = + TCollections.synchronizedMap(new TIntObjectHashMap<>()); + + private final Collection clients = + Collections.unmodifiableCollection(clientsById.valueCollection()); + + private final AtomicInteger nextId = new AtomicInteger(0); + + public ClientManager(Server server) { + this.server = server; + } + + public int grabClientId() { + return nextId.getAndIncrement(); + } + + public void addClient(Client client) { + clientsById.put(client.getId(), client); + + client.addListener(new DefaultServerCommsListener(this, client)); + } + + public void disconnectClient(Client client) { + client.disconnect(); + clientsById.remove(client.getId()); + } + + public void broadcastGamePacket(Packet packet) { + getClients().forEach(c -> { + if (c.getState() != State.CONNECTED) return; + if (!c.getRoles().contains(Role.GAME)) return; + c.sendPacket(packet); + }); + } + + public Collection getClients() { + return clients; + } + + public Client getById(int id) { + return clientsById.get(id); + } + + public Server getServer() { + return server; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/comms/DefaultServerCommsListener.java b/src/main/java/ru/windcorp/progressia/server/comms/DefaultServerCommsListener.java new file mode 100644 index 0000000..dfd10cd --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/comms/DefaultServerCommsListener.java @@ -0,0 +1,40 @@ +package ru.windcorp.progressia.server.comms; + +import java.io.IOException; + +import ru.windcorp.progressia.common.comms.CommsChannel.Role; +import ru.windcorp.progressia.common.comms.controls.PacketControl; +import ru.windcorp.progressia.common.comms.CommsListener; +import ru.windcorp.progressia.common.comms.packets.Packet; +import ru.windcorp.progressia.server.comms.controls.ControlLogicRegistry; + +public class DefaultServerCommsListener implements CommsListener { + + private final ClientManager manager; + private final Client client; + + public DefaultServerCommsListener(ClientManager manager, Client client) { + this.manager = manager; + this.client = client; + } + + @Override + public void onPacketReceived(Packet packet) { + if (client.getRoles().contains(Role.GAME)) { + if (packet instanceof PacketControl) { + PacketControl packetControl = (PacketControl) packet; + + ControlLogicRegistry.getInstance().get( + packetControl.getControl().getId() + ).apply(manager.getServer(), packetControl, client); + } + } + } + + @Override + public void onIOError(IOException reason) { + // TODO Auto-generated method stub + + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/comms/controls/ControlLogic.java b/src/main/java/ru/windcorp/progressia/server/comms/controls/ControlLogic.java new file mode 100644 index 0000000..17ff7f5 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/comms/controls/ControlLogic.java @@ -0,0 +1,20 @@ +package ru.windcorp.progressia.server.comms.controls; + +import ru.windcorp.progressia.common.comms.controls.PacketControl; +import ru.windcorp.progressia.common.util.Namespaced; +import ru.windcorp.progressia.server.Server; +import ru.windcorp.progressia.server.comms.Client; + +public abstract class ControlLogic extends Namespaced { + + public ControlLogic(String namespace, String name) { + super(namespace, name); + } + + public abstract void apply( + Server server, + PacketControl packet, + Client client + ); + +} diff --git a/src/main/java/ru/windcorp/progressia/server/comms/controls/ControlLogicRegistry.java b/src/main/java/ru/windcorp/progressia/server/comms/controls/ControlLogicRegistry.java new file mode 100644 index 0000000..d37f328 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/comms/controls/ControlLogicRegistry.java @@ -0,0 +1,14 @@ +package ru.windcorp.progressia.server.comms.controls; + +import ru.windcorp.progressia.common.util.NamespacedRegistry; + +public class ControlLogicRegistry extends NamespacedRegistry { + + private static final ControlLogicRegistry INSTANCE = + new ControlLogicRegistry(); + + public static ControlLogicRegistry getInstance() { + return INSTANCE; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/world/Changer.java b/src/main/java/ru/windcorp/progressia/server/world/Changer.java new file mode 100644 index 0000000..21baae5 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/world/Changer.java @@ -0,0 +1,16 @@ +package ru.windcorp.progressia.server.world; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.block.BlockData; +import ru.windcorp.progressia.common.block.BlockFace; +import ru.windcorp.progressia.common.block.TileData; + +public interface Changer { + + void setBlock(Vec3i pos, BlockData block); + + void addTile(Vec3i block, BlockFace face, TileData tile); + + void removeTile(Vec3i block, BlockFace face, TileData tile); + +} \ No newline at end of file diff --git a/src/main/java/ru/windcorp/progressia/server/world/ChunkLogic.java b/src/main/java/ru/windcorp/progressia/server/world/ChunkLogic.java new file mode 100644 index 0000000..58955b6 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/world/ChunkLogic.java @@ -0,0 +1,69 @@ +package ru.windcorp.progressia.server.world; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.function.BiConsumer; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.world.ChunkData; +import ru.windcorp.progressia.server.block.BlockLogic; +import ru.windcorp.progressia.server.block.BlockLogicRegistry; +import ru.windcorp.progressia.server.block.Tickable; + +public class ChunkLogic { + + private final WorldLogic world; + private final ChunkData data; + + private final Collection ticking = new ArrayList<>(); + + public ChunkLogic(WorldLogic world, ChunkData data) { + this.world = world; + this.data = data; + + generateTickList(); + } + + private void generateTickList() { + MutableBlockTickContext blockTickContext = + new MutableBlockTickContext(); + + blockTickContext.setWorld(getWorld()); + blockTickContext.setChunk(this); + + data.forEachBlock(blockInChunk -> { + BlockLogic block = getBlock(blockInChunk); + + if (block instanceof Tickable) { + blockTickContext.setCoordsInChunk(blockInChunk); + + if (((Tickable) block).doesTickRegularly(blockTickContext)) { + ticking.add(new Vec3i(blockInChunk)); + } + } + }); + } + + public WorldLogic getWorld() { + return world; + } + + public ChunkData getData() { + return data; + } + + public boolean hasTickingBlocks() { + return ticking.isEmpty(); + } + + public void forEachTickingBlock(BiConsumer action) { + ticking.forEach(blockInChunk -> { + action.accept(blockInChunk, getBlock(blockInChunk)); + }); + } + + public BlockLogic getBlock(Vec3i blockInChunk) { + return BlockLogicRegistry.get(getData().getBlock(blockInChunk).getId()); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/world/ChunkTickContext.java b/src/main/java/ru/windcorp/progressia/server/world/ChunkTickContext.java new file mode 100644 index 0000000..e995974 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/world/ChunkTickContext.java @@ -0,0 +1,13 @@ +package ru.windcorp.progressia.server.world; + +import ru.windcorp.progressia.common.world.ChunkData; + +public interface ChunkTickContext extends TickContext { + + ChunkLogic getChunk(); + + default ChunkData getChunkData() { + return getChunk().getData(); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/world/ImplementedChangeTracker.java b/src/main/java/ru/windcorp/progressia/server/world/ImplementedChangeTracker.java new file mode 100644 index 0000000..2540a53 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/world/ImplementedChangeTracker.java @@ -0,0 +1,155 @@ +package ru.windcorp.progressia.server.world; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.block.BlockData; +import ru.windcorp.progressia.common.block.BlockFace; +import ru.windcorp.progressia.common.block.TileData; +import ru.windcorp.progressia.common.comms.packets.Packet; +import ru.windcorp.progressia.common.comms.packets.PacketWorldChange; +import ru.windcorp.progressia.common.util.LowOverheadCache; +import ru.windcorp.progressia.common.util.Vectors; +import ru.windcorp.progressia.common.world.Coordinates; +import ru.windcorp.progressia.common.world.WorldData; +import ru.windcorp.progressia.server.Server; + +public class ImplementedChangeTracker implements Changer { + + public static interface Change { + void applyOnServer(WorldData world); + Packet asPacket(); + } + + private static class SetBlock + extends PacketWorldChange + implements Change { + + private final Vec3i position = new Vec3i(); + private BlockData block; + + public SetBlock() { + super("Core", "SetBlock"); + } + + public void initialize(Vec3i position, BlockData block) { + this.position.set(position.x, position.y, position.z); + this.block = block; + } + + @Override + public void applyOnServer(WorldData world) { + Vec3i blockInChunk = Vectors.grab3i(); + Coordinates.convertInWorldToInChunk(position, blockInChunk); + + world.getChunkByBlock(position).setBlock(blockInChunk, block); + + Vectors.release(blockInChunk); + } + + @Override + public void apply(WorldData world) { + applyOnServer(world); + } + + @Override + public Packet asPacket() { + return this; + } + + } + + private static class AddOrRemoveTile + extends PacketWorldChange + implements Change { + + private final Vec3i position = new Vec3i(); + private BlockFace face; + private TileData tile; + + private boolean shouldAdd; + + public AddOrRemoveTile() { + super("Core", "AddOrRemoveTile"); + } + + public void initialize( + Vec3i position, BlockFace face, + TileData tile, + boolean shouldAdd + ) { + this.position.set(position.x, position.y, position.z); + this.face = face; + this.tile = tile; + this.shouldAdd = shouldAdd; + } + + @Override + public void applyOnServer(WorldData world) { + Vec3i blockInChunk = Vectors.grab3i(); + Coordinates.convertInWorldToInChunk(position, blockInChunk); + + List tiles = world.getChunkByBlock(position) + .getTiles(blockInChunk, face); + + if (shouldAdd) { + tiles.add(tile); + } else { + tiles.remove(tile); + } + + Vectors.release(blockInChunk); + } + + @Override + public void apply(WorldData world) { + applyOnServer(world); + } + + @Override + public Packet asPacket() { + return this; + } + + } + + private final List changes = new ArrayList<>(1024); + + private final LowOverheadCache setBlockCache = + new LowOverheadCache<>(SetBlock::new); + + private final LowOverheadCache addOrRemoveTileCache = + new LowOverheadCache<>(AddOrRemoveTile::new); + + @Override + public void setBlock(Vec3i pos, BlockData block) { + SetBlock change = setBlockCache.grab(); + change.initialize(pos, block); + changes.add(change); + } + + @Override + public void addTile(Vec3i block, BlockFace face, TileData tile) { + AddOrRemoveTile change = addOrRemoveTileCache.grab(); + change.initialize(block, face, tile, true); + changes.add(change); + } + + @Override + public void removeTile(Vec3i block, BlockFace face, TileData tile) { + AddOrRemoveTile change = addOrRemoveTileCache.grab(); + change.initialize(block, face, tile, false); + changes.add(change); + } + + public void applyChanges(Server server) { + changes.forEach(c -> c.applyOnServer(server.getWorld().getData())); + changes.stream().map(Change::asPacket).filter(Objects::nonNull).forEach( + server.getClientManager()::broadcastGamePacket + ); + changes.clear(); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/world/MutableBlockTickContext.java b/src/main/java/ru/windcorp/progressia/server/world/MutableBlockTickContext.java new file mode 100644 index 0000000..6b27f2b --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/world/MutableBlockTickContext.java @@ -0,0 +1,91 @@ +package ru.windcorp.progressia.server.world; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.util.Vectors; +import ru.windcorp.progressia.common.world.Coordinates; +import ru.windcorp.progressia.server.Server; +import ru.windcorp.progressia.server.block.BlockTickContext; + +public class MutableBlockTickContext implements BlockTickContext { + + private double tickLength; + + private Server server; + private WorldLogic world; + + private ChunkLogic chunk; + + private final Vec3i blockInWorld = new Vec3i(); + private final Vec3i blockInChunk = new Vec3i(); + + public double getTickLength() { + return tickLength; + } + + public void setTickLength(double tickLength) { + this.tickLength = tickLength; + } + + @Override + public Server getServer() { + return server; + } + + public void setServer(Server server) { + this.server = server; + setWorld(server.getWorld()); + } + + @Override + public WorldLogic getWorld() { + return world; + } + + public void setWorld(WorldLogic world) { + this.world = world; + } + + @Override + public ChunkLogic getChunk() { + return chunk; + } + + public void setChunk(ChunkLogic chunk) { + this.chunk = chunk; + } + + @Override + public Vec3i getCoords() { + return this.blockInWorld; + } + + @Override + public Vec3i getChunkCoords() { + return this.blockInChunk; + } + + public void setCoordsInWorld(Vec3i coords) { + getCoords().set(coords.x, coords.y, coords.z); + Coordinates.convertInWorldToInChunk(getCoords(), getChunkCoords()); + + Vec3i chunk = Vectors.grab3i(); + Coordinates.convertInWorldToChunk(coords, chunk); + setChunk(getWorld().getChunk(chunk)); + Vectors.release(chunk); + } + + public void setCoordsInChunk(Vec3i coords) { + getChunkCoords().set(coords.x, coords.y, coords.z); + Coordinates.getInWorld( + getChunkData().getPosition(), getChunkCoords(), + getCoords() + ); + } + + @Override + public void requestBlockTick(Vec3i blockInWorld) { + // TODO implement + throw new UnsupportedOperationException("Not yet implemented"); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/world/TickContext.java b/src/main/java/ru/windcorp/progressia/server/world/TickContext.java new file mode 100644 index 0000000..d572fec --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/world/TickContext.java @@ -0,0 +1,23 @@ +package ru.windcorp.progressia.server.world; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.world.WorldData; +import ru.windcorp.progressia.server.Server; + +public interface TickContext { + + double getTickLength(); + + Server getServer(); + + default WorldLogic getWorld() { + return getServer().getWorld(); + } + + default WorldData getWorldData() { + return getWorld().getData(); + } + + void requestBlockTick(Vec3i blockInWorld); + +} diff --git a/src/main/java/ru/windcorp/progressia/server/world/Ticker.java b/src/main/java/ru/windcorp/progressia/server/world/Ticker.java new file mode 100644 index 0000000..478ab7a --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/world/Ticker.java @@ -0,0 +1,61 @@ +package ru.windcorp.progressia.server.world; + +import ru.windcorp.progressia.server.Server; +import ru.windcorp.progressia.server.block.Tickable; + +public class Ticker implements Runnable { + + private final ImplementedChangeTracker tracker = + new ImplementedChangeTracker(); + + private final MutableBlockTickContext blockTickContext = + new MutableBlockTickContext(); + + private final Server server; + + public Ticker(Server server) { + this.server = server; + } + + @Override + public void run() { + this.blockTickContext.setTickLength(1000 / 20); + server.getWorld().getChunks().forEach(chunk -> { + tickChunk(chunk); + }); + } + + private void tickChunk(ChunkLogic chunk) { + MutableBlockTickContext context = this.blockTickContext; + + context.setServer(server); + context.setChunk(chunk); + + tickRegularBlocks(chunk, context); + tickRandomBlocks(chunk, context); + + flushChanges(chunk); + } + + private void tickRegularBlocks( + ChunkLogic chunk, + MutableBlockTickContext context + ) { + chunk.forEachTickingBlock((blockInChunk, block) -> { + context.setCoordsInChunk(blockInChunk); + ((Tickable) block).tick(context, tracker); + }); + } + + private void tickRandomBlocks( + ChunkLogic chunk, + MutableBlockTickContext context + ) { + // TODO implement + } + + private void flushChanges(ChunkLogic chunk) { + this.tracker.applyChanges(server); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/world/WorldLogic.java b/src/main/java/ru/windcorp/progressia/server/world/WorldLogic.java new file mode 100644 index 0000000..2aae605 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/world/WorldLogic.java @@ -0,0 +1,41 @@ +package ru.windcorp.progressia.server.world; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.world.ChunkData; +import ru.windcorp.progressia.common.world.WorldData; + +public class WorldLogic { + + private final WorldData data; + + private final Map chunks = new HashMap<>(); + + public WorldLogic(WorldData data) { + this.data = data; + + for (ChunkData chunkData : data.getChunks()) { + chunks.put(chunkData, new ChunkLogic(this, chunkData)); + } + } + + public WorldData getData() { + return data; + } + + public ChunkLogic getChunk(ChunkData chunkData) { + return chunks.get(chunkData); + } + + public ChunkLogic getChunk(Vec3i pos) { + return chunks.get(getData().getChunk(pos)); + } + + public Collection getChunks() { + return chunks.values(); + } + +}