A change to the class hierarchy of WorldLogic similar to prev commit

- Renamed ChunkLogic -> DefaultChunkLogic, WorldLogic ->
DefaultWorldLogic
- Created/rewritten TileLogicReference{,RO}, TileLogicStack{,RO},
ChunkLogic{,RO}
- Drafted up something for ServerWorldContext & friends, WIP

(see commit 9a32660 for more details)
This commit is contained in:
OLEGSHA 2021-07-31 16:01:25 +03:00
parent 9a326603cd
commit a338a00f1d
No known key found for this signature in database
GPG Key ID: C22EB07953E71DE2
29 changed files with 754 additions and 390 deletions

View File

@ -4,11 +4,9 @@ import ru.windcorp.progressia.common.world.block.BlockData;
import ru.windcorp.progressia.common.world.generic.ChunkGenericWO;
import ru.windcorp.progressia.common.world.tile.TileData;
public interface ChunkData extends ChunkDataRO, ChunkGenericWO<BlockData, TileData, TileDataStack, TileDataReference, ChunkData> {
public interface ChunkData
extends ChunkDataRO, ChunkGenericWO<BlockData, TileData, TileDataStack, TileDataReference, ChunkData> {
// @Override
// default TileDataStack getTiles(Vec3i blockInChunk, BlockFace face) {
// return null;
// }
// currently empty
}

View File

@ -37,6 +37,10 @@ import ru.windcorp.progressia.common.world.rels.RelFace;
import ru.windcorp.progressia.common.world.tile.TileData;
import ru.windcorp.progressia.common.world.tile.TileStackIsFullException;
/**
* The implementation of {@link ChunkData} used to store the actual game world.
* This class should be considered an implementation detail.
*/
public class DefaultChunkData implements ChunkData {
public static final int BLOCKS_PER_CHUNK = Coordinates.CHUNK_SIZE;
@ -222,7 +226,7 @@ public class DefaultChunkData implements ChunkData {
}
public void invalidate() {
this.index = 0;
this.index = -1;
}
@Override

View File

@ -34,7 +34,7 @@ import ru.windcorp.progressia.common.world.rels.BlockFace;
* An unmodifiable chunk representation. Per default, it is usually one of
* {@link ru.windcorp.progressia.common.world.DefaultChunkData ChunkData},
* {@link ru.windcorp.progressia.client.world.ChunkRender ChunkRender} or
* {@link ru.windcorp.progressia.server.world.ChunkLogic ChunkLogic}, but this
* {@link ru.windcorp.progressia.server.world.DefaultChunkLogic ChunkLogic}, but this
* interface may be implemented differently for various reasons.
* <p>
* A generic chunk contains {@linkplain BlockGeneric blocks} and

View File

@ -18,6 +18,16 @@
package ru.windcorp.progressia.common.world.generic;
/**
* A reference to a single tile in a tile stack. A {@code TileReference} remains
* valid until the tile is removed from its stack.
* <p>
* Tile reference objects may be reused for other tiles; {@link #isValid()} only
* shows if there exists <em>some</em> tile that this object references; it may
* or may not be the tile this reference was acquired for. It is the
* responsibility of the programmer to discard references when the tile is
* removed.
*/
// @formatter:off
public interface TileGenericReferenceRO<
B extends BlockGeneric,
@ -28,16 +38,49 @@ public interface TileGenericReferenceRO<
> {
// @formatter:on
T get();
/**
* Gets the index that the referenced tile currently occupies. This value
* may change as tiles are added to or removed from the stack.
*
* @return the index of the tile or {@code -1} if this reference is invalid
*/
int getIndex();
/**
* Gets the tile stack that contains the referenced tile.
*
* @return the tile stack of the relevant tile or {@code null} if this
* reference is invalid.
*/
TS getStack();
/**
* Gets the tile that this object references.
*
* @return the relevant tile or {@code null} if this reference is invalid
*/
default T get() {
return getStack().get(getIndex());
}
/**
* Checks whether this reference is valid. A reference is valid if it points
* to <em>some</em> tile; it may or may not be the tile that this reference
* was acquired for. (A tile reference can only change the referenced tile
* after the previous tile is removed from the stack.)
*
* @return {@code true} iff there exists a tile that this reference points
* to.
*/
default boolean isValid() {
return get() != null;
}
/**
* Gets the tag of the referenced tile.
*
* @return the tag or {@code -1} iff this reference is invalid.
*/
default int getTag() {
TS tileStack = getStack();
if (tileStack == null) {

View File

@ -34,7 +34,7 @@ import ru.windcorp.progressia.server.events.ServerEvent;
import ru.windcorp.progressia.server.management.load.ChunkRequestDaemon;
import ru.windcorp.progressia.server.management.load.EntityRequestDaemon;
import ru.windcorp.progressia.server.management.load.LoadManager;
import ru.windcorp.progressia.server.world.WorldLogic;
import ru.windcorp.progressia.server.world.DefaultWorldLogic;
import ru.windcorp.progressia.server.world.tasks.WorldAccessor;
import ru.windcorp.progressia.server.world.ticking.Change;
import ru.windcorp.progressia.server.world.ticking.Evaluation;
@ -53,7 +53,7 @@ public class Server {
return ServerThread.getCurrentServer();
}
private final WorldLogic world;
private final DefaultWorldLogic world;
private final WorldAccessor worldAccessor = new WorldAccessor(this);
private final ServerThread serverThread;
@ -69,7 +69,7 @@ public class Server {
private final TickingSettings tickingSettings = new TickingSettings();
public Server(DefaultWorldData world) {
this.world = new WorldLogic(
this.world = new DefaultWorldLogic(
world,
this,
new TestPlanetGenerator("Test:PlanetGenerator", this, new Planet(4, 9.8f, 16f, 16f))
@ -91,9 +91,9 @@ public class Server {
/**
* Returns this server's world.
*
* @return this server's {@link WorldLogic}
* @return this server's {@link DefaultWorldLogic}
*/
public WorldLogic getWorld() {
public DefaultWorldLogic getWorld() {
return world;
}

View File

@ -15,230 +15,27 @@
* 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;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.function.BiConsumer;
import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.world.DefaultChunkData;
import ru.windcorp.progressia.common.world.Coordinates;
import ru.windcorp.progressia.common.world.generic.ChunkGenericRO;
import ru.windcorp.progressia.common.world.rels.AbsFace;
import ru.windcorp.progressia.common.world.ChunkData;
import ru.windcorp.progressia.common.world.rels.BlockFace;
import ru.windcorp.progressia.common.world.rels.RelFace;
import ru.windcorp.progressia.common.world.TileDataStack;
import ru.windcorp.progressia.common.world.TileDataReference;
import ru.windcorp.progressia.server.world.block.BlockLogic;
import ru.windcorp.progressia.server.world.block.BlockLogicRegistry;
import ru.windcorp.progressia.server.world.block.TickableBlock;
import ru.windcorp.progressia.server.world.tasks.TickChunk;
import ru.windcorp.progressia.server.world.ticking.TickingPolicy;
import ru.windcorp.progressia.server.world.tile.TickableTile;
import ru.windcorp.progressia.server.world.tile.TileLogic;
import ru.windcorp.progressia.server.world.tile.TileLogicReference;
import ru.windcorp.progressia.server.world.tile.TileLogicRegistry;
import ru.windcorp.progressia.server.world.tile.TileLogicStack;
public class ChunkLogic implements ChunkGenericRO<BlockLogic, TileLogic, TileLogicStack, TileLogicReference, ChunkLogic> {
public interface ChunkLogic extends ChunkLogicRO {
private final WorldLogic world;
private final DefaultChunkData data;
private final Collection<Vec3i> tickingBlocks = new ArrayList<>();
private final Collection<TileDataReference> tickingTiles = new ArrayList<>();
private final TickChunk tickTask = new TickChunk(this);
private final Map<TileDataStack, TileLogicStackImpl> tileLogicLists = Collections
.synchronizedMap(new WeakHashMap<>());
public ChunkLogic(WorldLogic world, DefaultChunkData data) {
this.world = world;
this.data = data;
tmp_generateTickLists();
}
@Override
public Vec3i getPosition() {
return getData().getPosition();
}
/*
* Override return type
*/
@Override
public AbsFace getUp() {
return getData().getUp();
ChunkData getData();
@Override
default TileLogicStack getTilesOrNull(Vec3i blockInChunk, BlockFace face) {
return (TileLogicStack) ChunkLogicRO.super.getTilesOrNull(blockInChunk, face);
}
@Override
public BlockLogic getBlock(Vec3i blockInChunk) {
return BlockLogicRegistry.getInstance().get(
getData().getBlock(blockInChunk).getId()
);
}
@Override
public TileLogicStack getTiles(Vec3i blockInChunk, BlockFace face) {
return getTileStackWrapper(getData().getTiles(blockInChunk, face));
}
@Override
public boolean hasTiles(Vec3i blockInChunk, BlockFace face) {
return getData().hasTiles(blockInChunk, face);
}
private TileLogicStack getTileStackWrapper(TileDataStack tileDataList) {
return tileLogicLists.computeIfAbsent(
tileDataList,
TileLogicStackImpl::new
);
}
public WorldLogic getWorld() {
return world;
}
public DefaultChunkData getData() {
return data;
}
public boolean isReady() {
return getWorld().getGenerator().isChunkReady(getData().getGenerationHint());
}
public boolean hasTickingBlocks() {
return !tickingBlocks.isEmpty();
}
public boolean hasTickingTiles() {
return !tickingTiles.isEmpty();
}
public void forEachTickingBlock(BiConsumer<Vec3i, BlockLogic> action) {
tickingBlocks.forEach(blockInChunk -> {
action.accept(blockInChunk, getBlock(blockInChunk));
});
}
public void forEachTickingTile(BiConsumer<TileDataReference, TileLogic> action) {
tickingTiles.forEach(ref -> {
action.accept(
ref,
TileLogicRegistry.getInstance().get(ref.get().getId())
);
});
}
public TickChunk getTickTask() {
return tickTask;
}
private class TileLogicStackImpl extends TileLogicStack {
private class TileLogicReferenceImpl implements TileLogicReference {
private final TileDataReference parent;
public TileLogicReferenceImpl (TileDataReference parent) {
this.parent = parent;
}
@Override
public TileLogic get() {
return TileLogicRegistry.getInstance().get(parent.get().getId());
}
@Override
public int getIndex() {
return parent.getIndex();
}
@Override
public TileLogicStack getStack() {
return TileLogicStackImpl.this;
}
}
private final TileDataStack parent;
public TileLogicStackImpl(TileDataStack parent) {
this.parent = parent;
}
@Override
public Vec3i getBlockInChunk(Vec3i output) {
return parent.getBlockInChunk(output);
}
@Override
public ChunkLogic getChunk() {
return ChunkLogic.this;
}
@Override
public RelFace getFace() {
return parent.getFace();
}
@Override
public TileLogicReference getReference(int index) {
return new TileLogicReferenceImpl(parent.getReference(index));
}
@Override
public int getIndexByTag(int tag) {
return parent.getIndexByTag(tag);
}
@Override
public int getTagByIndex(int index) {
return parent.getTagByIndex(index);
}
@Override
public TileLogic get(int index) {
return TileLogicRegistry.getInstance().get(parent.get(index).getId());
}
@Override
public int size() {
return parent.size();
}
@Override
public TileDataStack getData() {
return parent;
}
}
private void tmp_generateTickLists() {
ChunkTickContext context = TickContextMutable.start().withChunk(this).build();
context.forEachBlock(bctxt -> {
BlockLogic block = bctxt.getBlock();
if (!(block instanceof TickableBlock))
return;
if (((TickableBlock) block).getTickingPolicy(bctxt) == TickingPolicy.REGULAR) {
tickingBlocks.add(Coordinates.convertInWorldToInChunk(bctxt.getBlockInWorld(), null));
}
bctxt.forEachFace(fctxt -> fctxt.forEachTile(tctxt -> {
TileLogic tile = tctxt.getTile();
if (!(tile instanceof TickableTile))
return;
if (((TickableTile) tile).getTickingPolicy(tctxt) == TickingPolicy.REGULAR) {
tickingTiles.add(tctxt.getReference());
}
}));
});
}
TileLogicStack getTiles(Vec3i blockInChunk, BlockFace face);
}

View File

@ -0,0 +1,15 @@
package ru.windcorp.progressia.server.world;
import ru.windcorp.progressia.common.world.ChunkDataRO;
import ru.windcorp.progressia.common.world.generic.ChunkGenericRO;
import ru.windcorp.progressia.server.world.block.BlockLogic;
import ru.windcorp.progressia.server.world.tile.TileLogic;
public interface ChunkLogicRO
extends ChunkGenericRO<BlockLogic, TileLogic, TileLogicStackRO, TileLogicReferenceRO, ChunkLogicRO> {
ChunkDataRO getData();
boolean isReady();
}

View File

@ -30,12 +30,12 @@ public interface ChunkTickContext extends TickContext {
Vec3i getChunk();
default ChunkLogic getChunkLogic() {
default DefaultChunkLogic getChunkLogic() {
return getWorld().getChunk(getChunk());
}
default DefaultChunkData getChunkData() {
ChunkLogic chunkLogic = getChunkLogic();
DefaultChunkLogic chunkLogic = getChunkLogic();
return chunkLogic == null ? null : chunkLogic.getData();
}

View File

@ -0,0 +1,249 @@
/*
* 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;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.function.BiConsumer;
import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.world.DefaultChunkData;
import ru.windcorp.progressia.common.world.Coordinates;
import ru.windcorp.progressia.common.world.rels.AbsFace;
import ru.windcorp.progressia.common.world.rels.BlockFace;
import ru.windcorp.progressia.common.world.rels.RelFace;
import ru.windcorp.progressia.common.world.TileDataStack;
import ru.windcorp.progressia.common.world.TileDataReference;
import ru.windcorp.progressia.server.world.block.BlockLogic;
import ru.windcorp.progressia.server.world.block.BlockLogicRegistry;
import ru.windcorp.progressia.server.world.block.TickableBlock;
import ru.windcorp.progressia.server.world.tasks.TickChunk;
import ru.windcorp.progressia.server.world.ticking.TickingPolicy;
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 DefaultChunkLogic implements ChunkLogic {
private final DefaultWorldLogic world;
private final DefaultChunkData data;
private final Collection<Vec3i> tickingBlocks = new ArrayList<>();
private final Collection<TileDataReference> tickingTiles = new ArrayList<>();
private final TickChunk tickTask = new TickChunk(this);
private final Map<TileDataStack, TileLogicStackImpl> tileLogicLists = Collections
.synchronizedMap(new WeakHashMap<>());
public DefaultChunkLogic(DefaultWorldLogic world, DefaultChunkData data) {
this.world = world;
this.data = data;
tmp_generateTickLists();
}
@Override
public Vec3i getPosition() {
return getData().getPosition();
}
@Override
public AbsFace getUp() {
return getData().getUp();
}
@Override
public BlockLogic getBlock(Vec3i blockInChunk) {
return BlockLogicRegistry.getInstance().get(
getData().getBlock(blockInChunk).getId()
);
}
@Override
public TileLogicStack getTiles(Vec3i blockInChunk, BlockFace face) {
return getTileStackWrapper(getData().getTiles(blockInChunk, face));
}
@Override
public boolean hasTiles(Vec3i blockInChunk, BlockFace face) {
return getData().hasTiles(blockInChunk, face);
}
private TileLogicStack getTileStackWrapper(TileDataStack tileDataList) {
return tileLogicLists.computeIfAbsent(
tileDataList,
TileLogicStackImpl::new
);
}
public DefaultWorldLogic getWorld() {
return world;
}
@Override
public DefaultChunkData getData() {
return data;
}
@Override
public boolean isReady() {
return getWorld().getGenerator().isChunkReady(getData().getGenerationHint());
}
public boolean hasTickingBlocks() {
return !tickingBlocks.isEmpty();
}
public boolean hasTickingTiles() {
return !tickingTiles.isEmpty();
}
public void forEachTickingBlock(BiConsumer<Vec3i, BlockLogic> action) {
tickingBlocks.forEach(blockInChunk -> {
action.accept(blockInChunk, getBlock(blockInChunk));
});
}
public void forEachTickingTile(BiConsumer<TileDataReference, TileLogic> action) {
tickingTiles.forEach(ref -> {
action.accept(
ref,
TileLogicRegistry.getInstance().get(ref.get().getId())
);
});
}
public TickChunk getTickTask() {
return tickTask;
}
private class TileLogicStackImpl extends AbstractList<TileLogic> implements TileLogicStack {
private class TileLogicReferenceImpl implements TileLogicReference {
private final TileDataReference parent;
public TileLogicReferenceImpl (TileDataReference parent) {
this.parent = parent;
}
@Override
public TileDataReference getDataReference() {
return parent;
}
@Override
public TileLogic get() {
return TileLogicRegistry.getInstance().get(parent.get().getId());
}
@Override
public int getIndex() {
return parent.getIndex();
}
@Override
public TileLogicStack getStack() {
return TileLogicStackImpl.this;
}
}
private final TileDataStack parent;
public TileLogicStackImpl(TileDataStack parent) {
this.parent = parent;
}
@Override
public Vec3i getBlockInChunk(Vec3i output) {
return parent.getBlockInChunk(output);
}
@Override
public DefaultChunkLogic getChunk() {
return DefaultChunkLogic.this;
}
@Override
public RelFace getFace() {
return parent.getFace();
}
@Override
public TileLogicReference getReference(int index) {
return new TileLogicReferenceImpl(parent.getReference(index));
}
@Override
public int getIndexByTag(int tag) {
return parent.getIndexByTag(tag);
}
@Override
public int getTagByIndex(int index) {
return parent.getTagByIndex(index);
}
@Override
public TileLogic get(int index) {
return TileLogicRegistry.getInstance().get(parent.get(index).getId());
}
@Override
public int size() {
return parent.size();
}
@Override
public TileDataStack getData() {
return parent;
}
}
private void tmp_generateTickLists() {
ChunkTickContext context = TickContextMutable.start().withChunk(this).build();
context.forEachBlock(bctxt -> {
BlockLogic block = bctxt.getBlock();
if (!(block instanceof TickableBlock))
return;
if (((TickableBlock) block).getTickingPolicy(bctxt) == TickingPolicy.REGULAR) {
tickingBlocks.add(Coordinates.convertInWorldToInChunk(bctxt.getBlockInWorld(), null));
}
bctxt.forEachFace(fctxt -> fctxt.forEachTile(tctxt -> {
TileLogic tile = tctxt.getTile();
if (!(tile instanceof TickableTile))
return;
if (((TickableTile) tile).getTickingPolicy(tctxt) == TickingPolicy.REGULAR) {
tickingTiles.add(tctxt.getReference());
}
}));
});
}
}

View File

@ -0,0 +1,146 @@
/*
* 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;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import glm.Glm;
import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.util.crash.CrashReports;
import ru.windcorp.progressia.common.world.DefaultChunkData;
import ru.windcorp.progressia.common.world.ChunkDataListeners;
import ru.windcorp.progressia.common.world.DefaultWorldData;
import ru.windcorp.progressia.common.world.WorldDataListener;
import ru.windcorp.progressia.common.world.entity.EntityData;
import ru.windcorp.progressia.server.Server;
import ru.windcorp.progressia.server.world.generation.WorldGenerator;
import ru.windcorp.progressia.server.world.tasks.TickEntitiesTask;
import ru.windcorp.progressia.server.world.ticking.Evaluation;
public class DefaultWorldLogic implements WorldLogic {
private final DefaultWorldData data;
private final Server server;
private final WorldGenerator generator;
private final Map<DefaultChunkData, DefaultChunkLogic> chunks = new HashMap<>();
private final Evaluation tickEntitiesTask = new TickEntitiesTask();
public DefaultWorldLogic(DefaultWorldData data, Server server, WorldGenerator generator) {
this.data = data;
this.server = server;
this.generator = generator;
data.setGravityModel(getGenerator().getGravityModel());
data.addListener(new WorldDataListener() {
@Override
public void onChunkLoaded(DefaultWorldData world, DefaultChunkData chunk) {
chunks.put(chunk, new DefaultChunkLogic(DefaultWorldLogic.this, chunk));
}
@Override
public void beforeChunkUnloaded(DefaultWorldData world, DefaultChunkData chunk) {
chunks.remove(chunk);
}
});
data.addListener(ChunkDataListeners.createAdder(new UpdateTriggerer(server)));
}
@Override
public DefaultChunkLogic getChunk(Vec3i pos) {
return chunks.get(getData().getChunk(pos));
}
@Override
public Collection<DefaultChunkLogic> getChunks() {
return chunks.values();
}
@Override
public Collection<EntityData> getEntities() {
return getData().getEntities();
}
@Override
public EntityData getEntity(long entityId) {
return getData().getEntity(entityId);
}
public Evaluation getTickEntitiesTask() {
return tickEntitiesTask;
}
public Server getServer() {
return server;
}
public DefaultWorldData getData() {
return data;
}
public WorldGenerator getGenerator() {
return generator;
}
public DefaultChunkData generate(Vec3i chunkPos) {
DefaultChunkData chunk = getGenerator().generate(chunkPos);
if (!Glm.equals(chunkPos, chunk.getPosition())) {
throw CrashReports.report(null, "Generator %s has generated a chunk at (%d; %d; %d) when requested to generate a chunk at (%d; %d; %d)",
getGenerator(),
chunk.getX(), chunk.getY(), chunk.getZ(),
chunkPos.x, chunkPos.y, chunkPos.z
);
}
if (getData().getChunk(chunk.getPosition()) != chunk) {
if (isChunkLoaded(chunkPos)) {
throw CrashReports.report(null, "Generator %s has returned a chunk different to the chunk that is located at (%d; %d; %d)",
getGenerator(),
chunkPos.x, chunkPos.y, chunkPos.z
);
} else {
throw CrashReports.report(null, "Generator %s has returned a chunk that is not loaded when requested to generate a chunk at (%d; %d; %d)",
getGenerator(),
chunkPos.x, chunkPos.y, chunkPos.z
);
}
}
if (!getChunk(chunk).isReady()) {
throw CrashReports.report(null, "Generator %s has returned a chunk that is not ready when requested to generate a chunk at (%d; %d; %d)",
getGenerator(),
chunkPos.x, chunkPos.y, chunkPos.z
);
}
return chunk;
}
public DefaultChunkLogic getChunk(DefaultChunkData chunkData) {
return chunks.get(chunkData);
}
}

View File

@ -44,7 +44,7 @@ public class TickAndUpdateUtil {
}
}
public static void tickBlock(WorldLogic world, Vec3i blockInWorld) {
public static void tickBlock(DefaultWorldLogic world, Vec3i blockInWorld) {
BlockLogic block = world.getBlock(blockInWorld);
if (!(block instanceof TickableBlock))
return; // also checks nulls
@ -61,7 +61,7 @@ public class TickAndUpdateUtil {
}
}
public static void tickTile(WorldLogic world, Vec3i blockInWorld, BlockFace face, int layer) {
public static void tickTile(DefaultWorldLogic world, Vec3i blockInWorld, BlockFace face, int layer) {
TileLogic tile = world.getTile(blockInWorld, face, layer);
if (!(tile instanceof TickableTile)) {
return;
@ -72,7 +72,7 @@ public class TickAndUpdateUtil {
tickTile((TickableTile) tile, tickContext);
}
public static void tickTiles(WorldLogic world, Vec3i blockInWorld, BlockFace face) {
public static void tickTiles(DefaultWorldLogic world, Vec3i blockInWorld, BlockFace face) {
if (!world.isBlockLoaded(blockInWorld)) {
return;
}
@ -94,7 +94,7 @@ public class TickAndUpdateUtil {
}
}
public static void updateBlock(WorldLogic world, Vec3i blockInWorld) {
public static void updateBlock(DefaultWorldLogic world, Vec3i blockInWorld) {
BlockLogic block = world.getBlock(blockInWorld);
if (!(block instanceof UpdateableBlock))
return; // also checks nulls
@ -111,7 +111,7 @@ public class TickAndUpdateUtil {
}
}
public static void updateTile(WorldLogic world, Vec3i blockInWorld, BlockFace face, int layer) {
public static void updateTile(DefaultWorldLogic world, Vec3i blockInWorld, BlockFace face, int layer) {
TileLogic tile = world.getTile(blockInWorld, face, layer);
if (!(tile instanceof UpdateableTile)) {
return;
@ -122,7 +122,7 @@ public class TickAndUpdateUtil {
updateTile((UpdateableTile) tile, tickContext);
}
public static void updateTiles(WorldLogic world, Vec3i blockInWorld, BlockFace face) {
public static void updateTiles(DefaultWorldLogic world, Vec3i blockInWorld, BlockFace face) {
if (!world.isBlockLoaded(blockInWorld)) {
return;
}

View File

@ -30,7 +30,7 @@ public interface TickContext {
Server getServer();
default WorldLogic getWorld() {
default DefaultWorldLogic getWorld() {
return getServer().getWorld();
}

View File

@ -93,12 +93,12 @@ public abstract class TickContextMutable implements BlockTickContext, TSTickCont
public static interface Empty /* does not extend Builder */ {
World withServer(Server server);
default Builder.World withWorld(WorldLogic world) {
default Builder.World withWorld(DefaultWorldLogic world) {
Objects.requireNonNull(world, "world");
return withServer(world.getServer());
}
default Builder.Chunk withChunk(ChunkLogic chunk) {
default Builder.Chunk withChunk(DefaultChunkLogic chunk) {
Objects.requireNonNull(chunk, "chunk");
return withWorld(chunk.getWorld()).withChunk(chunk.getPosition());
}

View File

@ -0,0 +1,44 @@
/*
* 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;
import ru.windcorp.progressia.common.world.TileDataReference;
import ru.windcorp.progressia.common.world.generic.TileGenericReferenceRO;
import ru.windcorp.progressia.common.world.tile.TileData;
import ru.windcorp.progressia.server.world.tile.TileLogic;
/**
* A read-only {@link TileGenericReferenceRO TileReference} for a
* {@link TileData} that provides convenient read-write access to the matching
* {@link TileLogic} instance.
* <p>
* For all methods other than {@link #get()}, {@link #getStack()} and
* {@link #getDataReference()},
* <tt>logicRef.<i>method</i>() == logicRef.getDataReference().<i>method</i>()</tt>.
*/
public interface TileLogicReference
extends TileLogicReferenceRO {
/*
* Override return type
*/
@Override
TileDataReference getDataReference();
}

View File

@ -0,0 +1,82 @@
/*
* 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;
import ru.windcorp.progressia.common.world.TileDataReference;
import ru.windcorp.progressia.common.world.TileDataReferenceRO;
import ru.windcorp.progressia.common.world.generic.TileGenericReferenceRO;
import ru.windcorp.progressia.common.world.tile.TileData;
import ru.windcorp.progressia.server.world.block.BlockLogic;
import ru.windcorp.progressia.server.world.tile.TileLogic;
/**
* A {@link TileGenericReferenceRO TileReference} for a {@link TileData} that
* provides convenient access to the matching {@link TileLogic} instance.
* <p>
* For all methods other than {@link #get()}, {@link #getStack()} and
* {@link #getDataReference()},
* <tt>logicRef.<i>method</i>() == logicRef.getDataReference().<i>method</i>()</tt>.
*/
public interface TileLogicReferenceRO
extends TileGenericReferenceRO<BlockLogic, TileLogic, TileLogicStackRO, TileLogicReferenceRO, ChunkLogicRO> {
/**
* Returns a reference to the {@link TileData} that this object references.
*
* @return a {@link TileDataReference} equivalent to this object
*/
TileDataReferenceRO getDataReference();
/**
* Returns the {@link TileData} that is referenced by this object, or
* {@code null} if the tile does not exist.
*
* @return the relevant {@link TileData}
*/
default TileData getData() {
return getDataReference().get();
}
/**
* {@inheritDoc}
*
* @see #getData()
*/
@Override
TileLogic get();
/*
* Refer to #getDataReference() by default
*/
@Override
default int getIndex() {
return getDataReference().getIndex();
}
@Override
default int getTag() {
return getDataReference().getTag();
}
@Override
default boolean isValid() {
return getDataReference().isValid();
}
}

View File

@ -0,0 +1,37 @@
/*
* 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;
import ru.windcorp.progressia.common.world.TileDataStack;
public interface TileLogicStack extends TileLogicStackRO {
/*
* Override return types
*/
@Override
TileDataStack getData();
@Override
ChunkLogic getChunk();
@Override
TileLogicReference getReference(int index);
}

View File

@ -15,20 +15,17 @@
* 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.tile;
import java.util.AbstractList;
package ru.windcorp.progressia.server.world;
import ru.windcorp.progressia.common.world.TileDataStack;
import ru.windcorp.progressia.common.world.TileDataStackRO;
import ru.windcorp.progressia.common.world.generic.TileGenericStackRO;
import ru.windcorp.progressia.server.world.ChunkLogic;
import ru.windcorp.progressia.server.world.block.BlockLogic;
import ru.windcorp.progressia.server.world.tile.TileLogic;
public abstract class TileLogicStack
extends AbstractList<TileLogic>
implements TileGenericStackRO<BlockLogic, TileLogic, TileLogicStack, TileLogicReference, ChunkLogic> {
public interface TileLogicStackRO
extends TileGenericStackRO<BlockLogic, TileLogic, TileLogicStackRO, TileLogicReferenceRO, ChunkLogicRO> {
public abstract TileDataStack getData();
public abstract TileDataStackRO getData();
}

View File

@ -15,140 +15,38 @@
* 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;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import glm.Glm;
import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.util.crash.CrashReports;
import ru.windcorp.progressia.common.world.DefaultChunkData;
import ru.windcorp.progressia.common.world.ChunkDataListeners;
import ru.windcorp.progressia.common.world.DefaultWorldData;
import ru.windcorp.progressia.common.world.WorldDataListener;
import ru.windcorp.progressia.common.world.entity.EntityData;
import ru.windcorp.progressia.common.world.generic.WorldGenericRO;
import ru.windcorp.progressia.server.Server;
import ru.windcorp.progressia.server.world.block.BlockLogic;
import ru.windcorp.progressia.server.world.generation.WorldGenerator;
import ru.windcorp.progressia.server.world.tasks.TickEntitiesTask;
import ru.windcorp.progressia.server.world.ticking.Evaluation;
import ru.windcorp.progressia.server.world.tile.TileLogic;
import ru.windcorp.progressia.server.world.tile.TileLogicReference;
import ru.windcorp.progressia.server.world.tile.TileLogicStack;
import ru.windcorp.progressia.common.world.rels.BlockFace;
public class WorldLogic
implements WorldGenericRO<BlockLogic, TileLogic, TileLogicStack, TileLogicReference, ChunkLogic, EntityData
// not using EntityLogic because it is stateless
> {
public interface WorldLogic extends WorldLogicRO {
private final DefaultWorldData data;
private final Server server;
/*
* Override return types
*/
private final WorldGenerator generator;
@Override
ChunkLogic getChunk(Vec3i pos);
private final Map<DefaultChunkData, ChunkLogic> chunks = new HashMap<>();
@Override
Collection<? extends ChunkLogic> getChunks();
private final Evaluation tickEntitiesTask = new TickEntitiesTask();
public WorldLogic(DefaultWorldData data, Server server, WorldGenerator generator) {
this.data = data;
this.server = server;
this.generator = generator;
data.setGravityModel(getGenerator().getGravityModel());
data.addListener(new WorldDataListener() {
@Override
public void onChunkLoaded(DefaultWorldData world, DefaultChunkData chunk) {
chunks.put(chunk, new ChunkLogic(WorldLogic.this, chunk));
}
@Override
public void beforeChunkUnloaded(DefaultWorldData world, DefaultChunkData chunk) {
chunks.remove(chunk);
}
});
data.addListener(ChunkDataListeners.createAdder(new UpdateTriggerer(server)));
@Override
default ChunkLogic getChunkByBlock(Vec3i blockInWorld) {
return (ChunkLogic) WorldLogicRO.super.getChunkByBlock(blockInWorld);
}
@Override
public ChunkLogic getChunk(Vec3i pos) {
return chunks.get(getData().getChunk(pos));
default TileLogicStack getTiles(Vec3i blockInWorld, BlockFace face) {
return (TileLogicStack) WorldLogicRO.super.getTiles(blockInWorld, face);
}
@Override
public Collection<ChunkLogic> getChunks() {
return chunks.values();
}
@Override
public Collection<EntityData> getEntities() {
return getData().getEntities();
}
@Override
public EntityData getEntity(long entityId) {
return getData().getEntity(entityId);
}
public Evaluation getTickEntitiesTask() {
return tickEntitiesTask;
}
public Server getServer() {
return server;
}
public DefaultWorldData getData() {
return data;
}
public WorldGenerator getGenerator() {
return generator;
}
public DefaultChunkData generate(Vec3i chunkPos) {
DefaultChunkData chunk = getGenerator().generate(chunkPos);
if (!Glm.equals(chunkPos, chunk.getPosition())) {
throw CrashReports.report(null, "Generator %s has generated a chunk at (%d; %d; %d) when requested to generate a chunk at (%d; %d; %d)",
getGenerator(),
chunk.getX(), chunk.getY(), chunk.getZ(),
chunkPos.x, chunkPos.y, chunkPos.z
);
}
if (getData().getChunk(chunk.getPosition()) != chunk) {
if (isChunkLoaded(chunkPos)) {
throw CrashReports.report(null, "Generator %s has returned a chunk different to the chunk that is located at (%d; %d; %d)",
getGenerator(),
chunkPos.x, chunkPos.y, chunkPos.z
);
} else {
throw CrashReports.report(null, "Generator %s has returned a chunk that is not loaded when requested to generate a chunk at (%d; %d; %d)",
getGenerator(),
chunkPos.x, chunkPos.y, chunkPos.z
);
}
}
if (!getChunk(chunk).isReady()) {
throw CrashReports.report(null, "Generator %s has returned a chunk that is not ready when requested to generate a chunk at (%d; %d; %d)",
getGenerator(),
chunkPos.x, chunkPos.y, chunkPos.z
);
}
return chunk;
}
public ChunkLogic getChunk(DefaultChunkData chunkData) {
return chunks.get(chunkData);
default TileLogicStack getTilesOrNull(Vec3i blockInWorld, BlockFace face) {
return (TileLogicStack) WorldLogicRO.super.getTilesOrNull(blockInWorld, face);
}
}

View File

@ -15,13 +15,14 @@
* 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.tile;
package ru.windcorp.progressia.server.world;
import ru.windcorp.progressia.common.world.generic.TileGenericReferenceRO;
import ru.windcorp.progressia.server.world.ChunkLogic;
import ru.windcorp.progressia.common.world.entity.EntityData;
import ru.windcorp.progressia.common.world.generic.WorldGenericRO;
import ru.windcorp.progressia.server.world.block.BlockLogic;
import ru.windcorp.progressia.server.world.tile.TileLogic;
public interface TileLogicReference
extends TileGenericReferenceRO<BlockLogic, TileLogic, TileLogicStack, TileLogicReference, ChunkLogic> {
public interface WorldLogicRO
extends WorldGenericRO<BlockLogic, TileLogic, TileLogicStackRO, TileLogicReferenceRO, ChunkLogicRO, EntityData> {
}

View File

@ -0,0 +1,35 @@
/*
* 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;
import ru.windcorp.progressia.common.world.WorldData;
import ru.windcorp.progressia.server.world.WorldLogic;
public interface ServerWorldContext extends WorldData, ServerWorldContextRO {
public interface Logic extends ServerWorldContextRO.Logic, WorldLogic {
@Override
ServerWorldContext data();
}
@Override
ServerWorldContext.Logic logic();
}

View File

@ -0,0 +1,16 @@
package ru.windcorp.progressia.server.world.context;
import ru.windcorp.progressia.common.world.WorldDataRO;
import ru.windcorp.progressia.server.world.WorldLogicRO;
public interface ServerWorldContextRO extends WorldDataRO, ServerContext {
public interface Logic extends WorldLogicRO {
ServerWorldContextRO data();
}
ServerWorldContextRO.Logic logic();
}

View File

@ -29,7 +29,7 @@ import ru.windcorp.progressia.common.world.GravityModel;
import ru.windcorp.progressia.common.world.GravityModelRegistry;
import ru.windcorp.progressia.common.world.DefaultWorldData;
import ru.windcorp.progressia.server.Server;
import ru.windcorp.progressia.server.world.WorldLogic;
import ru.windcorp.progressia.server.world.DefaultWorldLogic;
public abstract class AbstractWorldGenerator<H> extends WorldGenerator {
@ -90,7 +90,7 @@ public abstract class AbstractWorldGenerator<H> extends WorldGenerator {
}
@Override
public WorldLogic getWorldLogic() {
public DefaultWorldLogic getWorldLogic() {
return server.getWorld();
}

View File

@ -30,7 +30,7 @@ import ru.windcorp.progressia.common.world.DecodingException;
import ru.windcorp.progressia.common.world.GravityModel;
import ru.windcorp.progressia.common.world.DefaultWorldData;
import ru.windcorp.progressia.server.Server;
import ru.windcorp.progressia.server.world.WorldLogic;
import ru.windcorp.progressia.server.world.DefaultWorldLogic;
public abstract class WorldGenerator extends Namespaced {
@ -52,7 +52,7 @@ public abstract class WorldGenerator extends Namespaced {
public abstract Vec3 suggestSpawnLocation();
public abstract Server getServer();
public abstract WorldLogic getWorldLogic();
public abstract DefaultWorldLogic getWorldLogic();
public abstract DefaultWorldData getWorldData();
}

View File

@ -25,7 +25,7 @@ import ru.windcorp.progressia.common.world.Coordinates;
import ru.windcorp.progressia.common.world.rels.AbsFace;
import ru.windcorp.progressia.server.Server;
import ru.windcorp.progressia.server.world.TickAndUpdateUtil;
import ru.windcorp.progressia.server.world.WorldLogic;
import ru.windcorp.progressia.server.world.DefaultWorldLogic;
class BlockTriggeredUpdate extends CachedEvaluation {
@ -39,7 +39,7 @@ class BlockTriggeredUpdate extends CachedEvaluation {
public void evaluate(Server server) {
Vec3i cursor = new Vec3i(blockInWorld.x, blockInWorld.y, blockInWorld.z);
WorldLogic world = server.getWorld();
DefaultWorldLogic world = server.getWorld();
for (AbsFace face : AbsFace.getFaces()) {
TickAndUpdateUtil.updateTiles(world, cursor, face);

View File

@ -31,7 +31,7 @@ import ru.windcorp.progressia.common.world.DefaultChunkData;
import ru.windcorp.progressia.common.world.rels.AbsFace;
import ru.windcorp.progressia.common.world.TileDataStack;
import ru.windcorp.progressia.server.Server;
import ru.windcorp.progressia.server.world.ChunkLogic;
import ru.windcorp.progressia.server.world.DefaultChunkLogic;
import ru.windcorp.progressia.server.world.TickContextMutable;
import ru.windcorp.progressia.server.world.block.BlockLogic;
import ru.windcorp.progressia.server.world.block.TickableBlock;
@ -61,9 +61,9 @@ public class TickChunk extends Evaluation {
this.randomTickMethods = ImmutableList.copyOf(randomTickMethods);
}
private final ChunkLogic chunk;
private final DefaultChunkLogic chunk;
public TickChunk(ChunkLogic chunk) {
public TickChunk(DefaultChunkLogic chunk) {
this.chunk = chunk;
}

View File

@ -25,7 +25,7 @@ import ru.windcorp.progressia.common.world.Coordinates;
import ru.windcorp.progressia.common.world.rels.AbsFace;
import ru.windcorp.progressia.server.Server;
import ru.windcorp.progressia.server.world.TickAndUpdateUtil;
import ru.windcorp.progressia.server.world.WorldLogic;
import ru.windcorp.progressia.server.world.DefaultWorldLogic;
class TileTriggeredUpdate extends CachedEvaluation {
@ -40,7 +40,7 @@ class TileTriggeredUpdate extends CachedEvaluation {
public void evaluate(Server server) {
Vec3i cursor = new Vec3i(blockInWorld.x, blockInWorld.y, blockInWorld.z);
WorldLogic world = server.getWorld();
DefaultWorldLogic world = server.getWorld();
TickAndUpdateUtil.updateTiles(world, cursor, face); // Update facemates
// (also self)

View File

@ -25,8 +25,9 @@ import java.util.function.Function;
import ru.windcorp.progressia.common.world.DefaultChunkData;
import ru.windcorp.progressia.common.world.rels.RelFace;
import ru.windcorp.progressia.common.world.TileDataStack;
import ru.windcorp.progressia.server.world.ChunkLogic;
import ru.windcorp.progressia.server.world.DefaultChunkLogic;
import ru.windcorp.progressia.server.world.TickContextMutable;
import ru.windcorp.progressia.server.world.TileLogicStackRO;
import ru.windcorp.progressia.server.world.block.BlockTickContext;
public interface TSTickContext extends BlockTickContext {
@ -41,15 +42,15 @@ public interface TSTickContext extends BlockTickContext {
* Getters
*/
default TileLogicStack getTLSOrNull() {
ChunkLogic chunkLogic = getChunkLogic();
default TileLogicStackRO getTLSOrNull() {
DefaultChunkLogic chunkLogic = getChunkLogic();
if (chunkLogic == null)
return null;
return chunkLogic.getTilesOrNull(getBlockInChunk(), getFace());
}
default TileLogicStack getTLS() {
default TileLogicStackRO getTLS() {
return getChunkLogic().getTiles(getBlockInChunk(), getFace());
}

View File

@ -19,6 +19,7 @@
package ru.windcorp.progressia.server.world.tile;
import ru.windcorp.progressia.common.world.tile.TileData;
import ru.windcorp.progressia.server.world.TileLogicStackRO;
import ru.windcorp.progressia.common.world.TileDataStack;
import ru.windcorp.progressia.common.world.TileDataReference;
@ -40,7 +41,7 @@ public interface TileTickContext extends TSTickContext {
*/
default TileLogic getTile() {
TileLogicStack stack = getTLSOrNull();
TileLogicStackRO stack = getTLSOrNull();
if (stack == null)
return null;
return stack.get(getLayer());

View File

@ -19,7 +19,7 @@
package ru.windcorp.progressia.test.gen;
import kdotjpg.opensimplex2.areagen.OpenSimplex2S;
import ru.windcorp.progressia.server.world.WorldLogic;
import ru.windcorp.progressia.server.world.DefaultWorldLogic;
class TestTerrainGenerator {
@ -31,7 +31,7 @@ class TestTerrainGenerator {
private final OpenSimplex2S noise;
private final Func2D shape;
public TestTerrainGenerator(TestWorldGenerator testWorldGenerator, WorldLogic world) {
public TestTerrainGenerator(TestWorldGenerator testWorldGenerator, DefaultWorldLogic world) {
this.noise = new OpenSimplex2S("We're getting somewhere".hashCode());
Func2D plainsHeight = tweak(