diff --git a/.gitignore b/.gitignore index 9edc0b1..0d32c6c 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,4 @@ bin # Ignore MacOS **/.DS_Store .idea/ +run/ diff --git a/README.md b/README.md index 3c60656..a5b54ce 100644 --- a/README.md +++ b/README.md @@ -43,3 +43,4 @@ plugins { * Trove4j * java-graphics/glm - GLM ported to Java. _Maven Central contains an outdated version, a custom repository used instead_ * Apache Commons Math (_not currently used_) +* log4j diff --git a/build.gradle b/build.gradle index 4a47ffe..618a35b 100644 --- a/build.gradle +++ b/build.gradle @@ -17,7 +17,11 @@ dependencies { implementation 'ru.windcorp.fork.io.github.java-graphics:glm:1.0.1' - testImplementation 'junit:junit:4.12' + // log4j + compile group: 'org.apache.logging.log4j', name: 'log4j-api', version: '2.13.3' + compile group: 'org.apache.logging.log4j', name: 'log4j-core', version: '2.13.3' + + testImplementation 'junit:junit:4.12' } /* @@ -70,4 +74,6 @@ dependencies { runtimeOnly "org.lwjgl:lwjgl-par::$lwjglNatives" runtimeOnly "org.lwjgl:lwjgl-stb::$lwjglNatives" if (lwjglNatives == "natives-macos") runtimeOnly "org.lwjgl:lwjgl-vulkan::$lwjglNatives" -} \ No newline at end of file +} + +// LWJGL END \ No newline at end of file diff --git a/pictures/jetbrains_ide.png b/pictures/jetbrains_ide.png new file mode 100644 index 0000000..75706e6 Binary files /dev/null and b/pictures/jetbrains_ide.png differ diff --git a/src/main/java/ru/windcorp/jputil/functions/FloatSupplier.java b/src/main/java/ru/windcorp/jputil/functions/FloatSupplier.java new file mode 100644 index 0000000..060b06a --- /dev/null +++ b/src/main/java/ru/windcorp/jputil/functions/FloatSupplier.java @@ -0,0 +1,8 @@ +package ru.windcorp.jputil.functions; + +@FunctionalInterface +public interface FloatSupplier { + + float getAsFloat(); + +} diff --git a/src/main/java/ru/windcorp/progressia/client/Client.java b/src/main/java/ru/windcorp/progressia/client/Client.java index b257e58..de6fa54 100644 --- a/src/main/java/ru/windcorp/progressia/client/Client.java +++ b/src/main/java/ru/windcorp/progressia/client/Client.java @@ -1,21 +1,18 @@ 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; +import ru.windcorp.progressia.common.world.entity.EntityData; public class Client { private final WorldRender world; + private EntityData localPlayer; - 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 Camera camera = new Camera((float) Math.toRadians(70)); private final ServerCommsChannel comms; @@ -30,6 +27,14 @@ public class Client { return world; } + public EntityData getLocalPlayer() { + return localPlayer; + } + + public void setLocalPlayer(EntityData localPlayer) { + this.localPlayer = localPlayer; + } + public Camera getCamera() { return camera; } diff --git a/src/main/java/ru/windcorp/progressia/client/ClientState.java b/src/main/java/ru/windcorp/progressia/client/ClientState.java index 1a3eb73..84c26a2 100644 --- a/src/main/java/ru/windcorp/progressia/client/ClientState.java +++ b/src/main/java/ru/windcorp/progressia/client/ClientState.java @@ -23,9 +23,14 @@ public class ClientState { public static void connectToLocalServer() { WorldData world = new WorldData(); - Client client = new Client(world, new LocalServerCommsChannel( + + LocalServerCommsChannel channel = new LocalServerCommsChannel( ServerState.getInstance() - )); + ); + + Client client = new Client(world, channel); + + channel.connect(); setInstance(client); diff --git a/src/main/java/ru/windcorp/progressia/client/TestContent.java b/src/main/java/ru/windcorp/progressia/client/TestContent.java index b866d76..d49b081 100644 --- a/src/main/java/ru/windcorp/progressia/client/TestContent.java +++ b/src/main/java/ru/windcorp/progressia/client/TestContent.java @@ -6,26 +6,22 @@ import static ru.windcorp.progressia.client.world.tile.TileRenderRegistry.getTil 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.comms.controls.*; import ru.windcorp.progressia.client.graphics.input.KeyMatcher; import ru.windcorp.progressia.client.world.block.*; -import ru.windcorp.progressia.client.world.tile.TileRender; -import ru.windcorp.progressia.client.world.tile.TileRenderGrass; -import ru.windcorp.progressia.client.world.tile.TileRenderRegistry; -import ru.windcorp.progressia.client.world.tile.TileRenderSimple; -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.client.world.entity.*; +import ru.windcorp.progressia.client.world.tile.*; +import ru.windcorp.progressia.common.comms.controls.*; import ru.windcorp.progressia.common.world.ChunkData; import ru.windcorp.progressia.common.world.block.*; -import ru.windcorp.progressia.common.world.tile.TileData; -import ru.windcorp.progressia.common.world.tile.TileDataRegistry; +import ru.windcorp.progressia.common.world.entity.*; +import ru.windcorp.progressia.common.world.tile.*; import ru.windcorp.progressia.server.Server; import ru.windcorp.progressia.server.comms.Client; -import ru.windcorp.progressia.server.comms.controls.ControlLogic; -import ru.windcorp.progressia.server.comms.controls.ControlLogicRegistry; +import ru.windcorp.progressia.server.comms.controls.*; import ru.windcorp.progressia.server.world.block.*; +import ru.windcorp.progressia.server.world.entity.*; +import ru.windcorp.progressia.server.world.tile.*; public class TestContent { @@ -37,6 +33,7 @@ public class TestContent { private static void registerWorldContent() { registerBlocks(); registerTiles(); + registerEntities(); } private static void registerBlocks() { @@ -64,15 +61,25 @@ public class TestContent { private static void registerTiles() { register(new TileData("Test", "Grass")); register(new TileRenderGrass("Test", "Grass", getTileTexture("grass_top"), getTileTexture("grass_side"))); + register(new TileLogic("Test", "Grass")); register(new TileData("Test", "Stones")); register(new TileRenderSimple("Test", "Stones", getTileTexture("stones"))); + register(new TileLogic("Test", "Stones")); register(new TileData("Test", "YellowFlowers")); register(new TileRenderSimple("Test", "YellowFlowers", getTileTexture("yellow_flowers"))); + register(new TileLogic("Test", "YellowFlowers")); register(new TileData("Test", "Sand")); register(new TileRenderSimple("Test", "Sand", getTileTexture("sand"))); + register(new TileLogic("Test", "Sand")); + } + + private static void registerEntities() { + register(new EntityData("Test", "Javapony")); + register(new TestEntityRenderJavapony()); + register(new EntityLogic("Test", "Javapony")); } private static void regsiterControls() { @@ -105,6 +112,10 @@ public class TestContent { TileDataRegistry.getInstance().register(x); } + private static void register(EntityData x) { + EntityDataRegistry.getInstance().register(x); + } + private static void register(BlockRender x) { BlockRenderRegistry.getInstance().register(x); } @@ -113,12 +124,20 @@ public class TestContent { TileRenderRegistry.getInstance().register(x); } + private static void register(EntityRender x) { + EntityRenderRegistry.getInstance().register(x); + } + private static void register(BlockLogic x) { BlockLogicRegistry.getInstance().register(x); } -// private static void register(TileRender x) { -// TileLogicRegistry.getInstance().register(x); -// } + private static void register(TileLogic x) { + TileLogicRegistry.getInstance().register(x); + } + + private static void register(EntityLogic x) { + EntityLogicRegistry.getInstance().register(x); + } } diff --git a/src/main/java/ru/windcorp/progressia/client/TestEntityRenderJavapony.java b/src/main/java/ru/windcorp/progressia/client/TestEntityRenderJavapony.java new file mode 100644 index 0000000..b1e8a92 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/TestEntityRenderJavapony.java @@ -0,0 +1,342 @@ +package ru.windcorp.progressia.client; + +import java.util.ArrayList; +import java.util.List; + +import glm.vec._3.Vec3; +import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface; +import ru.windcorp.progressia.client.graphics.backend.Usage; +import ru.windcorp.progressia.client.graphics.model.Face; +import ru.windcorp.progressia.client.graphics.model.Faces; +import ru.windcorp.progressia.client.graphics.model.LambdaModel; +import ru.windcorp.progressia.client.graphics.model.Renderable; +import ru.windcorp.progressia.client.graphics.model.Shape; +import ru.windcorp.progressia.client.graphics.model.Shapes.PppBuilder; +import ru.windcorp.progressia.client.graphics.model.StaticModel; +import ru.windcorp.progressia.client.graphics.texture.ComplexTexture; +import ru.windcorp.progressia.client.graphics.texture.Texture; +import ru.windcorp.progressia.client.graphics.world.WorldRenderProgram; +import ru.windcorp.progressia.client.world.entity.EntityRender; +import ru.windcorp.progressia.client.world.entity.EntityRenderRegistry; +import ru.windcorp.progressia.client.world.entity.EntityRenderable; +import ru.windcorp.progressia.client.world.entity.QuadripedModel; +import ru.windcorp.progressia.common.world.block.BlockFace; +import ru.windcorp.progressia.common.world.entity.EntityData; + +public class TestEntityRenderJavapony extends EntityRender { + + private final Renderable body; + private final Renderable head; + private final Renderable leftForeLeg; + private final Renderable leftHindLeg; + private final Renderable rightForeLeg; + private final Renderable rightHindLeg; + + public TestEntityRenderJavapony() { + super("Test", "Javapony"); + + ComplexTexture texture = new ComplexTexture( + EntityRenderRegistry.getEntityTexture("javapony"), + 256, 128 + ); + + this.body = createBody(texture); + this.head = createHead(texture); + this.leftForeLeg = createLeg(texture, 160, 0, true); + this.rightForeLeg = createLeg(texture, 160, 0, false); + this.leftHindLeg = createLeg(texture, 0, 0, true); + this.rightHindLeg = createLeg(texture, 0, 0, false); + } + + private static Renderable createBody(ComplexTexture texture) { + LambdaModel.Builder b = LambdaModel.lambdaBuilder(); + + b.addStaticPart(createMainBody(texture)); + + Texture tailStartTexture = texture.get(128, 96, 8, 32); + + b.addStaticPart( + new PppBuilder( + WorldRenderProgram.getDefault(), + BlockFace.mapToFaces( + tailStartTexture, tailStartTexture, + tailStartTexture, tailStartTexture, + tailStartTexture, tailStartTexture + ) + ) + .setOrigin(-60, -4, 14) + .setDepth(32, 0, -16).setWidth(8).setHeight(8) + .create() + ); + + Texture neckTexture = texture.get(0, 48, 16, 16); + + b.addStaticPart( + new PppBuilder( + WorldRenderProgram.getDefault(), + BlockFace.mapToFaces( + neckTexture, neckTexture, neckTexture, + neckTexture, neckTexture, neckTexture + ) + ) + .setOrigin(0, -8, 8) + .setWidth(16).setDepth(16).setHeight(2, 0, 16) + .create() + ); + + b.addDynamicPart( + createTail(texture), + m -> m + .translate(-60, 0, 24) + .rotateX(0.05f * Math.sin(GraphicsInterface.getTime())) + .rotateY(0.05f * Math.sin(Math.PI / 3 * GraphicsInterface.getTime())) + ); + + return new LambdaModel(b); + } + + private static Renderable createMainBody(ComplexTexture texture) { + WorldRenderProgram program = WorldRenderProgram.getDefault(); + List faces = new ArrayList<>(); + + final Vec3 color = new Vec3(1, 1, 1); + + // F BODY + faces.add(Faces.createRectangle( + program, texture.get(80, 16, 32, 32), color, + new Vec3(+16, -16, -16), + new Vec3(0, +32, 0), new Vec3(0, 0, +32), + false + )); + + // NECK BASE + faces.add(Faces.createRectangle( + program, texture.get(80, 48, 32, 16), color, + new Vec3(+16, -16, +16), + new Vec3(0, +32, 0), new Vec3(-16, 0, 0), + false + )); + + // T BODY (BACK) + faces.add(Faces.createRectangle( + program, texture.get(128, 0, 32, 48), color, + new Vec3(0, -16, +16), + new Vec3(0, +32, 0), new Vec3(-48, 0, 0), + false + )); + + // BOTTOM B (upper) + faces.add(Faces.createRectangle( + program, texture.get(144, 48, 32, 16), color, + new Vec3(-48, -16, 0), + new Vec3(0, 32, 0), new Vec3(0, 0, 16), + true + )); + + // BOTTOM B (lower) + faces.add(Faces.createRectangle( + program, texture.get(144, 48, 32, 16), color, + new Vec3(-48, -16, -16), + new Vec3(0, 32, 0), new Vec3(0, 0, 16), + true + )); + + // BOTTOM B (stomach) + faces.add(Faces.createRectangle( + program, texture.get(144, 48, 32, 16), color, + new Vec3(-48, -16, -16), + new Vec3(0, 32, 0), new Vec3(16, 0, 0), + false + )); + + // STOMACH + faces.add(Faces.createRectangle( + program, texture.get(224, 96, 32, 32), color, + new Vec3(-32, -16, -16), + new Vec3(0, 32, 0), new Vec3(32, 0, 0), + false + )); + + // BOTTOM F + faces.add(Faces.createRectangle( + program, texture.get(112, 48, 32, 16), color, + new Vec3(+16, -16, -16), + new Vec3(0, 32, 0), new Vec3(-16, 0, 0), + true + )); + + // BODY L + faces.add(Faces.createRectangle( + program, texture.get(112, 16, 16, 32), color, + new Vec3(+16, +16, -16), + new Vec3(-16, 0, 0), new Vec3(0, 0, +32), + false + )); + + // BODY SIDES (left) + faces.add(Faces.createRectangle( + program, texture.get(96, 96, 32, 32), color, + new Vec3(0, +16, -16), + new Vec3(-32, 0, 0), new Vec3(0, 0, +32), + false + )); + + // QT MARK (left) + faces.add(Faces.createRectangle( + program, texture.get(16, 96, 16, 32), color, + new Vec3(-32, +16, -16), + new Vec3(-16, 0, 0), new Vec3(0, 0, +32), + false + )); + + // BODY R + faces.add(Faces.createRectangle( + program, texture.get(64, 16, 16, 32), color, + new Vec3(0, -16, -16), + new Vec3(+16, 0, 0), new Vec3(0, 0, +32), + false + )); + + // BODY SIDES (right) + faces.add(Faces.createRectangle( + program, texture.get(96, 96, 32, 32), color, + new Vec3(0, -16, -16), + new Vec3(-32, 0, 0), new Vec3(0, 0, +32), + true + )); + + // QT MARK (right) + faces.add(Faces.createRectangle( + program, texture.get(16, 96, 16, 32), color, + new Vec3(-32, -16, -16), + new Vec3(-16, 0, 0), new Vec3(0, 0, +32), + true + )); + + return new Shape( + Usage.STATIC, program, + faces.toArray(new Face[faces.size()]) + ); + } + + private static Renderable createHead(ComplexTexture texture) { + WorldRenderProgram program = WorldRenderProgram.getDefault(); + StaticModel.Builder b = StaticModel.builder(); + + // Head + b.addPart(new PppBuilder( + program, texture.getCuboidTextures(0, 64, 32) + ).setOrigin(-16, -16, 0).setSize(32).create()); + + final float hairOffset = 1f; + + // Hair + b.addPart( + new PppBuilder( + program, texture.getCuboidTextures(128, 64, 32) + ) + .setOrigin(-16 - hairOffset, -16 - hairOffset, -hairOffset) + .setSize(32 + 2*hairOffset) + .create() + ); + + // Right ear + b.addPart(new PppBuilder( + program, texture.getCuboidTextures(48, 128-80, 8) + ).setOrigin(-16 + 3, -16, 32).setSize(8).create()); + + // Left ear + b.addPart(new PppBuilder( + program, texture.getCuboidTextures(48, 128-80, 8) + ).setOrigin(-16 + 3, +16, 32).setSize(8, -8, 8).flip().create()); + + // Muzzle + b.addPart(new PppBuilder( + program, BlockFace.mapToFaces( + texture.get(32, 64, 0, 0), + texture.get(32, 64, 0, 0), + texture.get(32 + 8, 64, 16, 8), + texture.get(32, 64, 0, 0), + texture.get(32, 64, 0, 0), + texture.get(32, 64, 0, 0) + ) + ).setOrigin(16, -8, 0).setSize(4, 16, 8).create()); + + // Nose + b.addPart(new PppBuilder( + program, BlockFace.mapToFaces( + texture.get(32, 64, 0, 0), + texture.get(32, 64, 0, 0), + texture.get(32 + 12, 64 + 8, 8, 4), + texture.get(32, 64, 0, 0), + texture.get(32, 64, 0, 0), + texture.get(32, 64, 0, 0) + ) + ).setOrigin(16, -4, 8).setSize(4, 8, 4).create()); + + return new StaticModel(b); + } + + private static Renderable createLeg( + ComplexTexture texture, + int textureX, int textureY, + boolean isLeft + ) { + PppBuilder b = new PppBuilder( + WorldRenderProgram.getDefault(), + texture.getCuboidTextures(textureX, textureY, 16, 48, 16) + ) + .setOrigin(-8, isLeft ? +8 : -8, -48) + .setSize(16, isLeft ? -16 : +16, 48); + + if (isLeft) b.flip(); + + return b.create(); + } + + private static Renderable createTail(ComplexTexture texture) { + WorldRenderProgram program = WorldRenderProgram.getDefault(); + StaticModel.Builder b = StaticModel.builder(); + + // Main tail + b.addPart(new PppBuilder( + program, BlockFace.mapToFaces( + texture.get(128, 96, 16, 16), + texture.get(128, 96, 16, 16), + texture.get(128, 96, 16, 32), + texture.get(128, 96, 16, 32), + texture.get(144, 96, 16, 32), + texture.get(144, 96, 16, 32) + ) + ).setOrigin(-8, -8, -32).setSize(16, 16, 32).create()); + + return new StaticModel(b); + } + + @Override + public EntityRenderable createRenderable(EntityData entity) { + return new QuadripedModel( + entity, + + new QuadripedModel.Body(body), + new QuadripedModel.Head( + head, new Vec3(12, 0, 20), 60, 45, new Vec3(16, 0, 20) + ), + new QuadripedModel.Leg( + leftForeLeg, new Vec3( 6, +8.1f, -16), 0.0f + ), + new QuadripedModel.Leg( + rightForeLeg, new Vec3( 6, -8.1f, -16), 2.5f + ), + new QuadripedModel.Leg( + leftHindLeg, new Vec3(-36, +8.2f, -16), 2.5f + ), + new QuadripedModel.Leg( + rightHindLeg, new Vec3(-36, -8.2f, -16), 0.0f + ), + + 1 / 96f + ); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/client/comms/DefaultClientCommsListener.java b/src/main/java/ru/windcorp/progressia/client/comms/DefaultClientCommsListener.java index 134bfb1..299c50a 100644 --- a/src/main/java/ru/windcorp/progressia/client/comms/DefaultClientCommsListener.java +++ b/src/main/java/ru/windcorp/progressia/client/comms/DefaultClientCommsListener.java @@ -1,12 +1,18 @@ package ru.windcorp.progressia.client.comms; import java.io.IOException; +import java.util.Collection; +import java.util.UUID; import ru.windcorp.progressia.client.Client; +import ru.windcorp.progressia.client.graphics.world.EntityAnchor; 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.PacketSetLocalPlayer; import ru.windcorp.progressia.common.comms.packets.PacketWorldChange; +import ru.windcorp.progressia.common.world.ChunkData; +import ru.windcorp.progressia.common.world.entity.EntityData; public class DefaultClientCommsListener implements CommsListener { @@ -24,9 +30,41 @@ public class DefaultClientCommsListener implements CommsListener { ); tmp_reassembleWorld(); + } else if (packet instanceof PacketSetLocalPlayer) { + setLocalPlayer((PacketSetLocalPlayer) packet); } } + private void setLocalPlayer(PacketSetLocalPlayer packet) { + UUID uuid = packet.getLocalPlayerEntityUUID(); + + Collection chunks = + getClient().getWorld().getData().getChunks(); + + EntityData entity = null; + + synchronized (chunks) { + chunkLoop: + for (ChunkData chunk : chunks) { + for (EntityData anEntity : chunk.getEntities()) { + if (anEntity.getUUID().equals(uuid)) { + entity = anEntity; + break chunkLoop; + } + } + } + } + + if (entity == null) { + throw new RuntimeException(""); + } + + getClient().setLocalPlayer(entity); + getClient().getCamera().setAnchor(new EntityAnchor( + getClient().getWorld().getEntityRenderable(entity) + )); + } + private void tmp_reassembleWorld() { getClient().getWorld().getChunks().forEach(ChunkRender::markForUpdate); } 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 index 478f6f4..8c7b6d7 100644 --- a/src/main/java/ru/windcorp/progressia/client/comms/localhost/LocalServerCommsChannel.java +++ b/src/main/java/ru/windcorp/progressia/client/comms/localhost/LocalServerCommsChannel.java @@ -6,10 +6,15 @@ import ru.windcorp.progressia.server.Server; public class LocalServerCommsChannel extends ServerCommsChannel { - private final LocalClient localClient; + private LocalClient localClient; + private final Server server; public LocalServerCommsChannel(Server server) { super(Role.GAME, Role.CHAT); + this.server = server; + } + + public void connect() { setState(State.CONNECTED); this.localClient = new LocalClient( diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/backend/FaceCulling.java b/src/main/java/ru/windcorp/progressia/client/graphics/backend/FaceCulling.java new file mode 100644 index 0000000..f289876 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/graphics/backend/FaceCulling.java @@ -0,0 +1,27 @@ +package ru.windcorp.progressia.client.graphics.backend; + +import java.util.ArrayDeque; +import java.util.Deque; + +public class FaceCulling { + + private static final Deque STACK = new ArrayDeque<>(); + + public static void push(boolean useFaceCulling) { + GraphicsBackend.setFaceCulling(useFaceCulling); + STACK.push(Boolean.valueOf(useFaceCulling)); + } + + public static void pop() { + STACK.pop(); + + if (STACK.isEmpty()) { + GraphicsBackend.setFaceCulling(false); + } else { + GraphicsBackend.setFaceCulling(STACK.getFirst()); + } + } + + private FaceCulling() {} + +} diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/backend/GraphicsBackend.java b/src/main/java/ru/windcorp/progressia/client/graphics/backend/GraphicsBackend.java index ba19d31..cc1df99 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/backend/GraphicsBackend.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/backend/GraphicsBackend.java @@ -35,6 +35,8 @@ public class GraphicsBackend { private static long framesRendered = 0; private static double frameStart = Double.NaN; + private static boolean faceCullingEnabled = false; + private GraphicsBackend() {} public static void initialize() { @@ -109,5 +111,17 @@ public class GraphicsBackend { public static void startNextLayer() { glClear(GL_DEPTH_BUFFER_BIT); } + + public static void setFaceCulling(boolean useFaceCulling) { + if (useFaceCulling == faceCullingEnabled) return; + + if (useFaceCulling) { + glEnable(GL_CULL_FACE); + } else { + glDisable(GL_CULL_FACE); + } + + faceCullingEnabled = useFaceCulling; + } } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/backend/LWJGLInitializer.java b/src/main/java/ru/windcorp/progressia/client/graphics/backend/LWJGLInitializer.java index ea14292..17e36f6 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/backend/LWJGLInitializer.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/backend/LWJGLInitializer.java @@ -81,7 +81,6 @@ class LWJGLInitializer { private static void initializeOpenGL() { GL.createCapabilities(); glEnable(GL_DEPTH_TEST); - glEnable(GL_CULL_FACE); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/flat/AssembledFlatLayer.java b/src/main/java/ru/windcorp/progressia/client/graphics/flat/AssembledFlatLayer.java index 0980e01..f53aab0 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/flat/AssembledFlatLayer.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/flat/AssembledFlatLayer.java @@ -18,6 +18,7 @@ package ru.windcorp.progressia.client.graphics.flat; import ru.windcorp.progressia.client.graphics.Layer; +import ru.windcorp.progressia.client.graphics.backend.FaceCulling; public abstract class AssembledFlatLayer extends Layer { @@ -48,11 +49,15 @@ public abstract class AssembledFlatLayer extends Layer { @Override protected void doRender() { + FaceCulling.push(false); + for (RenderTarget.Clip clip : clips) { clip.render(helper); } helper.reset(); + + FaceCulling.pop(); } } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/flat/FlatRenderProgram.java b/src/main/java/ru/windcorp/progressia/client/graphics/flat/FlatRenderProgram.java index 9828518..846013f 100755 --- a/src/main/java/ru/windcorp/progressia/client/graphics/flat/FlatRenderProgram.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/flat/FlatRenderProgram.java @@ -17,8 +17,6 @@ *******************************************************************************/ package ru.windcorp.progressia.client.graphics.flat; -import static org.lwjgl.opengl.GL11.*; - import java.nio.FloatBuffer; import com.google.common.collect.ObjectArrays; @@ -81,9 +79,7 @@ public class FlatRenderProgram extends ShapeRenderProgram { @Override public void render(ShapeRenderHelper helper, Shape shape) { - glDisable(GL_CULL_FACE); super.render(helper, shape); - glEnable(GL_CULL_FACE); } @Override 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 cf2cb90..f5da33e 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 @@ -30,6 +30,7 @@ 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.Camera; public class LayerTestUI extends AssembledFlatLayer { @@ -73,7 +74,10 @@ 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(ClientState.getInstance().getCamera().getYaw()).translate(-texSize/2, -texSize/2, 0) + mat -> + mat.translate(texSize/2, texSize/2, 0) + .rotateZ(getCompassRotation()) + .translate(-texSize/2, -texSize/2, 0) ) )); target.popTransform(); @@ -81,6 +85,15 @@ public class LayerTestUI extends AssembledFlatLayer { drawCross(target); } + private double getCompassRotation() { + Camera.Anchor anchor = + ClientState.getInstance().getCamera().getAnchor(); + + if (anchor == null) return 0; + + return -anchor.getCameraYaw(); + } + private void drawCross(RenderTarget target) { int cx = getWidth() / 2; int cy = getHeight() / 2; diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/gui/LayerTestGUI.java b/src/main/java/ru/windcorp/progressia/client/graphics/gui/LayerTestGUI.java index d8d364e..6a74d96 100755 --- a/src/main/java/ru/windcorp/progressia/client/graphics/gui/LayerTestGUI.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/gui/LayerTestGUI.java @@ -81,7 +81,7 @@ public class LayerTestGUI extends GUILayer { panel.addChild(new DebugComponent("Bravo", new Vec2i(200, 100), 0x44FF44)); Component charlie = new DebugComponent("Charlie", null, 0x222222); - charlie.setLayout(new LayoutVertical()); + charlie.setLayout(new LayoutVertical(5)); //Debug Localizer.getInstance().setLanguage("ru-RU"); @@ -90,16 +90,16 @@ public class LayerTestGUI extends GUILayer { // These two are swapped in code due to a bug in layouts, fixing ATM charlie.addChild( new Label( - "Epsilon", - new Font().withColor(0x4444BB).deriveItalic(), - () -> epsilon.get().concat("\u269b") + "Delta", + new Font().withColor(0xCCBB44).deriveShadow().deriveBold(), + "Пре-альфа!" ) ); charlie.addChild( new Label( - "Delta", - new Font().withColor(0xCCBB44).deriveShadow().deriveBold(), - "Пре-альфа!" + "Epsilon", + new Font().withColor(0x4444BB).deriveItalic(), + () -> epsilon.get().concat("\u269b") ) ); panel.addChild(charlie); diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/gui/layout/LayoutVertical.java b/src/main/java/ru/windcorp/progressia/client/graphics/gui/layout/LayoutVertical.java index 3ceea63..83e331f 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/gui/layout/LayoutVertical.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/gui/layout/LayoutVertical.java @@ -43,16 +43,14 @@ public class LayoutVertical implements Layout { @Override public void layout(Component c) { int x = c.getX() + margin, - y = c.getY() + margin; - - int height; + y = c.getY() + c.getHeight(); synchronized (c.getChildren()) { for (Component child : c.getChildren()) { - height = child.getPreferredSize().y; + int height = child.getPreferredSize().y; + y -= gap + height; child.setBounds(x, y, c.getWidth() - 2 * margin, height); - y += gap + height; } } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/model/Shapes.java b/src/main/java/ru/windcorp/progressia/client/graphics/model/Shapes.java index fabc468..224ff11 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/model/Shapes.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/model/Shapes.java @@ -17,10 +17,13 @@ *******************************************************************************/ package ru.windcorp.progressia.client.graphics.model; +import java.util.Map; + import glm.vec._3.Vec3; import ru.windcorp.progressia.client.graphics.backend.Usage; import ru.windcorp.progressia.client.graphics.texture.Texture; import ru.windcorp.progressia.common.util.Vectors; +import ru.windcorp.progressia.common.world.block.BlockFace; public class Shapes { @@ -40,7 +43,9 @@ public class Shapes { Texture northTexture, Texture southTexture, Texture eastTexture, - Texture westTexture + Texture westTexture, + + boolean flip ) { Vec3 faceOrigin = Vectors.grab3(); @@ -52,7 +57,7 @@ public class Shapes { faceOrigin.set(origin).add(height).add(width), faceWidth.set(width).negate(), depth, - false + flip ); Face bottom = Faces.createRectangle( @@ -61,7 +66,7 @@ public class Shapes { origin, width, depth, - false + flip ); Face north = Faces.createRectangle( @@ -70,7 +75,7 @@ public class Shapes { faceOrigin.set(origin).add(depth), width, height, - false + flip ); Face south = Faces.createRectangle( @@ -79,7 +84,7 @@ public class Shapes { faceOrigin.set(origin).add(width), faceWidth.set(width).negate(), height, - false + flip ); Face east = Faces.createRectangle( @@ -88,7 +93,7 @@ public class Shapes { origin, depth, height, - false + flip ); Face west = Faces.createRectangle( @@ -97,7 +102,7 @@ public class Shapes { faceOrigin.set(origin).add(width).add(depth), faceWidth.set(depth).negate(), height, - false + flip ); Shape result = new Shape( @@ -131,6 +136,8 @@ public class Shapes { private final Texture eastTexture; private final Texture westTexture; + private boolean flip = false; + public PppBuilder( ShapeRenderProgram program, Texture top, @@ -149,6 +156,21 @@ public class Shapes { this.westTexture = west; } + public PppBuilder( + ShapeRenderProgram program, + Map textureMap + ) { + this( + program, + textureMap.get(BlockFace.TOP), + textureMap.get(BlockFace.BOTTOM), + textureMap.get(BlockFace.NORTH), + textureMap.get(BlockFace.SOUTH), + textureMap.get(BlockFace.EAST), + textureMap.get(BlockFace.WEST) + ); + } + public PppBuilder(ShapeRenderProgram program, Texture texture) { this(program, texture, texture, texture, texture, texture, texture); } @@ -219,7 +241,16 @@ public class Shapes { } public PppBuilder setSize(float x, float y, float z) { - return this.setWidth(x).setDepth(y).setHeight(z); + return this.setDepth(x).setWidth(y).setHeight(z); + } + + public PppBuilder setSize(float size) { + return this.setSize(size, size, size); + } + + public PppBuilder flip() { + this.flip = true; + return this; } public Shape create() { @@ -233,7 +264,8 @@ public class Shapes { northTexture, southTexture, eastTexture, - westTexture + westTexture, + flip ); } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/texture/ComplexTexture.java b/src/main/java/ru/windcorp/progressia/client/graphics/texture/ComplexTexture.java new file mode 100644 index 0000000..d8f4c18 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/graphics/texture/ComplexTexture.java @@ -0,0 +1,66 @@ +package ru.windcorp.progressia.client.graphics.texture; + +import java.util.Map; + +import glm.vec._2.Vec2; +import ru.windcorp.progressia.common.world.block.BlockFace; + +public class ComplexTexture { + + private final TexturePrimitive primitive; + + private final float assumedWidth; + private final float assumedHeight; + + public ComplexTexture( + TexturePrimitive primitive, + int abstractWidth, int abstractHeight + ) { + this.primitive = primitive; + + this.assumedWidth = abstractWidth + * primitive.getWidth() / (float) primitive.getBufferWidth(); + + this.assumedHeight = abstractHeight + * primitive.getHeight() / (float) primitive.getBufferHeight(); + } + + public Texture get(int x, int y, int width, int height) { + return new SimpleTexture(new Sprite( + primitive, + new Vec2(x / assumedWidth, y / assumedHeight), + new Vec2(width / assumedWidth, height / assumedHeight) + )); + } + + public Map getCuboidTextures( + int x, int y, + int width, int height, int depth + ) { + return BlockFace.mapToFaces( + get( + x + depth + width, y + height + depth, + -width, -depth + ), + get( + x + depth + width + width, y + height + depth, + -width, -depth + ), + get(x + depth, y, width, height), + get( + x + depth + width + depth, y, + width, height + ), + get(x, y, depth, height), + get(x + depth + width, y, depth, height) + ); + } + + public Map getCuboidTextures( + int x, int y, + int size + ) { + return getCuboidTextures(x, y, size, size, size); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/world/Camera.java b/src/main/java/ru/windcorp/progressia/client/graphics/world/Camera.java index 3eb8745..7e8a59d 100644 --- a/src/main/java/ru/windcorp/progressia/client/graphics/world/Camera.java +++ b/src/main/java/ru/windcorp/progressia/client/graphics/world/Camera.java @@ -19,24 +19,62 @@ package ru.windcorp.progressia.client.graphics.world; import static java.lang.Math.*; +import java.util.Collection; +import java.util.function.Consumer; + import glm.Glm; import glm.mat._4.Mat4; import glm.vec._3.Vec3; import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface; +import ru.windcorp.progressia.client.graphics.world.Camera.Anchor.Mode; +import ru.windcorp.progressia.common.util.Vectors; public class Camera { - private final Vec3 position = new Vec3(); + public static interface Anchor { + + /** + * Offset is applied after the rotation. + */ + public static interface Mode { + void getCameraOffset(Vec3 output); + void applyCameraRotation(Mat4 output); + + public static Mode of( + Consumer offsetGetter, + Consumer rotator + ) { + return new Mode() { + @Override + public void getCameraOffset(Vec3 output) { + offsetGetter.accept(output); + } + + @Override + public void applyCameraRotation(Mat4 output) { + rotator.accept(output); + } + }; + } + } + + void getCameraPosition(Vec3 output); + void getCameraVelocity(Vec3 output); + float getCameraYaw(); + float getCameraPitch(); + + Collection getCameraModes(); + + } - private float pitch; - private float yaw; + private Anchor anchor; + + private Anchor.Mode[] modes; + private int currentModeIndex; private float fieldOfView; - public Camera(Vec3 position, float pitch, float yaw, float fieldOfView) { - teleport(position); - setPitch(pitch); - setYaw(yaw); + public Camera(float fieldOfView) { setFieldOfView(fieldOfView); } @@ -46,6 +84,7 @@ public class Camera { applyPerspective(helper); rotateCoordinateSystem(helper); + applyMode(helper); applyDirection(helper); applyPosition(helper); } @@ -64,14 +103,38 @@ public class Camera { private void rotateCoordinateSystem(WorldRenderHelper helper) { helper.pushViewTransform().rotateX(-PI / 2).rotateZ(PI / 2); } + + private void applyMode(WorldRenderHelper helper) { + Mode mode = getMode(); + + Mat4 matrix = helper.pushViewTransform(); + + Vec3 offset = Vectors.grab3(); + mode.getCameraOffset(offset); + + offset.negate(); + matrix.translate(offset); + + Vectors.release(offset); + + mode.applyCameraRotation(matrix); + } private void applyDirection(WorldRenderHelper helper) { - helper.pushViewTransform().rotateY(pitch).rotateZ(yaw); + helper.pushViewTransform() + .rotateY(-anchor.getCameraPitch()) + .rotateZ(-anchor.getCameraYaw()); } private void applyPosition(WorldRenderHelper helper) { - helper.pushViewTransform().translate(position.negate()); - position.negate(); + Vec3 v = Vectors.grab3(); + + anchor.getCameraPosition(v); + v.negate(); + + helper.pushViewTransform().translate(v); + + Vectors.release(v); } private float computeFovY() { @@ -87,45 +150,6 @@ public class Camera { )); } } - - public Vec3 getPosition() { - return position; - } - - public void teleport(Vec3 pos) { - position.set(pos); - } - - public void move(Vec3 pos) { - position.add(pos); - } - - public float getPitch() { - return pitch; - } - - public void setPitch(float pitch) { - final float maxPitch = (float) (Math.PI / 2); - this.pitch = Glm.clamp(pitch, -maxPitch, +maxPitch); - } - - public float getYaw() { - return yaw; - } - - public void setYaw(float yaw) { - this.yaw = Glm.mod(yaw, 2 * (float) PI); - } - - public void setDirection(float pitch, float yaw) { - setPitch(pitch); - setYaw(yaw); - } - - public void turn(float pitchChange, float yawChange) { - setPitch(getPitch() + pitchChange); - setYaw(getYaw() + yawChange); - } public float getFieldOfView() { return fieldOfView; @@ -134,5 +158,47 @@ public class Camera { public void setFieldOfView(float fieldOfView) { this.fieldOfView = fieldOfView; } + + public Anchor getAnchor() { + return anchor; + } + + public boolean hasAnchor() { + return anchor != null; + } + + public void setAnchor(Anchor anchor) { + if (anchor == null) { + this.anchor = null; + this.modes = null; + return; + } + + Collection modesCollection = anchor.getCameraModes(); + + if (modesCollection.isEmpty()) { + throw new IllegalArgumentException( + "Anchor " + anchor + " returned no camera modes," + + " at least one required" + ); + } + + this.anchor = anchor; + + this.modes = modesCollection.toArray(new Mode[modesCollection.size()]); + this.currentModeIndex = 0; + } + + public Anchor.Mode getMode() { + return modes[currentModeIndex]; + } + + public void selectNextMode() { + if (currentModeIndex == modes.length - 1) { + currentModeIndex = 0; + } else { + currentModeIndex++; + } + } } diff --git a/src/main/java/ru/windcorp/progressia/client/graphics/world/EntityAnchor.java b/src/main/java/ru/windcorp/progressia/client/graphics/world/EntityAnchor.java new file mode 100644 index 0000000..86f859f --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/graphics/world/EntityAnchor.java @@ -0,0 +1,67 @@ +package ru.windcorp.progressia.client.graphics.world; + +import java.util.Collection; + +import com.google.common.collect.ImmutableList; + +import glm.vec._3.Vec3; +import ru.windcorp.progressia.client.graphics.world.Camera.Anchor; +import ru.windcorp.progressia.client.world.entity.EntityRenderable; +import ru.windcorp.progressia.common.world.entity.EntityData; + +public class EntityAnchor implements Anchor { + + private final EntityData entity; + private final EntityRenderable model; + + private final Collection modes; + + public EntityAnchor(EntityRenderable model) { + this.entity = model.getData(); + this.model = model; + + this.modes = ImmutableList.of( + // From viewpoint / first person + Mode.of(v -> v.set(0), m -> {}), + + // Third person, looking forward + Mode.of( + v -> v.set(-3.5f, +0.5f, 0), + m -> {} + ), + + // Third person, looking back + Mode.of( + v -> v.set(-3.5f, 0, 0), + m -> m.rotateZ((float) Math.PI) + ) + ); + } + + @Override + public void getCameraPosition(Vec3 output) { + model.getViewPoint(output); + output.add(entity.getPosition()); + } + + @Override + public void getCameraVelocity(Vec3 output) { + output.set(entity.getVelocity()); + } + + @Override + public float getCameraYaw() { + return entity.getYaw(); + } + + @Override + public float getCameraPitch() { + return entity.getPitch(); + } + + @Override + public Collection getCameraModes() { + return modes; + } + +} 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 6f18b0e..2cc11b0 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 @@ -19,18 +19,23 @@ package ru.windcorp.progressia.client.graphics.world; import org.lwjgl.glfw.GLFW; +import glm.Glm; import glm.mat._3.Mat3; +import glm.vec._2.Vec2; import glm.vec._3.Vec3; 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.FaceCulling; import ru.windcorp.progressia.client.graphics.backend.GraphicsBackend; import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface; 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.common.util.FloatMathUtils; import ru.windcorp.progressia.common.util.Vectors; +import ru.windcorp.progressia.common.world.entity.EntityData; public class LayerWorld extends Layer { @@ -66,11 +71,20 @@ public class LayerWorld extends Layer { @Override protected void doRender() { - client.getCamera().apply(helper); - renderWorld(); - helper.reset(); + if (client.getLocalPlayer() != null) { + tmp_handleControls(); + } - angMat.set().rotateZ(-client.getCamera().getYaw()); + Camera camera = client.getCamera(); + if (camera.hasAnchor()) { + renderWorld(); + } + } + + private void tmp_handleControls() { + EntityData player = client.getLocalPlayer(); + + angMat.set().rotateZ(player.getYaw()); Vec3 movement = Vectors.grab3(); @@ -88,13 +102,21 @@ public class LayerWorld extends Layer { Vec3 velCopy = Vectors.grab3().set(velocity); velCopy.mul((float) (GraphicsInterface.getFrameLength() * 60)); - client.getCamera().move(velCopy); + + player.getPosition().add(velCopy); + player.getVelocity().set(velocity); Vectors.release(velCopy); } private void renderWorld() { + client.getCamera().apply(helper); + FaceCulling.push(true); + this.client.getWorld().render(helper); + + FaceCulling.pop(); + helper.reset(); } @Override @@ -156,6 +178,14 @@ public class LayerWorld extends Layer { flag = !flag; break; + case GLFW.GLFW_KEY_F5: + if (!event.isPress()) return false; + + if (client.getCamera().hasAnchor()) { + client.getCamera().selectNextMode(); + } + break; + default: return false; } @@ -166,12 +196,26 @@ public class LayerWorld extends Layer { private void onMouseMoved(CursorMoveEvent event) { if (!flag) return; - final float yawScale = 0.002f; + final float yawScale = -0.002f; final float pitchScale = yawScale; + + EntityData player = client.getLocalPlayer(); - client.getCamera().turn( - (float) (event.getChangeY() * pitchScale), - (float) (event.getChangeX() * yawScale) + if (player != null) { + normalizeAngles(player.getDirection().add( + (float) (event.getChangeX() * yawScale), + (float) (event.getChangeY() * pitchScale) + )); + } + } + + private void normalizeAngles(Vec2 dir) { + // Normalize yaw + dir.x = FloatMathUtils.normalizeAngle(dir.x); + + // Clamp pitch + dir.y = Glm.clamp( + dir.y, -FloatMathUtils.PI_F/2, +FloatMathUtils.PI_F/2 ); } diff --git a/src/main/java/ru/windcorp/progressia/client/world/WorldRender.java b/src/main/java/ru/windcorp/progressia/client/world/WorldRender.java index 249dd91..fc219f4 100644 --- a/src/main/java/ru/windcorp/progressia/client/world/WorldRender.java +++ b/src/main/java/ru/windcorp/progressia/client/world/WorldRender.java @@ -18,19 +18,27 @@ package ru.windcorp.progressia.client.world; import java.util.Collection; +import java.util.Collections; import java.util.HashMap; import java.util.Map; +import java.util.WeakHashMap; import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.client.graphics.backend.FaceCulling; import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper; +import ru.windcorp.progressia.client.world.entity.EntityRenderRegistry; +import ru.windcorp.progressia.client.world.entity.EntityRenderable; import ru.windcorp.progressia.common.world.ChunkData; import ru.windcorp.progressia.common.world.WorldData; +import ru.windcorp.progressia.common.world.entity.EntityData; public class WorldRender { private final WorldData data; private final Map chunks = new HashMap<>(); + private final Map entityModels = + Collections.synchronizedMap(new WeakHashMap<>()); public WorldRender(WorldData data) { this.data = data; @@ -60,6 +68,34 @@ public class WorldRender { for (ChunkRender chunk : getChunks()) { chunk.render(renderer); } + + renderEntities(renderer); + } + + private void renderEntities(ShapeRenderHelper renderer) { + FaceCulling.push(false); + + for (ChunkRender chunk : getChunks()) { + chunk.getData().forEachEntity(entity -> { + renderer.pushTransform().translate(entity.getPosition()); + getEntityRenderable(entity).render(renderer); + renderer.popTransform(); + }); + } + + FaceCulling.pop(); + } + + public EntityRenderable getEntityRenderable(EntityData entity) { + return entityModels.computeIfAbsent( + entity, + WorldRender::createEntityRenderable + ); + } + + private static EntityRenderable createEntityRenderable(EntityData entity) { + return EntityRenderRegistry.getInstance().get(entity.getId()) + .createRenderable(entity); } } diff --git a/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRender.java b/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRender.java new file mode 100644 index 0000000..32c9be1 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRender.java @@ -0,0 +1,14 @@ +package ru.windcorp.progressia.client.world.entity; + +import ru.windcorp.progressia.common.util.Namespaced; +import ru.windcorp.progressia.common.world.entity.EntityData; + +public abstract class EntityRender extends Namespaced { + + public EntityRender(String namespace, String name) { + super(namespace, name); + } + + public abstract EntityRenderable createRenderable(EntityData entity); + +} diff --git a/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRenderRegistry.java b/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRenderRegistry.java new file mode 100644 index 0000000..e79cebe --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRenderRegistry.java @@ -0,0 +1,35 @@ +package ru.windcorp.progressia.client.world.entity; + +import java.io.IOException; + +import ru.windcorp.progressia.client.graphics.texture.TextureLoader; +import ru.windcorp.progressia.client.graphics.texture.TexturePrimitive; +import ru.windcorp.progressia.client.graphics.texture.TextureSettings; +import ru.windcorp.progressia.common.resource.ResourceManager; +import ru.windcorp.progressia.common.util.NamespacedRegistry; + +public class EntityRenderRegistry extends NamespacedRegistry { + + private static final EntityRenderRegistry INSTANCE = + new EntityRenderRegistry(); + + public static EntityRenderRegistry getInstance() { + return INSTANCE; + } + + public static TexturePrimitive getEntityTexture(String name) { + try { + return new TexturePrimitive( + TextureLoader.loadPixels( + ResourceManager.getTextureResource( + "entities/" + name + ), + new TextureSettings(false) + ).getData() + ); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + +} diff --git a/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRenderable.java b/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRenderable.java new file mode 100644 index 0000000..e7e8466 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/world/entity/EntityRenderable.java @@ -0,0 +1,23 @@ +package ru.windcorp.progressia.client.world.entity; + +import glm.vec._3.Vec3; +import ru.windcorp.progressia.client.graphics.model.Renderable; +import ru.windcorp.progressia.common.world.entity.EntityData; + +public abstract class EntityRenderable implements Renderable { + + private final EntityData data; + + public EntityRenderable(EntityData data) { + this.data = data; + } + + public EntityData getData() { + return data; + } + + public void getViewPoint(Vec3 output) { + output.set(0, 0, 0); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/client/world/entity/QuadripedModel.java b/src/main/java/ru/windcorp/progressia/client/world/entity/QuadripedModel.java new file mode 100644 index 0000000..a7bdc97 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/world/entity/QuadripedModel.java @@ -0,0 +1,236 @@ +package ru.windcorp.progressia.client.world.entity; + +import static java.lang.Math.*; +import static ru.windcorp.progressia.common.util.FloatMathUtils.*; + +import glm.Glm; +import glm.mat._4.Mat4; +import glm.vec._3.Vec3; +import glm.vec._4.Vec4; +import ru.windcorp.progressia.client.graphics.backend.GraphicsInterface; +import ru.windcorp.progressia.client.graphics.model.Renderable; +import ru.windcorp.progressia.client.graphics.model.ShapeRenderHelper; +import ru.windcorp.progressia.common.util.Matrices; +import ru.windcorp.progressia.common.util.Vectors; +import ru.windcorp.progressia.common.world.entity.EntityData; + +public class QuadripedModel extends EntityRenderable { + + private static abstract class BodyPart { + private final Renderable renderable; + private final Vec3 translation = new Vec3(); + + public BodyPart(Renderable renderable, Vec3 joint) { + this.renderable = renderable; + if (joint != null) { + this.translation.set(joint); + } + } + + + protected void render( + ShapeRenderHelper renderer, QuadripedModel model + ) { + renderer.pushTransform().translate(translation); + applyTransform(renderer.pushTransform(), model); + renderable.render(renderer); + renderer.popTransform(); + renderer.popTransform(); + } + + protected abstract void applyTransform(Mat4 mat, QuadripedModel model); + + public Vec3 getTranslation() { + return translation; + } + } + + public static class Body extends BodyPart { + public Body(Renderable renderable) { + super(renderable, null); + } + + @Override + protected void applyTransform(Mat4 mat, QuadripedModel model) { + // Do nothing + } + } + + public static class Head extends BodyPart { + private final float maxYaw; + private final float maxPitch; + + private final Vec3 viewPoint; + + public Head( + Renderable renderable, Vec3 joint, + double maxYawDegrees, double maxPitchDegrees, + Vec3 viewPoint + ) { + super(renderable, joint); + this.maxYaw = (float) toRadians(maxYawDegrees); + this.maxPitch = (float) toRadians(maxPitchDegrees); + this.viewPoint = viewPoint; + } + + @Override + protected void applyTransform(Mat4 mat, QuadripedModel model) { + mat.rotateZ(model.headYaw).rotateY(model.headPitch); + } + + public Vec3 getViewPoint() { + return viewPoint; + } + } + + public static class Leg extends BodyPart { + private final float animationOffset; + + public Leg( + Renderable renderable, Vec3 joint, + float animationOffset + ) { + super(renderable, joint); + this.animationOffset = animationOffset; + } + + @Override + protected void applyTransform(Mat4 mat, QuadripedModel model) { + mat.rotateY(sin(model.walkingFrequency * model.walkingAnimationParameter + animationOffset) * model.walkingSwing * model.velocityCoeff); + } + } + + private final Body body; + private final Head head; + private final Leg leftForeLeg, rightForeLeg; + private final Leg leftHindLeg, rightHindLeg; + + private final float scale; + + private float walkingAnimationParameter = 0; + private float velocityCoeff = 0; + private float velocity = 0; + + /** + * Controls how quickly velocityCoeff approaches 1 + */ + private float velocityCutoff = 10; + + private float walkingFrequency = 0.15f; + private float walkingSwing = (float) toRadians(30); + + private float bodyYaw = Float.NaN; + private float headYaw; + private float headPitch; + + public QuadripedModel( + EntityData entity, + + Body body, Head head, + Leg leftForeLeg, Leg rightForeLeg, + Leg leftHindLeg, Leg rightHindLeg, + + float scale + ) { + super(entity); + + this.body = body; + this.head = head; + this.leftForeLeg = leftForeLeg; + this.rightForeLeg = rightForeLeg; + this.leftHindLeg = leftHindLeg; + this.rightHindLeg = rightHindLeg; + + this.scale = scale; + } + + @Override + public void render(ShapeRenderHelper renderer) { + renderer.pushTransform().scale(scale).rotateZ(bodyYaw); + body.render(renderer, this); + head.render(renderer, this); + leftForeLeg.render(renderer, this); + rightForeLeg.render(renderer, this); + leftHindLeg.render(renderer, this); + rightHindLeg.render(renderer, this); + renderer.popTransform(); + + accountForVelocity(); + evaluateAngles(); + } + + private void evaluateAngles() { + float globalYaw = normalizeAngle(getData().getYaw()); + + if (Float.isNaN(bodyYaw)) { + bodyYaw = globalYaw; + headYaw = 0; + } else { + headYaw = normalizeAngle(globalYaw - bodyYaw); + + if (headYaw > +head.maxYaw) { + bodyYaw += headYaw - +head.maxYaw; + headYaw = +head.maxYaw; + } else if (headYaw < -head.maxYaw) { + bodyYaw += headYaw - -head.maxYaw; + headYaw = -head.maxYaw; + } + } + + bodyYaw = normalizeAngle(bodyYaw); + + headPitch = Glm.clamp( + getData().getPitch(), + -head.maxPitch, head.maxPitch + ); + } + + private void accountForVelocity() { + Vec3 horizontal = Vectors.grab3(); + horizontal.set(getData().getVelocity()); + horizontal.z = 0; + + velocity = horizontal.length(); + + evaluateVelocityCoeff(); + + // TODO switch to world time + walkingAnimationParameter += velocity * GraphicsInterface.getFrameLength() * 1000; + + bodyYaw += velocityCoeff * normalizeAngle( + (float) (atan2(horizontal.y, horizontal.x) - bodyYaw) + ) * min(1, GraphicsInterface.getFrameLength() * 10); + Vectors.release(horizontal); + } + + private void evaluateVelocityCoeff() { + if (velocity * velocityCutoff > 1) { + velocityCoeff = 1; + } else { + velocityCoeff = velocity * velocityCutoff; + velocityCoeff *= velocityCoeff; + } + } + + @Override + public void getViewPoint(Vec3 output) { + Mat4 m = Matrices.grab4(); + Vec4 v = Vectors.grab4(); + + m.identity() + .scale(scale) + .rotateZ(bodyYaw) + .translate(head.getTranslation()) + .rotateZ(headYaw) + .rotateY(headPitch); + + v.set(head.getViewPoint(), 1); + m.mul(v); + + output.set(v.x, v.y, v.z); + + Vectors.release(v); + Matrices.release(m); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/client/world/tile/TileLocation.java b/src/main/java/ru/windcorp/progressia/client/world/tile/TileLocation.java new file mode 100644 index 0000000..9590454 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/client/world/tile/TileLocation.java @@ -0,0 +1,22 @@ +package ru.windcorp.progressia.client.world.tile; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.world.block.BlockFace; + +public class TileLocation { + + public final Vec3i pos = new Vec3i(); + public BlockFace face; + public int layer; + + public TileLocation() { + // Do nothing + } + + public TileLocation(TileLocation src) { + this.pos.set(src.pos.x, src.pos.y, src.pos.z); + this.face = src.face; + this.layer = src.layer; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/comms/packets/PacketSetLocalPlayer.java b/src/main/java/ru/windcorp/progressia/common/comms/packets/PacketSetLocalPlayer.java new file mode 100644 index 0000000..f20843a --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/comms/packets/PacketSetLocalPlayer.java @@ -0,0 +1,18 @@ +package ru.windcorp.progressia.common.comms.packets; + +import java.util.UUID; + +public class PacketSetLocalPlayer extends Packet { + + private final UUID localPlayerEntityUUID; + + public PacketSetLocalPlayer(UUID uuid) { + super("Core", "SetLocalPlayer"); + this.localPlayerEntityUUID = uuid; + } + + public UUID getLocalPlayerEntityUUID() { + return localPlayerEntityUUID; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/util/FloatMathUtils.java b/src/main/java/ru/windcorp/progressia/common/util/FloatMathUtils.java new file mode 100644 index 0000000..bdf3dec --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/util/FloatMathUtils.java @@ -0,0 +1,19 @@ +package ru.windcorp.progressia.common.util; + +import org.apache.commons.math3.util.FastMath; + +public class FloatMathUtils { + + public static final float PI_F = (float) Math.PI; + + public static float floor(float x) { + return (float) FastMath.floor(x); + } + + public static float normalizeAngle(float a) { + return a - 2*PI_F * floor((a + PI_F) / (2*PI_F)); + } + + private FloatMathUtils() {} + +} diff --git a/src/main/java/ru/windcorp/progressia/common/util/Matrices.java b/src/main/java/ru/windcorp/progressia/common/util/Matrices.java new file mode 100644 index 0000000..bc60c22 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/util/Matrices.java @@ -0,0 +1,65 @@ +package ru.windcorp.progressia.common.util; + +import glm.mat._3.Mat3; +import glm.mat._4.Mat4; +import glm.mat._4.d.Mat4d; + +/** + * A set of caches for GLM matrix objects. Use this instead of allocating new + * matrices when the objects are effectively local. + *

