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
This commit is contained in:
OLEGSHA 2021-08-04 18:42:16 +03:00
parent 1ee9a55d19
commit fbc803d6e2
Signed by: OLEGSHA
GPG Key ID: E57A4B08D64AFF7A
8 changed files with 744 additions and 3 deletions

View File

@ -29,7 +29,10 @@ public interface TileDataContext
extends TileGenericContextWO<BlockData, TileData, TileDataStack, TileDataReference, ChunkData, EntityData>,
BlockFaceDataContext,
TileDataContextRO {
// currently empty
@Override
default int getTag() {
return getTileReference().getTag();
}
}

View File

@ -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

View File

@ -96,6 +96,7 @@ public class DefaultWorldLogic implements WorldLogic {
return server;
}
@Override
public DefaultWorldData getData() {
return data;
}

View File

@ -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);

View File

@ -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<BlockLogic, TileLogic, TileLogicStackRO, TileLogicReferenceRO, ChunkLogicRO, EntityData> {
WorldDataRO getData();
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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.
* <p>
* Use other unutilized instances of {@link ReusableServerContext} or
* {@link #empty()} static method to acquire a usable instance.
* <p>
* 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();
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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());
}
}
}

View File

@ -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 <https://www.gnu.org/licenses/>.
*/
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<? extends ChunkLogic> getChunks() {
return world.getChunks();
}
@Override
public Collection<EntityData> 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<? extends ChunkData> getChunks() {
assert requireContextRole(Role.WORLD);
return world.getData().getChunks();
}
@Override
public Collection<EntityData> 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 <SE extends StatefulObject & EntityGeneric> void changeEntity(SE entity, StateChange<SE> 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
*/
}