Added server and reorganized client
- Added server - Managed with ServerState - Added BlockLogic (no TileLogic yet) - Added ticking - Added server-client communication API (only local implemented, no net) - Added controls system (not fully implemented) - Added Client class - Contains most game-related references for client: WorldRender, Camera, ... - Managed with ClientState fjckign finally~
This commit is contained in:
parent
e8f3177266
commit
30e61cf33f
41
src/main/java/ru/windcorp/progressia/client/Client.java
Normal file
41
src/main/java/ru/windcorp/progressia/client/Client.java
Normal file
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
40
src/main/java/ru/windcorp/progressia/client/ClientState.java
Normal file
40
src/main/java/ru/windcorp/progressia/client/ClientState.java
Normal file
@ -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() {}
|
||||
|
||||
}
|
101
src/main/java/ru/windcorp/progressia/client/TestContent.java
Normal file
101
src/main/java/ru/windcorp/progressia/client/TestContent.java
Normal file
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
|
||||
}
|
@ -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<KeyEvent> predicate;
|
||||
private final PacketControl packet;
|
||||
|
||||
public ControlTriggerOnKeyPress(
|
||||
String namespace, String name,
|
||||
Predicate<KeyEvent> 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;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package ru.windcorp.progressia.client.comms.controls;
|
||||
|
||||
import ru.windcorp.progressia.common.util.NamespacedRegistry;
|
||||
|
||||
public class ControlTriggerRegistry extends NamespacedRegistry<ControlTrigger> {
|
||||
|
||||
private static final ControlTriggerRegistry INSTANCE =
|
||||
new ControlTriggerRegistry();
|
||||
|
||||
public static ControlTriggerRegistry getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
}
|
||||
|
||||
}
|
@ -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<Runnable> 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 <E extends Exception> void waitAndInvoke(
|
||||
ThrowingRunnable<E> task
|
||||
) throws InterruptedException, E {
|
||||
|
||||
if (GraphicsInterface.isRenderThread()) {
|
||||
task.run();
|
||||
return;
|
||||
}
|
||||
|
||||
final AtomicBoolean flag =
|
||||
new AtomicBoolean(false);
|
||||
final AtomicReference<Throwable> 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
|
||||
}
|
||||
HANDLER.waitAndInvoke(task);
|
||||
}
|
||||
|
||||
public static void runTasks() {
|
||||
Iterator<Runnable> tasks = QUEUE.iterator();
|
||||
|
||||
while (tasks.hasNext()) {
|
||||
tasks.next().run();
|
||||
tasks.remove();
|
||||
}
|
||||
HANDLER.runTasks();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -43,6 +43,8 @@ class RenderThread extends Thread {
|
||||
waitForFrame();
|
||||
GraphicsBackend.endFrame();
|
||||
}
|
||||
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
private void render() {
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
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"));
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
world.getChunk(chunkPos).markForUpdate();
|
||||
|
||||
Vectors.release(pos);
|
||||
Vectors.release(chunkPos);
|
||||
|
||||
break;
|
||||
}
|
||||
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)
|
||||
);
|
||||
|
@ -34,23 +34,8 @@ 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),
|
||||
|
@ -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),
|
||||
|
@ -24,13 +24,6 @@ public class BlockDataRegistry {
|
||||
|
||||
private static final Map<String, BlockData> 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);
|
||||
}
|
||||
|
@ -24,13 +24,6 @@ public class TileDataRegistry {
|
||||
|
||||
private static final Map<String, TileData> 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);
|
||||
}
|
||||
|
@ -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<Role> roles;
|
||||
|
||||
private final Collection<CommsListener> 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<Role> getRoles() {
|
||||
return roles;
|
||||
}
|
||||
|
||||
public boolean isReady() {
|
||||
return getState() == State.CONNECTED;
|
||||
}
|
||||
|
||||
public synchronized void setState(State state) {
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,13 @@
|
||||
package ru.windcorp.progressia.common.comms.controls;
|
||||
|
||||
import ru.windcorp.progressia.common.util.NamespacedRegistry;
|
||||
|
||||
public class ControlDataRegistry extends NamespacedRegistry<ControlData> {
|
||||
|
||||
private static final ControlDataRegistry INSTANCE = new ControlDataRegistry();
|
||||
|
||||
public static ControlDataRegistry getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
|
||||
}
|
@ -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<E extends Namespaced>
|
||||
implements Map<String, E> {
|
||||
|
||||
private final Map<String, E> backingMap =
|
||||
Collections.synchronizedMap(new HashMap<>());
|
||||
|
||||
public void register(E element) {
|
||||
backingMap.put(element.getId(), element);
|
||||
}
|
||||
|
||||
public void registerAll(Collection<? extends E> 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<? extends String, ? extends E> m) {
|
||||
throw new UnsupportedOperationException(
|
||||
"Use NamespacedRegistry.registerAll(Collection<? extends E>)"
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
backingMap.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> keySet() {
|
||||
return backingMap.keySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<E> values() {
|
||||
return backingMap.values();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Entry<String, E>> entrySet() {
|
||||
return backingMap.entrySet();
|
||||
}
|
||||
|
||||
}
|
111
src/main/java/ru/windcorp/progressia/common/util/TaskQueue.java
Normal file
111
src/main/java/ru/windcorp/progressia/common/util/TaskQueue.java
Normal file
@ -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 <https://www.gnu.org/licenses/>.
|
||||
*******************************************************************************/
|
||||
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<Runnable> 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 <E extends Exception> void waitAndInvoke(
|
||||
ThrowingRunnable<E> task
|
||||
) throws InterruptedException, E {
|
||||
|
||||
if (runNow.getAsBoolean()) {
|
||||
task.run();
|
||||
return;
|
||||
}
|
||||
|
||||
final AtomicBoolean flag =
|
||||
new AtomicBoolean(false);
|
||||
final AtomicReference<Throwable> 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<Runnable> tasks = queue.iterator();
|
||||
|
||||
while (tasks.hasNext()) {
|
||||
tasks.next().run();
|
||||
tasks.remove();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -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<Vec3i> 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() {}
|
||||
|
||||
}
|
@ -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();
|
||||
@ -240,6 +242,14 @@ public class ChunkData {
|
||||
(blockInChunk.z == max && face == TOP );
|
||||
}
|
||||
|
||||
public void forEachBlock(Consumer<Vec3i> action) {
|
||||
VectorUtil.forEachVectorInCuboid(
|
||||
0, 0, 0,
|
||||
BLOCKS_PER_CHUNK, BLOCKS_PER_CHUNK, BLOCKS_PER_CHUNK,
|
||||
action
|
||||
);
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return position.x;
|
||||
}
|
||||
|
@ -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.
|
||||
* <p>
|
||||
* Three types of coordinates are used in Progressia:
|
||||
* <ul>
|
||||
*
|
||||
* <li><em>World coordinates</em>, in code referred to as {@code blockInWorld} -
|
||||
* coordinates relative to world origin. Every block in the world has unique
|
||||
* world coordinates.</li>
|
||||
*
|
||||
* <li><em>Chunk coordinates</em>, 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 <tt>[0; {@link #BLOCKS_PER_CHUNK})
|
||||
* </tt>.</li>
|
||||
*
|
||||
* <li><em>Coordinates of chunk</em>, in code referred to as {@code chunk} -
|
||||
* chunk coordinates relative to world origin. Every chunk in the world has
|
||||
* unique coordinates of chunk.</li>
|
||||
*
|
||||
* </ul>
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
}
|
@ -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<ChunkData> getChunks() {
|
||||
return Collections.unmodifiableCollection(chunks.valueCollection());
|
||||
}
|
||||
|
78
src/main/java/ru/windcorp/progressia/server/Server.java
Normal file
78
src/main/java/ru/windcorp/progressia/server/Server.java
Normal file
@ -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 <E extends Exception> void waitAndInvoke(
|
||||
ThrowingRunnable<E> 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
|
||||
}
|
||||
|
||||
}
|
25
src/main/java/ru/windcorp/progressia/server/ServerState.java
Normal file
25
src/main/java/ru/windcorp/progressia/server/ServerState.java
Normal file
@ -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() {}
|
||||
|
||||
}
|
@ -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> 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;
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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<String, BlockLogic> REGISTRY = new HashMap<>();
|
||||
|
||||
public static BlockLogic get(String name) {
|
||||
return REGISTRY.get(name);
|
||||
}
|
||||
|
||||
public static void register(BlockLogic blockLogic) {
|
||||
REGISTRY.put(blockLogic.getId(), blockLogic);
|
||||
}
|
||||
|
||||
}
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
@ -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<Client> clientsById =
|
||||
TCollections.synchronizedMap(new TIntObjectHashMap<>());
|
||||
|
||||
private final Collection<Client> 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<Client> getClients() {
|
||||
return clients;
|
||||
}
|
||||
|
||||
public Client getById(int id) {
|
||||
return clientsById.get(id);
|
||||
}
|
||||
|
||||
public Server getServer() {
|
||||
return server;
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
|
||||
}
|
||||
|
||||
}
|
@ -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
|
||||
);
|
||||
|
||||
}
|
@ -0,0 +1,14 @@
|
||||
package ru.windcorp.progressia.server.comms.controls;
|
||||
|
||||
import ru.windcorp.progressia.common.util.NamespacedRegistry;
|
||||
|
||||
public class ControlLogicRegistry extends NamespacedRegistry<ControlLogic> {
|
||||
|
||||
private static final ControlLogicRegistry INSTANCE =
|
||||
new ControlLogicRegistry();
|
||||
|
||||
public static ControlLogicRegistry getInstance() {
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
|
||||
}
|
@ -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<Vec3i> 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<Vec3i, BlockLogic> action) {
|
||||
ticking.forEach(blockInChunk -> {
|
||||
action.accept(blockInChunk, getBlock(blockInChunk));
|
||||
});
|
||||
}
|
||||
|
||||
public BlockLogic getBlock(Vec3i blockInChunk) {
|
||||
return BlockLogicRegistry.get(getData().getBlock(blockInChunk).getId());
|
||||
}
|
||||
|
||||
}
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
@ -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<TileData> 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<Change> changes = new ArrayList<>(1024);
|
||||
|
||||
private final LowOverheadCache<SetBlock> setBlockCache =
|
||||
new LowOverheadCache<>(SetBlock::new);
|
||||
|
||||
private final LowOverheadCache<AddOrRemoveTile> 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();
|
||||
}
|
||||
|
||||
}
|
@ -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");
|
||||
}
|
||||
|
||||
}
|
@ -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);
|
||||
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
@ -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<ChunkData, ChunkLogic> 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<ChunkLogic> getChunks() {
|
||||
return chunks.values();
|
||||
}
|
||||
|
||||
}
|
Reference in New Issue
Block a user