From fbc803d6e27d911970ac15aa29a9f19c06504062 Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Wed, 4 Aug 2021 18:42:16 +0300 Subject: [PATCH] Laid some groundwork for context implementation; rewrite incoming - ReusableServerContext will be the default context implementation. It is sort of implemented, but not really - WorldLogic{,RO} now declare getData() method - WorldGenericContextWO.removeEntity(EntityGeneric) received a default implementation - TileDataContext.getTag() received a default implementation --- .../common/world/context/TileDataContext.java | 7 +- .../context/WorldGenericContextWO.java | 4 +- .../server/world/DefaultWorldLogic.java | 1 + .../progressia/server/world/WorldLogic.java | 4 + .../progressia/server/world/WorldLogicRO.java | 3 + .../context/impl/ReusableServerContext.java | 122 ++++ .../impl/ReusableServerContextBuilders.java | 87 +++ .../impl/ReusableServerContextImpl.java | 519 ++++++++++++++++++ 8 files changed, 744 insertions(+), 3 deletions(-) create mode 100644 src/main/java/ru/windcorp/progressia/server/world/context/impl/ReusableServerContext.java create mode 100644 src/main/java/ru/windcorp/progressia/server/world/context/impl/ReusableServerContextBuilders.java create mode 100644 src/main/java/ru/windcorp/progressia/server/world/context/impl/ReusableServerContextImpl.java diff --git a/src/main/java/ru/windcorp/progressia/common/world/context/TileDataContext.java b/src/main/java/ru/windcorp/progressia/common/world/context/TileDataContext.java index c3e1321..79e8d51 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/context/TileDataContext.java +++ b/src/main/java/ru/windcorp/progressia/common/world/context/TileDataContext.java @@ -29,7 +29,10 @@ public interface TileDataContext extends TileGenericContextWO, BlockFaceDataContext, TileDataContextRO { - - // currently empty + + @Override + default int getTag() { + return getTileReference().getTag(); + } } diff --git a/src/main/java/ru/windcorp/progressia/common/world/generic/context/WorldGenericContextWO.java b/src/main/java/ru/windcorp/progressia/common/world/generic/context/WorldGenericContextWO.java index ddac83d..27bbb1a 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/generic/context/WorldGenericContextWO.java +++ b/src/main/java/ru/windcorp/progressia/common/world/generic/context/WorldGenericContextWO.java @@ -140,7 +140,9 @@ public interface WorldGenericContextWO< * @see #removeEntity(long) */ @Override - void removeEntity(E entity); + default void removeEntity(E entity) { + removeEntity(entity.getEntityId()); + } /** * Requests that the specified change is applied to the given entity. The diff --git a/src/main/java/ru/windcorp/progressia/server/world/DefaultWorldLogic.java b/src/main/java/ru/windcorp/progressia/server/world/DefaultWorldLogic.java index e38559d..d578fa7 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/DefaultWorldLogic.java +++ b/src/main/java/ru/windcorp/progressia/server/world/DefaultWorldLogic.java @@ -96,6 +96,7 @@ public class DefaultWorldLogic implements WorldLogic { return server; } + @Override public DefaultWorldData getData() { return data; } diff --git a/src/main/java/ru/windcorp/progressia/server/world/WorldLogic.java b/src/main/java/ru/windcorp/progressia/server/world/WorldLogic.java index 0995ce4..f0cdb0a 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/WorldLogic.java +++ b/src/main/java/ru/windcorp/progressia/server/world/WorldLogic.java @@ -20,6 +20,7 @@ package ru.windcorp.progressia.server.world; import java.util.Collection; import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.world.WorldData; import ru.windcorp.progressia.common.world.rels.BlockFace; public interface WorldLogic extends WorldLogicRO { @@ -27,6 +28,9 @@ public interface WorldLogic extends WorldLogicRO { /* * Override return types */ + + @Override + WorldData getData(); @Override ChunkLogic getChunk(Vec3i pos); diff --git a/src/main/java/ru/windcorp/progressia/server/world/WorldLogicRO.java b/src/main/java/ru/windcorp/progressia/server/world/WorldLogicRO.java index 1fc1549..7f98ef9 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/WorldLogicRO.java +++ b/src/main/java/ru/windcorp/progressia/server/world/WorldLogicRO.java @@ -17,6 +17,7 @@ */ package ru.windcorp.progressia.server.world; +import ru.windcorp.progressia.common.world.WorldDataRO; import ru.windcorp.progressia.common.world.entity.EntityData; import ru.windcorp.progressia.common.world.generic.WorldGenericRO; import ru.windcorp.progressia.server.world.block.BlockLogic; @@ -24,5 +25,7 @@ import ru.windcorp.progressia.server.world.tile.TileLogic; public interface WorldLogicRO extends WorldGenericRO { + + WorldDataRO getData(); } diff --git a/src/main/java/ru/windcorp/progressia/server/world/context/impl/ReusableServerContext.java b/src/main/java/ru/windcorp/progressia/server/world/context/impl/ReusableServerContext.java new file mode 100644 index 0000000..fdffd22 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/world/context/impl/ReusableServerContext.java @@ -0,0 +1,122 @@ +/* + * Progressia + * Copyright (C) 2020-2021 Wind Corporation and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package ru.windcorp.progressia.server.world.context.impl; + +import ru.windcorp.progressia.common.world.context.Context; +import ru.windcorp.progressia.server.world.WorldLogic; +import ru.windcorp.progressia.server.world.WorldLogicRO; +import ru.windcorp.progressia.server.world.context.ServerBlockContext; +import ru.windcorp.progressia.server.world.context.ServerBlockContextRO; +import ru.windcorp.progressia.server.world.context.ServerBlockFaceContext; +import ru.windcorp.progressia.server.world.context.ServerBlockFaceContextRO; +import ru.windcorp.progressia.server.world.context.ServerContext; +import ru.windcorp.progressia.server.world.context.ServerTileContext; +import ru.windcorp.progressia.server.world.context.ServerTileContextRO; +import ru.windcorp.progressia.server.world.context.ServerWorldContext; +import ru.windcorp.progressia.server.world.context.ServerWorldContextRO; + +/** + * An implementation of the entire {@link ServerContext} tree. The objects of + * this class are designed to be highly reusable, including reusability in + * {@linkplain Context#subcontexting subcontexting}. Use this implementation + * when a {@link WorldLogic} (or a {@link WorldLogicRO}) instance requires a + * context wrapper. + *

+ * Use other unutilized instances of {@link ReusableServerContext} or + * {@link #empty()} static method to acquire a usable instance. + *

+ * This class defines the outward-facing safe interface of the actual + * implementation located in {@link ReusableServerContextImpl}. The reasoning + * for creating a subclass is to allow a single instance to implement both + * {@linkplain ReusableServerContextBuilders builder interfaces} and the context + * interface without causing confusion around object states. + * + * @author javapony + */ +public abstract class ReusableServerContext implements ServerTileContext { + + /** + * An RSC can conform to a variety of different {@link Context} interfaces. + * Each compliance mode is identified by a Role. + */ + public enum Role { + /** + * This object has not been configured yet. + */ + NONE, + /** + * This object conforms to {@link ServerWorldContext} or + * {@link ServerWorldContextRO}. + */ + WORLD, + /** + * This object conforms to {@link ServerBlockContext} or + * {@link ServerBlockContextRO}. + */ + LOCATION, + /** + * This object conforms to {@link ServerBlockFaceContext} or + * {@link ServerBlockFaceContextRO}. + */ + TILE_STACK, + /** + * This object conforms to {@link ServerTileContext} or + * {@link ServerTileContextRO}. + */ + TILE + } + + /** + * Do not extend ReusableServerContext directly. Use + * {@link ReusableServerContextImpl} if this is truly necessary. + */ + ReusableServerContext() { + // do nothing + } + + /** + * Resets this object to its uninitialized state and returns a builder to + * reinitialize it. + * + * @return a {@link ReusableServerContextBuilders.Empty} instance that may + * be used to reinitialize this object + * @throws IllegalStateException if active subcontexting is detected. + * Detection is done on a best-effort basis; + * do not rely this exception + */ + public abstract ReusableServerContextBuilders.Empty reuse() throws IllegalStateException; + + /** + * Returns the {@link Role} currently assumed by this object. + * + * @return the role + */ + public abstract Role getRole(); + + /** + * Instantiates a new {@link ReusableServerContext} using an appropriate + * implementation. + * + * @return a {@link ReusableServerContextBuilders.Empty} instance that can + * be used to initialize this object + */ + public static ReusableServerContextBuilders.Empty empty() { + return new ReusableServerContextImpl(); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/world/context/impl/ReusableServerContextBuilders.java b/src/main/java/ru/windcorp/progressia/server/world/context/impl/ReusableServerContextBuilders.java new file mode 100644 index 0000000..349c66a --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/world/context/impl/ReusableServerContextBuilders.java @@ -0,0 +1,87 @@ +/* + * Progressia + * Copyright (C) 2020-2021 Wind Corporation and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package ru.windcorp.progressia.server.world.context.impl; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.world.generic.TileGenericReferenceRO; +import ru.windcorp.progressia.common.world.generic.TileGenericStackRO; +import ru.windcorp.progressia.common.world.rels.BlockFace; +import ru.windcorp.progressia.common.world.rels.RelFace; +import ru.windcorp.progressia.server.Server; +import ru.windcorp.progressia.server.world.WorldLogic; + +public interface ReusableServerContextBuilders { + + ReusableServerContext build(); + + public interface Empty /* does not extend RSCB */ { + + WithWorld in(Server server, WorldLogic world); + + default WithWorld inRealWorldOf(Server server) { + return in(server, server.getWorld()); + } + + } + + public interface WithWorld extends ReusableServerContextBuilders { + + WithLocation at(Vec3i location); + + default ReusableServerContext at(TileGenericReferenceRO reference) { + if (!reference.isValid()) { + throw new IllegalArgumentException("Reference " + reference + " is invalid"); + } + + TileGenericStackRO stack = reference.getStack(); + return at(stack.getBlockInWorld(null)).on(stack.getFace()).index(reference.getIndex()); + } + + } + + public interface WithLocation extends ReusableServerContextBuilders { + + WithTileStack on(RelFace side); + WithTileStack on(BlockFace side); + + default ReusableServerContext on(TileGenericReferenceRO reference) { + if (!reference.isValid()) { + throw new IllegalArgumentException("Reference " + reference + " is invalid"); + } + + TileGenericStackRO stack = reference.getStack(); + return on(stack.getFace()).index(reference.getIndex()); + } + + } + + public interface WithTileStack extends ReusableServerContextBuilders { + + ReusableServerContext index(int index); + + default ReusableServerContext index(TileGenericReferenceRO reference) { + if (!reference.isValid()) { + throw new IllegalArgumentException("Reference " + reference + " is invalid"); + } + + return index(reference.getIndex()); + } + + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/world/context/impl/ReusableServerContextImpl.java b/src/main/java/ru/windcorp/progressia/server/world/context/impl/ReusableServerContextImpl.java new file mode 100644 index 0000000..2336937 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/world/context/impl/ReusableServerContextImpl.java @@ -0,0 +1,519 @@ +/* + * Progressia + * Copyright (C) 2020-2021 Wind Corporation and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package ru.windcorp.progressia.server.world.context.impl; + +import java.util.Collection; +import java.util.Random; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.state.StateChange; +import ru.windcorp.progressia.common.state.StatefulObject; +import ru.windcorp.progressia.common.world.ChunkData; +import ru.windcorp.progressia.common.world.GravityModel; +import ru.windcorp.progressia.common.world.TileDataStack; +import ru.windcorp.progressia.common.world.WorldData; +import ru.windcorp.progressia.common.world.block.BlockData; +import ru.windcorp.progressia.common.world.entity.EntityData; +import ru.windcorp.progressia.common.world.generic.EntityGeneric; +import ru.windcorp.progressia.common.world.rels.BlockFace; +import ru.windcorp.progressia.common.world.rels.RelFace; +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.TileLogicStack; +import ru.windcorp.progressia.server.world.WorldLogic; +import ru.windcorp.progressia.server.world.context.ServerTileContext; + +class ReusableServerContextImpl extends ReusableServerContext + implements ReusableServerContextBuilders.Empty, ReusableServerContextBuilders.WithWorld, + ReusableServerContextBuilders.WithLocation, ReusableServerContextBuilders.WithTileStack { + + /* + * STATE MANAGEMENT & UTIL + */ + + public ReusableServerContextImpl() { + reuse(); + } + + /** + * The relevant {@link Server} instance. If this is {@code null}, the role + * is {@link Role#NONE}. + */ + protected Server server; + + /** + * The relevant {@link WorldLogic} instance. If this is {@code null}, the + * role is {@link Role#NONE}. + */ + protected WorldLogic world; + + /** + * The relevant location. If this is {@code null}, the role is + * {@link Role#WORLD} or {@link Role#NONE}. + */ + protected Vec3i location; + + /** + * A {@code final} reference to the {@link Vec3i} instance used by + * {@link #location}. + */ + protected final Vec3i locationVectorContainer = new Vec3i(); + + /** + * The relevant {@link RelFace}. If the role is {@link Role#TILE_STACK} or + * {@link Role#TILE}, this is not {@code null}. + */ + protected RelFace blockFace; + + /** + * The index of the relevant tile. This value is {@code -1} unless the role + * is {@link Role#TILE}. + */ + protected int index; + + /** + * Determines whether this object currently acts as a builder or a context. + */ + protected boolean isBuilder; + + /** + * Counts the instances of subcontexting that are currently active. This + * value increases by 1 when subcontexting begins and decreases by 1 when it + * ends. This is always 0 when the object is a builder. + */ + protected int subcontextDepth = 0; + + /** + * The Logic view returned by {@link #logic()}. + */ + protected final ReusableServerContextImpl.Logic logic = new Logic(); + + /** + * Returns the Role currently assumed by this object. + * + * @return the role + */ + @Override + public Role getRole() { + if (server == null) + return Role.NONE; + if (location == null) + return Role.WORLD; + if (blockFace == null) + return Role.LOCATION; + if (index == -1) + return Role.TILE_STACK; + return Role.TILE; + } + + /** + * Throws an {@link IllegalStateException} iff this object does not conform + * to the specified role. The object must not be a builder to pass the + * check. + * + * @param role the required role + * @return {@code true} (for convenience with {@code assert}) + * @throws IllegalStateException when the check fails + */ + public boolean requireContextRole(Role role) throws IllegalStateException { + + boolean ok = !isBuilder && getRole().compareTo(role) <= 0; + if (!ok) { + complainAboutIllegalState(role, false); + } + return true; + + } + + /** + * Throws an {@link IllegalStateException} iff this object does not conform + * to the specified role. The object must be a builder to pass the check. If + * {@code role} is {@code null}, any role except {@link Role#NONE} passes. + * + * @param role the required role or {@code null}, see above + * @return {@code true} (for convenience with {@code assert}) + * @throws IllegalStateException when the check fails + */ + public boolean requireBuilderRole(Role role) { + + boolean ok = isBuilder && role == null ? (getRole() != Role.NONE) : (getRole() == role); + if (!ok) { + complainAboutIllegalState(role, true); + } + return true; + + } + + private void complainAboutIllegalState(Role role, boolean builder) { + throw new IllegalStateException( + "Required " + (builder ? "builder for" : "context") + " " + role + + ", but I am currently " + this + ); + } + + @Override + public String toString() { + String result; + + switch (getRole()) { + case TILE: + result = String.format( + "ServerTileContext[x=%d, y=%d, z=%d, %s, index=%d]", + location.x, + location.y, + location.z, + blockFace, + index + ); + break; + case TILE_STACK: + result = String + .format("ServerBlockFaceContext[x=%d, y=%d, z=%d, %s]", location.x, location.y, location.z, blockFace); + break; + case LOCATION: + result = String.format("ServerBlockContext[x=%d, y=%d, z=%d]", location.x, location.y, location.z); + break; + case WORLD: + result = String.format("ServerWorldContext"); + break; + default: + result = "Uninitialized ReusableServerContext"; + break; + } + + if (isBuilder) { + result = "Builder for " + result; + } + + return result; + } + + /* + * RSC INTERFACE + */ + + @Override + public Empty reuse() { + + if (subcontextDepth != 0) { + throw new IllegalStateException("Resetting is not allowed when subcontexting"); + } + + server = null; + world = null; + location = null; + blockFace = null; + index = -1; + + isBuilder = true; + + return this; + + } + + /* + * BUILDER INTERFACE + */ + + @Override + public ReusableServerContext build() { + assert requireBuilderRole(null); + isBuilder = false; + return this; + } + + /* + * Empty + */ + + @Override + public WithWorld in(Server server, WorldLogic world) { + requireBuilderRole(Role.NONE); + this.server = server; + this.world = world; + return this; + } + + /* + * WithWorld + */ + + @Override + public WithLocation at(Vec3i location) { + requireBuilderRole(Role.WORLD); + this.location = this.locationVectorContainer; + this.location.set(location.x, location.y, location.z); + return this; + } + + /* + * WithLocation + */ + + @Override + public WithTileStack on(RelFace side) { + requireBuilderRole(Role.LOCATION); + this.blockFace = side; + return this; + } + + @Override + public WithTileStack on(BlockFace side) { + requireBuilderRole(Role.LOCATION); + this.blockFace = side.relativize(world.getData().getUp(location)); + return this; + } + + /* + * WithTileStack + */ + + @Override + public ReusableServerContext index(int index) { + requireBuilderRole(Role.TILE_STACK); + this.index = index; + return build(); + } + + /* + * ServerWorldContext.Logic STUFF + */ + + private class Logic implements ServerTileContext.Logic { + @Override + public boolean isReal() { + return ReusableServerContextImpl.this.isReal(); + } + + @Override + public Collection getChunks() { + return world.getChunks(); + } + + @Override + public Collection getEntities() { + return ReusableServerContextImpl.this.getEntities(); + } + + @Override + public EntityData getEntity(long entityId) { + return ReusableServerContextImpl.this.getEntity(entityId); + } + + @Override + public Server getServer() { + return ReusableServerContextImpl.this.getServer(); + } + + @Override + public Random getRandom() { + return ReusableServerContextImpl.this.getRandom(); + } + + @Override + public Vec3i getLocation() { + return ReusableServerContextImpl.this.getLocation(); + } + + @Override + public RelFace getFace() { + return ReusableServerContextImpl.this.getFace(); + } + + @Override + public int getLayer() { + return ReusableServerContextImpl.this.getLayer(); + } + + @Override + public TileLogicStack getTiles(Vec3i blockInWorld, BlockFace face) { + return world.getTiles(blockInWorld, face); + } + + @Override + public ChunkLogic getChunk(Vec3i pos) { + return world.getChunk(pos); + } + + @Override + public WorldData getData() { + return world.getData(); + } + + @Override + public ServerTileContext data() { + return ReusableServerContextImpl.this; + } + + @Override + public String toString() { + return ReusableServerContextImpl.this + ".Logic"; + } + } + + @Override + public Logic logic() { + return logic; + } + + /* + * LOCATION GETTERS + */ + + @Override + public Server getServer() { + assert requireContextRole(Role.WORLD); + return server; + } + + @Override + public Vec3i getLocation() { + assert requireContextRole(Role.LOCATION); + return location; + } + + @Override + public RelFace getFace() { + assert requireContextRole(Role.TILE_STACK); + return blockFace; + } + + @Override + public int getLayer() { + assert requireContextRole(Role.TILE); + return index; + } + + /* + * RO CONTEXT INTERFACE + */ + + @Override + public boolean isReal() { + assert requireContextRole(Role.WORLD); + return true; + } + + @Override + public Random getRandom() { + assert requireContextRole(Role.WORLD); + return server.getAdHocRandom(); + } + + @Override + public ChunkData getChunk(Vec3i pos) { + assert requireContextRole(Role.WORLD); + return world.getData().getChunk(pos); + } + + @Override + public Collection getChunks() { + assert requireContextRole(Role.WORLD); + return world.getData().getChunks(); + } + + @Override + public Collection getEntities() { + assert requireContextRole(Role.WORLD); + return world.getEntities(); + } + + @Override + public EntityData getEntity(long entityId) { + assert requireContextRole(Role.WORLD); + return world.getEntity(entityId); + } + + @Override + public GravityModel getGravityModel() { + assert requireContextRole(Role.WORLD); + return world.getData().getGravityModel(); + } + + @Override + public float getTime() { + assert requireContextRole(Role.WORLD); + return world.getData().getTime(); + } + + /* + * RO CONTEXT OPTIMIZATIONS + */ + + /* + * RW CONTEXT INTERFACE + */ + + @Override + public boolean isImmediate() { + assert requireContextRole(Role.WORLD); + return true; + } + + @Override + public void setBlock(Vec3i blockInWorld, BlockData block, boolean notify) { + assert requireContextRole(Role.WORLD); + world.getData().setBlock(blockInWorld, block, notify); + } + + @Override + public void addTile(Vec3i location, BlockFace face, TileData tile) { + assert requireContextRole(Role.WORLD); + world.getData().getTiles(location, face).addFarthest(tile); + } + + @Override + public void removeTile(Vec3i location, BlockFace face, int tag) { + assert requireContextRole(Role.WORLD); + TileDataStack stack = world.getData().getTilesOrNull(location, face); + if (stack == null) return; + int index = stack.getIndexByTag(tag); + if (index == -1) return; + stack.remove(index); + } + + @Override + public void addEntity(EntityData entity) { + assert requireContextRole(Role.WORLD); + world.getData().addEntity(entity); + } + + @Override + public void removeEntity(long entityId) { + assert requireContextRole(Role.WORLD); + world.getData().removeEntity(entityId); + } + + @Override + public void changeEntity(SE entity, StateChange change) { + assert requireContextRole(Role.WORLD); + world.getData().changeEntity(entity, change); + } + + @Override + public void advanceTime(float change) { + assert requireContextRole(Role.WORLD); + world.getData().advanceTime(change); + } + + /* + * RW CONTEXT OPTIMIZATIONS + */ + +}