+ * All {@code grab}bed objects must be {@code release}d as soon as possible. + * Ideally, user code should be: + *

+ * Mat4 myMatrix = Vectors.grab4();
+ * try {
+ *     // use myMatrix
+ * } finally {
+ *     Matrices.release(myMatrix);
+ * }
+ * 
+ * Provided objects may be reused after {@code release} has been invoked; + * do not store them. + *

+ * This class is thread- and recursion-safe. + * + * @see Vectors + */ +public class Matrices { + + private static final LowOverheadCache MAT3S = + new LowOverheadCache<>(Mat3::new); + + public static Mat3 grab3() { + return MAT3S.grab(); + } + + public static void release(Mat3 m) { + MAT3S.release(m); + } + + private static final LowOverheadCache MAT4S = + new LowOverheadCache<>(Mat4::new); + + public static Mat4 grab4() { + return MAT4S.grab(); + } + + public static void release(Mat4 m) { + MAT4S.release(m); + } + + private static final LowOverheadCache MAT4DS = + new LowOverheadCache<>(Mat4d::new); + + public static Mat4d grab4d() { + return MAT4DS.grab(); + } + + public static void release(Mat4d m) { + MAT4DS.release(m); + } + + private Matrices() {} + +} diff --git a/src/main/java/ru/windcorp/progressia/common/util/Vectors.java b/src/main/java/ru/windcorp/progressia/common/util/Vectors.java index 2468707..489bf13 100644 --- a/src/main/java/ru/windcorp/progressia/common/util/Vectors.java +++ b/src/main/java/ru/windcorp/progressia/common/util/Vectors.java @@ -25,6 +25,8 @@ import glm.vec._4.i.Vec4i; * do not store them. *

* This class is thread- and recursion-safe. + * + * @see Matrices */ public class Vectors { 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 64033a6..681b779 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java +++ b/src/main/java/ru/windcorp/progressia/common/world/ChunkData.java @@ -20,18 +20,25 @@ package ru.windcorp.progressia.common.world; import static ru.windcorp.progressia.common.world.block.BlockFace.*; import java.util.ArrayList; +import java.util.Collections; import java.util.List; +import java.util.UUID; +import java.util.function.BiConsumer; import java.util.function.Consumer; import com.google.common.collect.Lists; +import glm.vec._2.Vec2; +import glm.vec._3.Vec3; import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.client.world.tile.TileLocation; import ru.windcorp.progressia.common.util.SizeLimitedList; import ru.windcorp.progressia.common.util.VectorUtil; import ru.windcorp.progressia.common.util.Vectors; import ru.windcorp.progressia.common.world.block.BlockData; import ru.windcorp.progressia.common.world.block.BlockDataRegistry; import ru.windcorp.progressia.common.world.block.BlockFace; +import ru.windcorp.progressia.common.world.entity.EntityData; import ru.windcorp.progressia.common.world.tile.TileData; import ru.windcorp.progressia.common.world.tile.TileDataRegistry; @@ -53,6 +60,9 @@ public class ChunkData { BLOCK_FACE_COUNT ]; + private final List entities = + Collections.synchronizedList(new ArrayList<>()); + public ChunkData(int x, int y, int z, WorldData world) { this.position.set(x, y, z); this.world = world; @@ -121,6 +131,14 @@ public class ChunkData { } } } + + EntityData javapony = new EntityData("Test", "Javapony"); + javapony.setUUID(UUID.nameUUIDFromBytes(new byte[] {42})); + javapony.setPosition(new Vec3(-6, -6, 20)); + javapony.setDirection(new Vec2( + (float) Math.toRadians(40), (float) Math.toRadians(45) + )); + getEntities().add(javapony); } public BlockData getBlock(Vec3i posInChunk) { @@ -214,6 +232,10 @@ public class ChunkData { face.getId(); } + public List getEntities() { + return entities; + } + private static void checkLocalCoordinates(Vec3i posInChunk) { if (!isInBounds(posInChunk)) { throw new IllegalArgumentException( @@ -249,6 +271,38 @@ public class ChunkData { action ); } + + /** + * Iterates over all tiles in this chunk. Tiles are referenced using their + * primary block (so that the face is + * {@linkplain BlockFace#isPrimary() primary}). + * + * @param action the action to perform. {@code TileLocation} refers to each + * tile using its primary block + */ + public void forEachTile(BiConsumer action) { + TileLocation loc = new TileLocation(); + + forEachBlock(blockInChunk -> { + loc.pos.set(blockInChunk.x, blockInChunk.y, blockInChunk.z); + + for (BlockFace face : BlockFace.getPrimaryFaces()) { + List list = getTilesOrNull(blockInChunk, face); + if (list == null) continue; + + loc.face = face; + + for (loc.layer = 0; loc.layer < list.size(); ++loc.layer) { + TileData tile = list.get(loc.layer); + action.accept(loc, tile); + } + } + }); + } + + public void forEachEntity(Consumer action) { + getEntities().forEach(action); + } public int getX() { return position.x; diff --git a/src/main/java/ru/windcorp/progressia/common/world/block/BlockFace.java b/src/main/java/ru/windcorp/progressia/common/world/block/BlockFace.java index b9ed2cc..f33f8d7 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/block/BlockFace.java +++ b/src/main/java/ru/windcorp/progressia/common/world/block/BlockFace.java @@ -18,6 +18,7 @@ package ru.windcorp.progressia.common.world.block; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; import glm.vec._3.i.Vec3i; @@ -58,6 +59,21 @@ public final class BlockFace extends BlockRelation { b.counterFace = a; } + public static ImmutableMap mapToFaces( + E top, E bottom, + E north, E south, + E east, E west + ) { + return ImmutableMap.builderWithExpectedSize(6) + .put(TOP, top) + .put(BOTTOM, bottom) + .put(NORTH, north) + .put(SOUTH, south) + .put(EAST, east) + .put(WEST, west) + .build(); + } + private static int nextId = 0; private final int id; diff --git a/src/main/java/ru/windcorp/progressia/common/world/entity/EntityData.java b/src/main/java/ru/windcorp/progressia/common/world/entity/EntityData.java new file mode 100644 index 0000000..1748104 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/entity/EntityData.java @@ -0,0 +1,62 @@ +package ru.windcorp.progressia.common.world.entity; + +import java.util.UUID; + +import glm.vec._2.Vec2; +import glm.vec._3.Vec3; +import ru.windcorp.progressia.common.util.Namespaced; + +public class EntityData extends Namespaced { + + private final Vec3 position = new Vec3(); + private final Vec3 velocity = new Vec3(); + + private final Vec2 direction = new Vec2(); + + private UUID uuid; + + public EntityData(String namespace, String name) { + super(namespace, name); + } + + public Vec3 getPosition() { + return position; + } + + public void setPosition(Vec3 position) { + this.position.set(position); + } + + public Vec3 getVelocity() { + return velocity; + } + + public void setVelocity(Vec3 velocity) { + this.velocity.set(velocity); + } + + public Vec2 getDirection() { + return direction; + } + + public void setDirection(Vec2 direction) { + this.direction.set(direction.x, direction.y); + } + + public float getYaw() { + return getDirection().x; + } + + public float getPitch() { + return getDirection().y; + } + + public UUID getUUID() { + return uuid; + } + + public void setUUID(UUID uuid) { + this.uuid = uuid; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/entity/EntityDataRegistry.java b/src/main/java/ru/windcorp/progressia/common/world/entity/EntityDataRegistry.java new file mode 100644 index 0000000..8457541 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/entity/EntityDataRegistry.java @@ -0,0 +1,13 @@ +package ru.windcorp.progressia.common.world.entity; + +import ru.windcorp.progressia.common.util.NamespacedRegistry; + +public class EntityDataRegistry extends NamespacedRegistry { + + private static final EntityDataRegistry INSTANCE = new EntityDataRegistry(); + + public static EntityDataRegistry getInstance() { + return INSTANCE; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java b/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java index cfff2b0..6f9381b 100644 --- a/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java +++ b/src/main/java/ru/windcorp/progressia/server/comms/ClientManager.java @@ -4,12 +4,14 @@ import java.util.Collection; import java.util.Collections; import java.util.concurrent.atomic.AtomicInteger; +import glm.vec._3.i.Vec3i; 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.common.comms.packets.PacketSetLocalPlayer; import ru.windcorp.progressia.server.Server; public class ClientManager { @@ -36,6 +38,11 @@ public class ClientManager { clientsById.put(client.getId(), client); client.addListener(new DefaultServerCommsListener(this, client)); + + client.sendPacket(new PacketSetLocalPlayer( + server.getWorld().getData().getChunk(new Vec3i(0, 0, 0)) + .getEntities().get(0).getUUID() + )); } public void disconnectClient(Client client) { diff --git a/src/main/java/ru/windcorp/progressia/server/world/ChunkLogic.java b/src/main/java/ru/windcorp/progressia/server/world/ChunkLogic.java index 9b489a4..6ba1087 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/ChunkLogic.java +++ b/src/main/java/ru/windcorp/progressia/server/world/ChunkLogic.java @@ -2,43 +2,82 @@ package ru.windcorp.progressia.server.world; import java.util.ArrayList; import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.WeakHashMap; import java.util.function.BiConsumer; +import com.google.common.collect.Lists; + import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.client.world.tile.TileLocation; import ru.windcorp.progressia.common.world.ChunkData; +import ru.windcorp.progressia.common.world.block.BlockFace; +import ru.windcorp.progressia.common.world.tile.TileData; import ru.windcorp.progressia.server.world.block.BlockLogic; import ru.windcorp.progressia.server.world.block.BlockLogicRegistry; -import ru.windcorp.progressia.server.world.block.Tickable; +import ru.windcorp.progressia.server.world.block.TickableBlock; +import ru.windcorp.progressia.server.world.tile.TickableTile; +import ru.windcorp.progressia.server.world.tile.TileLogic; +import ru.windcorp.progressia.server.world.tile.TileLogicRegistry; public class ChunkLogic { private final WorldLogic world; private final ChunkData data; - private final Collection ticking = new ArrayList<>(); + private final Collection tickingBlocks = new ArrayList<>(); + private final Collection tickingTiles = new ArrayList<>(); + + private final Map, List> tileLogicLists = + Collections.synchronizedMap(new WeakHashMap<>()); public ChunkLogic(WorldLogic world, ChunkData data) { this.world = world; this.data = data; - generateTickList(); + generateTickLists(); } - private void generateTickList() { + private void generateTickLists() { MutableBlockTickContext blockTickContext = new MutableBlockTickContext(); + MutableTileTickContext tileTickContext = + new MutableTileTickContext(); + blockTickContext.setWorld(getWorld()); blockTickContext.setChunk(this); + tileTickContext.setWorld(getWorld()); + tileTickContext.setChunk(this); + data.forEachBlock(blockInChunk -> { BlockLogic block = getBlock(blockInChunk); - if (block instanceof Tickable) { + if (block instanceof TickableBlock) { blockTickContext.setCoordsInChunk(blockInChunk); - if (((Tickable) block).doesTickRegularly(blockTickContext)) { - ticking.add(new Vec3i(blockInChunk)); + if (((TickableBlock) block) + .doesTickRegularly(blockTickContext) + ) { + tickingBlocks.add(new Vec3i(blockInChunk)); + } + } + }); + + data.forEachTile((loc, tileData) -> { + TileLogic tile = + TileLogicRegistry.getInstance().get(tileData.getId()); + + if (tile instanceof TickableTile) { + tileTickContext.setCoordsInChunk(loc.pos); + tileTickContext.setFace(loc.face); + tileTickContext.setLayer(loc.layer); + + if (((TickableTile) tile).doesTickRegularly(tileTickContext)) { + tickingTiles.add(new TileLocation(loc)); } } }); @@ -53,19 +92,57 @@ public class ChunkLogic { } public boolean hasTickingBlocks() { - return ticking.isEmpty(); + return !tickingBlocks.isEmpty(); + } + + public boolean hasTickingTiles() { + return !tickingTiles.isEmpty(); } public void forEachTickingBlock(BiConsumer action) { - ticking.forEach(blockInChunk -> { + tickingBlocks.forEach(blockInChunk -> { action.accept(blockInChunk, getBlock(blockInChunk)); }); } + public void forEachTickingTile(BiConsumer action) { + tickingTiles.forEach(location -> { + action.accept( + location, + getTilesOrNull(location.pos, location.face) + .get(location.layer) + ); + }); + } + public BlockLogic getBlock(Vec3i blockInChunk) { return BlockLogicRegistry.getInstance().get( getData().getBlock(blockInChunk).getId() ); } + + public List getTiles(Vec3i blockInChunk, BlockFace face) { + return wrapTileList(getData().getTiles(blockInChunk, face)); + } + + public List getTilesOrNull(Vec3i blockInChunk, BlockFace face) { + List tiles = getData().getTilesOrNull(blockInChunk, face); + if (tiles == null) return null; + return wrapTileList(tiles); + } + + private List wrapTileList(List tileDataList) { + return tileLogicLists.computeIfAbsent( + tileDataList, + ChunkLogic::createWrapper + ); + } + + private static List createWrapper(List tileDataList) { + return Lists.transform( + tileDataList, + data -> TileLogicRegistry.getInstance().get(data.getId()) + ); + } } diff --git a/src/main/java/ru/windcorp/progressia/server/world/MutableBlockTickContext.java b/src/main/java/ru/windcorp/progressia/server/world/MutableBlockTickContext.java index 0b03a5c..b23731a 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/MutableBlockTickContext.java +++ b/src/main/java/ru/windcorp/progressia/server/world/MutableBlockTickContext.java @@ -3,57 +3,15 @@ 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.world.block.BlockTickContext; -public class MutableBlockTickContext implements BlockTickContext { - - private double tickLength; - - private Server server; - private WorldLogic world; - - private ChunkLogic chunk; +public class MutableBlockTickContext +extends MutableChunkTickContext +implements BlockTickContext { 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; @@ -82,10 +40,4 @@ public class MutableBlockTickContext implements BlockTickContext { ); } - @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/MutableChunkTickContext.java b/src/main/java/ru/windcorp/progressia/server/world/MutableChunkTickContext.java new file mode 100644 index 0000000..775cc1e --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/world/MutableChunkTickContext.java @@ -0,0 +1,66 @@ +package ru.windcorp.progressia.server.world; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.world.block.BlockFace; +import ru.windcorp.progressia.server.Server; + +public class MutableChunkTickContext implements ChunkTickContext { + + private double tickLength; + private Server server; + private WorldLogic world; + private ChunkLogic chunk; + + public MutableChunkTickContext() { + super(); + } + + 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 void requestBlockTick(Vec3i blockInWorld) { + // TODO implement + throw new UnsupportedOperationException("Not yet implemented"); + } + + @Override + public void requestTileTick(Vec3i blockInWorld, BlockFace face, int layer) { + // TODO implement + throw new UnsupportedOperationException("Not yet implemented"); + } + +} \ No newline at end of file diff --git a/src/main/java/ru/windcorp/progressia/server/world/MutableTileTickContext.java b/src/main/java/ru/windcorp/progressia/server/world/MutableTileTickContext.java new file mode 100644 index 0000000..6770834 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/world/MutableTileTickContext.java @@ -0,0 +1,65 @@ +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.common.world.block.BlockFace; +import ru.windcorp.progressia.server.world.tile.TileTickContext; + +public class MutableTileTickContext +extends MutableChunkTickContext +implements TileTickContext { + + private final Vec3i blockInWorld = new Vec3i(); + private final Vec3i blockInChunk = new Vec3i(); + + private BlockFace face; + private int layer; + + @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 BlockFace getFace() { + return face; + } + + public void setFace(BlockFace face) { + this.face = face; + } + + @Override + public int getLayer() { + return layer; + } + + public void setLayer(int layer) { + this.layer = layer; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/world/TickContext.java b/src/main/java/ru/windcorp/progressia/server/world/TickContext.java index d572fec..73d43e0 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/TickContext.java +++ b/src/main/java/ru/windcorp/progressia/server/world/TickContext.java @@ -2,6 +2,7 @@ package ru.windcorp.progressia.server.world; import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.common.world.WorldData; +import ru.windcorp.progressia.common.world.block.BlockFace; import ru.windcorp.progressia.server.Server; public interface TickContext { @@ -19,5 +20,7 @@ public interface TickContext { } void requestBlockTick(Vec3i blockInWorld); + + void requestTileTick(Vec3i blockInWorld, BlockFace face, int layer); } diff --git a/src/main/java/ru/windcorp/progressia/server/world/Ticker.java b/src/main/java/ru/windcorp/progressia/server/world/Ticker.java index 975ddcb..ec4b4e5 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/Ticker.java +++ b/src/main/java/ru/windcorp/progressia/server/world/Ticker.java @@ -1,7 +1,8 @@ package ru.windcorp.progressia.server.world; import ru.windcorp.progressia.server.Server; -import ru.windcorp.progressia.server.world.block.Tickable; +import ru.windcorp.progressia.server.world.block.TickableBlock; +import ru.windcorp.progressia.server.world.tile.TickableTile; public class Ticker implements Runnable { @@ -11,6 +12,9 @@ public class Ticker implements Runnable { private final MutableBlockTickContext blockTickContext = new MutableBlockTickContext(); + private final MutableTileTickContext tileTickContext = + new MutableTileTickContext(); + private final Server server; public Ticker(Server server) { @@ -26,30 +30,41 @@ public class Ticker implements Runnable { } private void tickChunk(ChunkLogic chunk) { - MutableBlockTickContext context = this.blockTickContext; + MutableBlockTickContext blockContext = this.blockTickContext; + MutableTileTickContext tileContext = this.tileTickContext; - context.setServer(server); - context.setChunk(chunk); + blockContext.setServer(server); + tileContext.setServer(server); - tickRegularBlocks(chunk, context); - tickRandomBlocks(chunk, context); + tickRegularTickers(chunk, blockContext, tileContext); + tickRandomBlocks(chunk, blockContext, tileContext); flushChanges(chunk); } - private void tickRegularBlocks( + private void tickRegularTickers( ChunkLogic chunk, - MutableBlockTickContext context + MutableBlockTickContext blockContext, + MutableTileTickContext tileContext ) { chunk.forEachTickingBlock((blockInChunk, block) -> { - context.setCoordsInChunk(blockInChunk); - ((Tickable) block).tick(context, tracker); + blockContext.setCoordsInChunk(blockInChunk); + ((TickableBlock) block).tick(blockContext, tracker); + }); + + chunk.forEachTickingTile((locInChunk, tile) -> { + tileContext.setCoordsInChunk(locInChunk.pos); + tileContext.setFace(locInChunk.face); + tileContext.setLayer(locInChunk.layer); + + ((TickableTile) tile).tick(tileContext, tracker); }); } private void tickRandomBlocks( ChunkLogic chunk, - MutableBlockTickContext context + MutableBlockTickContext blockContext, + MutableTileTickContext tileContext ) { // TODO implement } diff --git a/src/main/java/ru/windcorp/progressia/server/world/block/ForwardingBlockTickContext.java b/src/main/java/ru/windcorp/progressia/server/world/block/ForwardingBlockTickContext.java index 49bb42c..03be3be 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/block/ForwardingBlockTickContext.java +++ b/src/main/java/ru/windcorp/progressia/server/world/block/ForwardingBlockTickContext.java @@ -4,11 +4,12 @@ import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.common.world.ChunkData; import ru.windcorp.progressia.common.world.WorldData; import ru.windcorp.progressia.common.world.block.BlockData; +import ru.windcorp.progressia.common.world.block.BlockFace; import ru.windcorp.progressia.server.Server; import ru.windcorp.progressia.server.world.ChunkLogic; import ru.windcorp.progressia.server.world.WorldLogic; -public class ForwardingBlockTickContext { +public class ForwardingBlockTickContext implements BlockTickContext { private BlockTickContext parent; @@ -24,46 +25,62 @@ public class ForwardingBlockTickContext { this.parent = parent; } + @Override public ChunkLogic getChunk() { return parent.getChunk(); } - + + @Override public ChunkData getChunkData() { return parent.getChunkData(); } + @Override public double getTickLength() { return parent.getTickLength(); } + @Override public Server getServer() { return parent.getServer(); } + @Override public Vec3i getCoords() { return parent.getCoords(); } + @Override public WorldLogic getWorld() { return parent.getWorld(); } + @Override public WorldData getWorldData() { return parent.getWorldData(); } + @Override public Vec3i getChunkCoords() { return parent.getChunkCoords(); } + @Override public void requestBlockTick(Vec3i blockInWorld) { parent.requestBlockTick(blockInWorld); } + @Override + public void requestTileTick(Vec3i blockInWorld, BlockFace face, int layer) { + parent.requestTileTick(blockInWorld, face, layer); + } + + @Override public BlockLogic getBlock() { return parent.getBlock(); } + @Override public BlockData getBlockData() { return parent.getBlockData(); } diff --git a/src/main/java/ru/windcorp/progressia/server/world/block/Tickable.java b/src/main/java/ru/windcorp/progressia/server/world/block/TickableBlock.java similarity index 87% rename from src/main/java/ru/windcorp/progressia/server/world/block/Tickable.java rename to src/main/java/ru/windcorp/progressia/server/world/block/TickableBlock.java index cb12d80..4bdfceb 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/block/Tickable.java +++ b/src/main/java/ru/windcorp/progressia/server/world/block/TickableBlock.java @@ -2,7 +2,7 @@ package ru.windcorp.progressia.server.world.block; import ru.windcorp.progressia.server.world.Changer; -public interface Tickable { +public interface TickableBlock { void tick(BlockTickContext context, Changer changer); diff --git a/src/main/java/ru/windcorp/progressia/server/world/block/Updatable.java b/src/main/java/ru/windcorp/progressia/server/world/block/UpdatableBlock.java similarity index 79% rename from src/main/java/ru/windcorp/progressia/server/world/block/Updatable.java rename to src/main/java/ru/windcorp/progressia/server/world/block/UpdatableBlock.java index ab13886..b76f6da 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/block/Updatable.java +++ b/src/main/java/ru/windcorp/progressia/server/world/block/UpdatableBlock.java @@ -2,7 +2,7 @@ package ru.windcorp.progressia.server.world.block; import ru.windcorp.progressia.server.world.Changer; -public interface Updatable { +public interface UpdatableBlock { void update(BlockTickContext context, Changer changer); diff --git a/src/main/java/ru/windcorp/progressia/server/world/entity/EntityLogic.java b/src/main/java/ru/windcorp/progressia/server/world/entity/EntityLogic.java new file mode 100644 index 0000000..c3c09e6 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/world/entity/EntityLogic.java @@ -0,0 +1,11 @@ +package ru.windcorp.progressia.server.world.entity; + +import ru.windcorp.progressia.common.util.Namespaced; + +public class EntityLogic extends Namespaced { + + public EntityLogic(String namespace, String name) { + super(namespace, name); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/world/entity/EntityLogicRegistry.java b/src/main/java/ru/windcorp/progressia/server/world/entity/EntityLogicRegistry.java new file mode 100644 index 0000000..21e37f9 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/world/entity/EntityLogicRegistry.java @@ -0,0 +1,14 @@ +package ru.windcorp.progressia.server.world.entity; + +import ru.windcorp.progressia.common.util.NamespacedRegistry; + +public class EntityLogicRegistry extends NamespacedRegistry { + + private static final EntityLogicRegistry INSTANCE = + new EntityLogicRegistry(); + + public static EntityLogicRegistry getInstance() { + return INSTANCE; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/world/tile/ForwardingTileTickContext.java b/src/main/java/ru/windcorp/progressia/server/world/tile/ForwardingTileTickContext.java new file mode 100644 index 0000000..0f2c29e --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/world/tile/ForwardingTileTickContext.java @@ -0,0 +1,132 @@ +package ru.windcorp.progressia.server.world.tile; + +import java.util.List; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.world.ChunkData; +import ru.windcorp.progressia.common.world.WorldData; +import ru.windcorp.progressia.common.world.block.BlockData; +import ru.windcorp.progressia.common.world.block.BlockFace; +import ru.windcorp.progressia.common.world.tile.TileData; +import ru.windcorp.progressia.server.Server; +import ru.windcorp.progressia.server.world.ChunkLogic; +import ru.windcorp.progressia.server.world.WorldLogic; +import ru.windcorp.progressia.server.world.block.BlockLogic; + +public class ForwardingTileTickContext implements TileTickContext { + + private TileTickContext parent; + + public ForwardingTileTickContext(TileTickContext parent) { + this.parent = parent; + } + + public TileTickContext getParent() { + return parent; + } + + public void setParent(TileTickContext parent) { + this.parent = parent; + } + + @Override + public ChunkLogic getChunk() { + return parent.getChunk(); + } + + @Override + public ChunkData getChunkData() { + return parent.getChunkData(); + } + + @Override + public double getTickLength() { + return parent.getTickLength(); + } + + @Override + public Server getServer() { + return parent.getServer(); + } + + @Override + public WorldLogic getWorld() { + return parent.getWorld(); + } + + @Override + public WorldData getWorldData() { + return parent.getWorldData(); + } + + @Override + public void requestBlockTick(Vec3i blockInWorld) { + parent.requestBlockTick(blockInWorld); + } + + @Override + public void requestTileTick(Vec3i blockInWorld, BlockFace face, int layer) { + parent.requestTileTick(blockInWorld, face, layer); + } + + @Override + public Vec3i getCoords() { + return parent.getCoords(); + } + + @Override + public Vec3i getChunkCoords() { + return parent.getChunkCoords(); + } + + @Override + public BlockFace getFace() { + return parent.getFace(); + } + + @Override + public int getLayer() { + return parent.getLayer(); + } + + @Override + public TileLogic getTile() { + return parent.getTile(); + } + + @Override + public TileData getTileData() { + return parent.getTileData(); + } + + @Override + public List getTiles() { + return parent.getTiles(); + } + + @Override + public List getTilesOrNull() { + return parent.getTilesOrNull(); + } + + @Override + public List getTileDataList() { + return parent.getTileDataList(); + } + + @Override + public List getTileDataListOrNull() { + return parent.getTileDataListOrNull(); + } + + @Override + public BlockLogic getBlock() { + return parent.getBlock(); + } + + @Override + public BlockData getBlockData() { + return parent.getBlockData(); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/world/tile/TickableTile.java b/src/main/java/ru/windcorp/progressia/server/world/tile/TickableTile.java new file mode 100644 index 0000000..d078ba0 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/world/tile/TickableTile.java @@ -0,0 +1,17 @@ +package ru.windcorp.progressia.server.world.tile; + +import ru.windcorp.progressia.server.world.Changer; + +public interface TickableTile { + + void tick(TileTickContext context, Changer changer); + + default boolean doesTickRegularly(TileTickContext context) { + return false; + } + + default boolean doesTickRandomly(TileTickContext context) { + return false; + } + +} \ No newline at end of file diff --git a/src/main/java/ru/windcorp/progressia/server/world/tile/TileLogic.java b/src/main/java/ru/windcorp/progressia/server/world/tile/TileLogic.java new file mode 100644 index 0000000..f530c26 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/world/tile/TileLogic.java @@ -0,0 +1,28 @@ +package ru.windcorp.progressia.server.world.tile; + +import ru.windcorp.progressia.common.util.Namespaced; +import ru.windcorp.progressia.common.world.block.BlockFace; + +public class TileLogic extends Namespaced { + + public TileLogic(String namespace, String name) { + super(namespace, name); + } + + public boolean canOccupyFace(TileTickContext context) { + return canOccupyFace(context.getFace()); + } + + public boolean canOccupyFace(BlockFace face) { + return true; + } + + public boolean isSolid(TileTickContext context) { + return isSolid(); + } + + public boolean isSolid() { + return false; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/world/tile/TileLogicRegistry.java b/src/main/java/ru/windcorp/progressia/server/world/tile/TileLogicRegistry.java new file mode 100644 index 0000000..a5b522b --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/world/tile/TileLogicRegistry.java @@ -0,0 +1,13 @@ +package ru.windcorp.progressia.server.world.tile; + +import ru.windcorp.progressia.common.util.NamespacedRegistry; + +public class TileLogicRegistry extends NamespacedRegistry { + + private static final TileLogicRegistry INSTANCE = new TileLogicRegistry(); + + public static TileLogicRegistry getInstance() { + return INSTANCE; + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/world/tile/TileTickContext.java b/src/main/java/ru/windcorp/progressia/server/world/tile/TileTickContext.java new file mode 100644 index 0000000..0b80b5f --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/world/tile/TileTickContext.java @@ -0,0 +1,71 @@ +package ru.windcorp.progressia.server.world.tile; + +import java.util.List; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.world.block.BlockData; +import ru.windcorp.progressia.common.world.block.BlockFace; +import ru.windcorp.progressia.common.world.tile.TileData; +import ru.windcorp.progressia.server.world.ChunkTickContext; +import ru.windcorp.progressia.server.world.block.BlockLogic; + +public interface TileTickContext extends ChunkTickContext { + + /** + * Returns the current world coordinates. + * @return the world coordinates of the tile being ticked + */ + Vec3i getCoords(); + + /** + * Returns the current chunk coordinates. + * @return the chunk coordinates of the tile being ticked + */ + Vec3i getChunkCoords(); + + /** + * Returns the current block face. This face is always + * {@linkplain BlockFace#isPrimary() primary}. + * @return the block face that the tile being ticked occupies + */ + BlockFace getFace(); + + /** + * Returns the current layer. + * @return the layer that the tile being ticked occupies in the tile stack + */ + int getLayer(); + + default TileLogic getTile() { + return getTiles().get(getLayer()); + } + + default TileData getTileData() { + return getTileDataList().get(getLayer()); + } + + default List getTiles() { + return getChunk().getTiles(getChunkCoords(), getFace()); + } + + default List getTilesOrNull() { + return getChunk().getTilesOrNull(getChunkCoords(), getFace()); + } + + default List getTileDataList() { + return getChunkData().getTiles(getChunkCoords(), getFace()); + } + + default List getTileDataListOrNull() { + return getChunkData().getTilesOrNull(getChunkCoords(), getFace()); + } + + default BlockLogic getBlock() { + return getChunk().getBlock(getChunkCoords()); + } + + default BlockData getBlockData() { + return getChunkData().getBlock(getChunkCoords()); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/world/tile/UpdatableTile.java b/src/main/java/ru/windcorp/progressia/server/world/tile/UpdatableTile.java new file mode 100644 index 0000000..00613f0 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/world/tile/UpdatableTile.java @@ -0,0 +1,9 @@ +package ru.windcorp.progressia.server.world.tile; + +import ru.windcorp.progressia.server.world.Changer; + +public interface UpdatableTile { + + void update(TileTickContext context, Changer changer); + +} diff --git a/src/main/resources/assets/textures/entities/javapony.png b/src/main/resources/assets/textures/entities/javapony.png new file mode 100644 index 0000000..59ac59c Binary files /dev/null and b/src/main/resources/assets/textures/entities/javapony.png differ diff --git a/src/main/resources/log4j2.xml b/src/main/resources/log4j2.xml new file mode 100644 index 0000000..29aaea2 --- /dev/null +++ b/src/main/resources/log4j2.xml @@ -0,0 +1,34 @@ + + + + %d{yyyy-MM-dd'T'HH:mm:ss.SSSZ} %p %m%n + logs + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file