Began work on integrating the new Contexts. Compiles but does not work

- All TickContexts including TickContextMutable are deleted
- Previous occurrences of TickContexts are replaced by appropriate
ServerContexts
- Added Context.popAndReturn methods for convenience

Current known problems:
- World does not generate properly on startup
- No bulk methods in the new API (the likes of forEachTile, etc.)
- AbsFace/RelFace ambiguity in the new API (see
TestTileLogicGrass.java:68)
- TestTileLogicGrass.java:53 is disabled for some reason
This commit is contained in:
OLEGSHA 2021-08-09 20:32:15 +03:00
parent 5fb4c601ff
commit 0a45613e45
Signed by: OLEGSHA
GPG Key ID: E57A4B08D64AFF7A
29 changed files with 305 additions and 1120 deletions

View File

@ -136,4 +136,24 @@ public interface Context {
*/ */
void pop(); void pop();
default <T> T popAndReturn(T result) {
pop();
return result;
}
default boolean popAndReturn(boolean result) {
pop();
return result;
}
default int popAndReturn(int result) {
pop();
return result;
}
default float popAndReturn(float result) {
pop();
return result;
}
} }

View File

@ -35,6 +35,9 @@ import ru.windcorp.progressia.server.management.load.ChunkRequestDaemon;
import ru.windcorp.progressia.server.management.load.EntityRequestDaemon; import ru.windcorp.progressia.server.management.load.EntityRequestDaemon;
import ru.windcorp.progressia.server.management.load.LoadManager; import ru.windcorp.progressia.server.management.load.LoadManager;
import ru.windcorp.progressia.server.world.DefaultWorldLogic; import ru.windcorp.progressia.server.world.DefaultWorldLogic;
import ru.windcorp.progressia.server.world.context.ServerWorldContext;
import ru.windcorp.progressia.server.world.context.impl.DefaultServerContext;
import ru.windcorp.progressia.server.world.context.impl.ReportingServerContext;
import ru.windcorp.progressia.server.world.tasks.WorldAccessor; import ru.windcorp.progressia.server.world.tasks.WorldAccessor;
import ru.windcorp.progressia.server.world.ticking.Change; import ru.windcorp.progressia.server.world.ticking.Change;
import ru.windcorp.progressia.server.world.ticking.Evaluation; import ru.windcorp.progressia.server.world.ticking.Evaluation;
@ -97,6 +100,19 @@ public class Server {
return world; return world;
} }
/**
* Instantiates and returns an new {@link ServerWorldContext} instance
* suitable for read and write access to the server's world. This is the
* preferred way to query or change the world.
*
* @return the context
*/
public ServerWorldContext createContext() {
return new ReportingServerContext(DefaultServerContext.empty().inRealWorldOf(this).build()).withListener(worldAccessor);
}
/** /**
* Returns this server's {@link ClientManager}. Use this to deal with * Returns this server's {@link ClientManager}. Use this to deal with
* communications, e.g. send packets. * communications, e.g. send packets.
@ -219,16 +235,20 @@ public class Server {
return this.serverThread.getTicker().getUptimeTicks(); return this.serverThread.getTicker().getUptimeTicks();
} }
/** // /**
* Returns the {@link WorldAccessor} object for this server. Use the // * Returns the {@link WorldAccessor} object for this server. Use the
* provided accessor to request common {@link Evaluation}s and // * provided accessor to request common {@link Evaluation}s and
* {@link Change}s. // * {@link Change}s.
* // *
* @return a {@link WorldAccessor} // * @return a {@link WorldAccessor}
* @see #requestChange(Change) // * @see #requestChange(Change)
* @see #requestEvaluation(Evaluation) // * @see #requestEvaluation(Evaluation)
*/ // */
public WorldAccessor getWorldAccessor() { // public WorldAccessor getWorldAccessor() {
// return worldAccessor;
// }
public WorldAccessor getWorldAccessor___really_bad_dont_use() {
return worldAccessor; return worldAccessor;
} }

View File

@ -1,55 +0,0 @@
/*
* 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.function.Consumer;
import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.world.DefaultChunkData;
import ru.windcorp.progressia.common.world.generic.GenericChunks;
import ru.windcorp.progressia.common.world.rels.AbsFace;
import ru.windcorp.progressia.server.world.block.BlockTickContext;
public interface ChunkTickContext extends TickContext {
Vec3i getChunk();
default DefaultChunkLogic getChunkLogic() {
return getWorld().getChunk(getChunk());
}
default DefaultChunkData getChunkData() {
DefaultChunkLogic chunkLogic = getChunkLogic();
return chunkLogic == null ? null : chunkLogic.getData();
}
default AbsFace getUp() {
return getChunkData().getUp();
}
default void forEachBlock(Consumer<BlockTickContext> action) {
TickContextMutable context = TickContextMutable.uninitialized();
GenericChunks.forEachBiC(blockInChunk -> {
context.rebuild().withServer(getServer()).withChunk(getChunk()).withBlockInChunk(blockInChunk).build();
action.accept(context);
});
}
}

View File

@ -34,9 +34,13 @@ import ru.windcorp.progressia.common.world.rels.BlockFace;
import ru.windcorp.progressia.common.world.rels.RelFace; import ru.windcorp.progressia.common.world.rels.RelFace;
import ru.windcorp.progressia.common.world.TileDataStack; import ru.windcorp.progressia.common.world.TileDataStack;
import ru.windcorp.progressia.common.world.TileDataReference; import ru.windcorp.progressia.common.world.TileDataReference;
import ru.windcorp.progressia.server.Server;
import ru.windcorp.progressia.server.world.block.BlockLogic; import ru.windcorp.progressia.server.world.block.BlockLogic;
import ru.windcorp.progressia.server.world.block.BlockLogicRegistry; import ru.windcorp.progressia.server.world.block.BlockLogicRegistry;
import ru.windcorp.progressia.server.world.block.TickableBlock; import ru.windcorp.progressia.server.world.block.TickableBlock;
import ru.windcorp.progressia.server.world.context.ServerBlockContextRO;
import ru.windcorp.progressia.server.world.context.ServerTileContextRO;
import ru.windcorp.progressia.server.world.context.ServerWorldContextRO;
import ru.windcorp.progressia.server.world.tasks.TickChunk; import ru.windcorp.progressia.server.world.tasks.TickChunk;
import ru.windcorp.progressia.server.world.ticking.TickingPolicy; import ru.windcorp.progressia.server.world.ticking.TickingPolicy;
import ru.windcorp.progressia.server.world.tile.TickableTile; import ru.windcorp.progressia.server.world.tile.TickableTile;
@ -221,28 +225,45 @@ public class DefaultChunkLogic implements ChunkLogic {
} }
private void tmp_generateTickLists() { private void tmp_generateTickLists() {
ChunkTickContext context = TickContextMutable.start().withChunk(this).build(); ServerWorldContextRO context = Server.getCurrentServer().createContext();
Vec3i blockInChunk = new Vec3i();
context.forEachBlock(bctxt -> { forEachBiW(location -> {
BlockLogic block = bctxt.getBlock();
ServerBlockContextRO blockContext = context.push(location);
BlockLogic block = blockContext.logic().getBlock();
Coordinates.convertInWorldToInChunk(location, blockInChunk);
if (!(block instanceof TickableBlock)) if (!(block instanceof TickableBlock))
return; return;
if (((TickableBlock) block).getTickingPolicy(bctxt) == TickingPolicy.REGULAR) { if (((TickableBlock) block).getTickingPolicy(blockContext) == TickingPolicy.REGULAR) {
tickingBlocks.add(Coordinates.convertInWorldToInChunk(bctxt.getBlockInWorld(), null)); tickingBlocks.add(blockInChunk);
} }
bctxt.forEachFace(fctxt -> fctxt.forEachTile(tctxt -> { for (RelFace face : RelFace.getFaces()) {
TileLogic tile = tctxt.getTile(); TileLogicStack stack = getTilesOrNull(blockInChunk, face);
if (stack == null || stack.isEmpty()) continue;
if (!(tile instanceof TickableTile)) for (int i = 0; i < stack.size(); ++i) {
return; ServerTileContextRO tileContext = blockContext.push(face, i);
if (((TickableTile) tile).getTickingPolicy(tctxt) == TickingPolicy.REGULAR) { TileLogic tile = stack.get(i);
tickingTiles.add(tctxt.getReference());
if (!(tile instanceof TickableTile))
return;
if (((TickableTile) tile).getTickingPolicy(tileContext) == TickingPolicy.REGULAR) {
tickingTiles.add(stack.getData().getReference(i));
}
tileContext.pop();
} }
})); }
blockContext.pop();
}); });
} }

View File

@ -65,7 +65,7 @@ public class DefaultWorldLogic implements WorldLogic {
} }
}); });
data.addListener(ChunkDataListeners.createAdder(new UpdateTriggerer(server))); data.addListener(ChunkDataListeners.createAdder(new UpdateTriggerer(server.getWorldAccessor___really_bad_dont_use())));
} }
@Override @Override

View File

@ -24,19 +24,26 @@ import ru.windcorp.progressia.common.world.entity.EntityData;
import ru.windcorp.progressia.common.world.rels.BlockFace; import ru.windcorp.progressia.common.world.rels.BlockFace;
import ru.windcorp.progressia.server.Server; import ru.windcorp.progressia.server.Server;
import ru.windcorp.progressia.server.world.block.BlockLogic; import ru.windcorp.progressia.server.world.block.BlockLogic;
import ru.windcorp.progressia.server.world.block.BlockTickContext;
import ru.windcorp.progressia.server.world.block.TickableBlock; import ru.windcorp.progressia.server.world.block.TickableBlock;
import ru.windcorp.progressia.server.world.block.UpdateableBlock; import ru.windcorp.progressia.server.world.block.UpdateableBlock;
import ru.windcorp.progressia.server.world.context.ServerBlockContext;
import ru.windcorp.progressia.server.world.context.ServerTileContext;
import ru.windcorp.progressia.server.world.context.ServerTileStackContext;
import ru.windcorp.progressia.server.world.context.ServerWorldContext;
import ru.windcorp.progressia.server.world.entity.EntityLogic; import ru.windcorp.progressia.server.world.entity.EntityLogic;
import ru.windcorp.progressia.server.world.entity.EntityLogicRegistry; import ru.windcorp.progressia.server.world.entity.EntityLogicRegistry;
import ru.windcorp.progressia.server.world.tile.TickableTile; import ru.windcorp.progressia.server.world.tile.TickableTile;
import ru.windcorp.progressia.server.world.tile.TileLogic; import ru.windcorp.progressia.server.world.tile.TileLogic;
import ru.windcorp.progressia.server.world.tile.TileTickContext;
import ru.windcorp.progressia.server.world.tile.UpdateableTile; import ru.windcorp.progressia.server.world.tile.UpdateableTile;
public class TickAndUpdateUtil { public class TickAndUpdateUtil {
public static void tickBlock(TickableBlock block, BlockTickContext context) { public static void tickBlock(ServerBlockContext context) {
BlockLogic uncheckedBlock = context.logic().getBlock();
if (!(uncheckedBlock instanceof BlockLogic)) {
return;
}
TickableBlock block = (TickableBlock) uncheckedBlock;
try { try {
block.tick(context); block.tick(context);
} catch (Exception e) { } catch (Exception e) {
@ -44,16 +51,21 @@ public class TickAndUpdateUtil {
} }
} }
public static void tickBlock(DefaultWorldLogic world, Vec3i blockInWorld) { public static void tickBlock(Server server, Vec3i blockInWorld) {
BlockLogic block = world.getBlock(blockInWorld); BlockLogic block = server.getWorld().getBlock(blockInWorld);
if (!(block instanceof TickableBlock)) if (!(block instanceof TickableBlock)) {
return; // also checks nulls return;
}
BlockTickContext tickContext = TickContextMutable.start().withWorld(world).withBlock(blockInWorld).build(); ServerBlockContext context = server.createContext().push(blockInWorld);
tickBlock((TickableBlock) block, tickContext); tickBlock(context);
} }
public static void tickTile(TickableTile tile, TileTickContext context) { public static void tickTile(ServerTileContext context) {
TileLogic uncheckedTile = context.logic().getTile();
if (!(uncheckedTile instanceof TickableTile)) {
return;
}
TickableTile tile = (TickableTile) uncheckedTile;
try { try {
tile.tick(context); tile.tick(context);
} catch (Exception e) { } catch (Exception e) {
@ -61,82 +73,88 @@ public class TickAndUpdateUtil {
} }
} }
public static void tickTile(DefaultWorldLogic world, Vec3i blockInWorld, BlockFace face, int layer) { public static void tickTile(Server server, Vec3i blockInWorld, BlockFace face, int layer) {
TileLogic tile = world.getTile(blockInWorld, face, layer); TileLogic tile = server.getWorld().getTile(blockInWorld, face, layer);
if (!(tile instanceof TickableTile)) { if (!(tile instanceof TickableTile)) {
return; return;
} }
ServerTileContext context = server.createContext()
TileTickContext tickContext = TickContextMutable.start().withWorld(world).withBlock(blockInWorld).withFace(face) .push(blockInWorld, face.relativize(server.getWorld().getUp(blockInWorld)), layer);
.withLayer(layer); tickTile(context);
tickTile((TickableTile) tile, tickContext);
} }
public static void tickTiles(DefaultWorldLogic world, Vec3i blockInWorld, BlockFace face) { public static void tickTiles(Server server, Vec3i blockInWorld, BlockFace face) {
if (!world.isLocationLoaded(blockInWorld)) { if (!server.getWorld().hasTiles(blockInWorld, face)) {
return; return;
} }
TickContextMutable.start().withWorld(world).withBlock(blockInWorld).withFace(face).build() ServerTileStackContext context = server.createContext()
.forEachTile(context -> { .push(blockInWorld, face.relativize(server.getWorld().getUp(blockInWorld)));
TileLogic tile = context.getTile(); for (int i = 0; i < context.getTileCount(); ++i) {
if (tile instanceof TickableTile) { tickTile(context.push(i));
tickTile((TickableTile) tile, context); context.pop();
} }
});
} }
public static void updateBlock(UpdateableBlock block, BlockTickContext context) { public static void updateBlock(ServerBlockContext context) {
BlockLogic uncheckedBlock = context.logic().getBlock();
if (!(uncheckedBlock instanceof BlockLogic)) {
return;
}
UpdateableBlock block = (UpdateableBlock) uncheckedBlock;
try { try {
block.update(context); block.update(context);
} catch (Exception e) { } catch (Exception e) {
throw CrashReports.report(e, "Could not update block {}", block); throw CrashReports.report(e, "Could not update block %s", block);
} }
} }
public static void updateBlock(DefaultWorldLogic world, Vec3i blockInWorld) { public static void updateBlock(Server server, Vec3i blockInWorld) {
BlockLogic block = world.getBlock(blockInWorld); BlockLogic block = server.getWorld().getBlock(blockInWorld);
if (!(block instanceof UpdateableBlock)) if (!(block instanceof UpdateableBlock)) {
return; // also checks nulls return;
}
BlockTickContext tickContext = TickContextMutable.start().withWorld(world).withBlock(blockInWorld).build(); ServerBlockContext context = server.createContext().push(blockInWorld);
updateBlock((UpdateableBlock) block, tickContext); updateBlock(context);
} }
public static void updateTile(UpdateableTile tile, TileTickContext context) { public static void updateTile(ServerTileContext context) {
TileLogic uncheckedTile = context.logic().getTile();
if (!(uncheckedTile instanceof UpdateableTile)) {
return;
}
UpdateableTile tile = (UpdateableTile) uncheckedTile;
try { try {
tile.update(context); tile.update(context);
} catch (Exception e) { } catch (Exception e) {
throw CrashReports.report(e, "Could not update tile {}", tile); throw CrashReports.report(e, "Could not tick tile %s", tile);
} }
} }
public static void updateTile(DefaultWorldLogic world, Vec3i blockInWorld, BlockFace face, int layer) { public static void updateTile(Server server, Vec3i blockInWorld, BlockFace face, int layer) {
TileLogic tile = world.getTile(blockInWorld, face, layer); TileLogic tile = server.getWorld().getTile(blockInWorld, face, layer);
if (!(tile instanceof UpdateableTile)) { if (!(tile instanceof UpdateableTile)) {
return; return;
} }
ServerTileContext context = server.createContext()
TileTickContext tickContext = TickContextMutable.start().withWorld(world).withBlock(blockInWorld).withFace(face) .push(blockInWorld, face.relativize(server.getWorld().getUp(blockInWorld)), layer);
.withLayer(layer); updateTile(context);
updateTile((UpdateableTile) tile, tickContext);
} }
public static void updateTiles(DefaultWorldLogic world, Vec3i blockInWorld, BlockFace face) { public static void updateTiles(Server server, Vec3i blockInWorld, BlockFace face) {
if (!world.isLocationLoaded(blockInWorld)) { if (!server.getWorld().hasTiles(blockInWorld, face)) {
return; return;
} }
TickContextMutable.start().withWorld(world).withBlock(blockInWorld).withFace(face).build() ServerTileStackContext context = server.createContext()
.forEachTile(context -> { .push(blockInWorld, face.relativize(server.getWorld().getUp(blockInWorld)));
TileLogic tile = context.getTile(); for (int i = 0; i < context.getTileCount(); ++i) {
if (tile instanceof UpdateableTile) { updateTile(context.push(i));
updateTile((UpdateableTile) tile, context); context.pop();
} }
});
} }
public static void tickEntity(EntityLogic logic, EntityData data, TickContext context) { public static void tickEntity(EntityLogic logic, EntityData data, ServerWorldContext context) {
try { try {
logic.tick(data, context); logic.tick(data, context);
} catch (Exception e) { } catch (Exception e) {
@ -148,7 +166,7 @@ public class TickAndUpdateUtil {
tickEntity( tickEntity(
EntityLogicRegistry.getInstance().get(data.getId()), EntityLogicRegistry.getInstance().get(data.getId()),
data, data,
TickContextMutable.start().withServer(server).build() server.createContext()
); );
} }

View File

@ -1,49 +0,0 @@
/*
* 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.Random;
import ru.windcorp.progressia.common.world.DefaultWorldData;
import ru.windcorp.progressia.server.Server;
import ru.windcorp.progressia.server.world.tasks.WorldAccessor;
public interface TickContext {
float getTickLength();
Server getServer();
default DefaultWorldLogic getWorld() {
return getServer().getWorld();
}
default WorldAccessor getAccessor() {
return getServer().getWorldAccessor();
}
default Random getRandom() {
return getServer().getAdHocRandom();
}
default DefaultWorldData getWorldData() {
return getWorld().getData();
}
}

View File

@ -1,497 +0,0 @@
/*
* 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.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
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.TileGenericStackRO;
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.Server;
import ru.windcorp.progressia.server.world.block.BlockTickContext;
import ru.windcorp.progressia.server.world.tile.TSTickContext;
import ru.windcorp.progressia.server.world.tile.TileTickContext;
public abstract class TickContextMutable implements BlockTickContext, TSTickContext, TileTickContext {
private static enum Role {
NONE, WORLD, CHUNK, BLOCK, TILE_STACK, TILE;
}
/*
* TickContextMutable interface
*/
// Only TickContextMutable.Impl can extend; extend Impl if need be
private TickContextMutable() {
}
public abstract Builder.Empty rebuild();
/*
* Static methods
*/
public static TickContextMutable uninitialized() {
return new Impl();
}
public static Builder.Empty start() {
return uninitialized().rebuild();
}
public static Builder.World copyWorld(TickContext context) {
return start().withServer(context.getServer());
}
public static Builder.Chunk copyChunk(ChunkTickContext context) {
return start().withChunk(context.getChunkLogic());
}
public static Builder.Block copyBlock(BlockTickContext context) {
return copyWorld(context).withBlock(context.getBlockInWorld());
}
public static Builder.TileStack copyTS(TSTickContext context) {
return copyBlock(context).withFace(context.getFace());
}
public static TileTickContext copyTile(TileTickContext context) {
return copyTS(context).withLayer(context.getLayer());
}
/*
* Builder interfaces
*/
public static interface Builder {
TickContextMutable build();
public static interface Empty /* does not extend Builder */ {
World withServer(Server server);
default Builder.World withWorld(DefaultWorldLogic world) {
Objects.requireNonNull(world, "world");
return withServer(world.getServer());
}
default Builder.Chunk withChunk(DefaultChunkLogic chunk) {
Objects.requireNonNull(chunk, "chunk");
return withWorld(chunk.getWorld()).withChunk(chunk.getPosition());
}
}
public static interface World extends Builder {
Chunk withChunk(Vec3i chunk);
Block withBlock(Vec3i blockInWorld);
TileStack withTS(TileGenericStackRO<?, ?, ?, ?, ?> tileStack);
default Builder.Chunk withChunk(DefaultChunkData chunk) {
Objects.requireNonNull(chunk, "chunk");
return withChunk(chunk.getPosition());
}
default TileTickContext withTile(TileDataReference ref) {
Objects.requireNonNull(ref, "ref");
return withTS(ref.getStack()).withLayer(ref.getIndex());
}
}
public static interface Chunk extends Builder {
Builder.Block withBlockInChunk(Vec3i blockInChunk);
}
public static interface Block extends Builder {
Builder.TileStack withFace(BlockFace face);
}
public static interface TileStack extends Builder {
TickContextMutable withLayer(int layer);
}
}
/*
* Impl
*/
public static class Impl
extends TickContextMutable
implements Builder.Empty, Builder.World, Builder.Chunk, Builder.Block, Builder.TileStack {
protected Impl() {
}
protected Server server;
protected final Vec3i chunk = new Vec3i();
protected final Vec3i blockInWorld = new Vec3i();
protected RelFace face;
protected int layer;
protected Role role = Role.NONE;
protected boolean isBeingBuilt = false;
/**
* Updated lazily
*/
protected final Vec3i blockInChunk = new Vec3i();
/*
* TickContextMutable
*/
@Override
public Server getServer() {
checkContextState(Role.WORLD);
return this.server;
}
@Override
public float getTickLength() {
checkContextState(Role.WORLD);
return (float) this.server.getTickLength();
}
@Override
public Vec3i getChunk() {
checkContextState(Role.CHUNK);
return this.chunk;
}
@Override
public Vec3i getBlockInWorld() {
checkContextState(Role.BLOCK);
return this.blockInWorld;
}
@Override
public RelFace getFace() {
checkContextState(Role.TILE_STACK);
return this.face;
}
@Override
public int getLayer() {
checkContextState(Role.TILE);
return this.layer;
}
@Override
public Builder.Empty rebuild() {
this.role = Role.NONE;
this.isBeingBuilt = true;
this.server = null;
this.chunk.set(0);
this.blockInWorld.set(0);
this.face = null;
this.layer = -1;
return this;
}
/*
* Builder
* memo: do NOT use Context getters, they throw ISEs
*/
@Override
public TickContextMutable build() {
checkBuilderState(null);
this.isBeingBuilt = false;
return this;
}
@Override
public World withServer(Server server) {
Objects.requireNonNull(server, "server");
checkBuilderState(Role.NONE);
this.server = server;
this.role = Role.WORLD;
return this;
}
@Override
public Chunk withChunk(Vec3i chunk) {
Objects.requireNonNull(chunk, "chunk");
checkBuilderState(Role.WORLD);
this.chunk.set(chunk.x, chunk.y, chunk.z);
this.role = Role.CHUNK;
return this;
}
@Override
public Block withBlock(Vec3i blockInWorld) {
Objects.requireNonNull(blockInWorld, "blockInWorld");
checkBuilderState(Role.WORLD);
this.blockInWorld.set(blockInWorld.x, blockInWorld.y, blockInWorld.z);
Coordinates.convertInWorldToChunk(blockInWorld, this.chunk);
this.role = Role.BLOCK;
return this;
}
@Override
public TileStack withTS(TileGenericStackRO<?, ?, ?, ?, ?> tileStack) {
Objects.requireNonNull(tileStack, "tileStack");
return withBlock(
tileStack.getBlockInWorld(this.blockInWorld) // This is safe
).withFace(tileStack.getFace());
}
@Override
public Block withBlockInChunk(Vec3i blockInChunk) {
Objects.requireNonNull(blockInChunk, "blockInChunk");
checkBuilderState(Role.CHUNK);
Coordinates.getInWorld(this.chunk, blockInChunk, this.blockInWorld);
this.role = Role.BLOCK;
return this;
}
@Override
public TileStack withFace(BlockFace face) {
Objects.requireNonNull(face, "face");
checkBuilderState(Role.BLOCK);
this.face = face.relativize(server.getWorld().getChunk(chunk).getUp());
this.role = Role.TILE_STACK;
return this;
}
@Override
public TickContextMutable withLayer(int layer) {
checkBuilderState(Role.TILE);
this.layer = layer;
this.role = Role.TILE;
return build();
}
/*
* Optimization
*/
@Override
public Vec3i getBlockInChunk() {
return Coordinates.convertInWorldToInChunk(getBlockInWorld(), this.blockInChunk);
}
@Override
public void forEachBlock(Consumer<BlockTickContext> action) {
checkContextState(Role.CHUNK);
Vec3i v = this.blockInWorld;
int previousX = v.x;
int previousY = v.y;
int previousZ = v.z;
Role previousRole = this.role;
this.role = Role.BLOCK;
final int minX = Coordinates.getInWorld(chunk.x, 0);
final int minY = Coordinates.getInWorld(chunk.y, 0);
final int minZ = Coordinates.getInWorld(chunk.z, 0);
final int size = DefaultChunkData.BLOCKS_PER_CHUNK;
for (v.x = minX; v.x < minX + size; ++v.x) {
for (v.y = minY; v.y < minY + size; ++v.y) {
for (v.z = minZ; v.z < minZ + size; ++v.z) {
action.accept(this);
}
}
}
this.role = previousRole;
blockInWorld.set(previousX, previousY, previousZ);
}
@Override
public void forEachFace(Consumer<TSTickContext> action) {
checkContextState(Role.BLOCK);
RelFace previousFace = this.face;
Role previousRole = this.role;
this.role = Role.TILE_STACK;
for (int i = 0; i < BlockFace.BLOCK_FACE_COUNT; ++i) {
this.face = RelFace.getFaces().get(i);
action.accept(this);
}
this.role = previousRole;
this.face = previousFace;
}
@Override
public <R> R evalNeighbor(Vec3i direction, Function<BlockTickContext, R> action) {
this.blockInWorld.add(direction);
R result = action.apply(this);
this.blockInWorld.sub(direction);
return result;
}
@Override
public void forNeighbor(Vec3i direction, Consumer<BlockTickContext> action) {
this.blockInWorld.add(direction);
action.accept(this);
this.blockInWorld.sub(direction);
}
@Override
public boolean forEachTile(Consumer<TileTickContext> action) {
checkContextState(Role.TILE_STACK);
int previousLayer = this.layer;
Role previousRole = this.role;
this.role = Role.TILE;
TileDataStack stack = getTDSOrNull();
if (stack == null || stack.isEmpty())
return false;
for (this.layer = 0; this.layer < stack.size(); ++this.layer) {
action.accept(this);
}
this.role = previousRole;
this.layer = previousLayer;
return true;
}
@Override
public <R> R evalComplementary(Function<TSTickContext, R> action) {
Objects.requireNonNull(action, "action");
checkContextState(Role.TILE_STACK);
Vec3i vector = this.face.getVector(getUp());
this.blockInWorld.add(vector);
this.face = this.face.getCounter();
R result = action.apply(this);
this.face = this.face.getCounter();
this.blockInWorld.sub(vector);
return result;
}
@Override
public void forComplementary(Consumer<TSTickContext> action) {
Objects.requireNonNull(action, "action");
checkContextState(Role.TILE_STACK);
Vec3i vector = this.face.getVector(getUp());
this.blockInWorld.add(vector);
this.face = this.face.getCounter();
action.accept(this);
this.face = this.face.getCounter();
this.blockInWorld.sub(vector);
}
/*
* Misc
*/
protected void checkContextState(Role requiredRole) {
if (isBeingBuilt) {
throw new IllegalStateException("This context is still being built");
}
if ((role == null) || (requiredRole.compareTo(role) > 0)) {
throw new IllegalStateException(
"This context is currently initialized as " + role + "; requested " + requiredRole
);
}
}
protected void checkBuilderState(Role requiredRole) {
if (!isBeingBuilt) {
throw new IllegalStateException("This context is already built");
}
if (requiredRole == null) {
if (role == Role.NONE) {
throw new IllegalStateException("This context is currently not initialized");
}
} else {
if (role != requiredRole) {
throw new IllegalStateException(
"This context is currently initialized as " + role + "; requested " + requiredRole
);
}
}
}
@Override
public String toString() {
final String format;
switch (this.role) {
case WORLD:
format = "TickContext";
break;
case CHUNK:
format = "(%2$d; %3$d; %4$d)";
break;
case BLOCK:
format = "(%5$d; %6$d; %7$d)";
break;
case TILE_STACK:
format = "((%5$d; %6$d; %7$d); %8$6s)";
break;
case TILE:
format = "((%5$d; %6$d; %7$d); %8$6s; %9$d)";
break;
case NONE:
default:
format = "Uninitialized TickContextMutable";
break;
}
return String.format(
format,
this.chunk.x,
this.chunk.y,
this.chunk.z,
this.blockInWorld.x,
this.blockInWorld.y,
this.blockInWorld.z,
this.face,
this.layer
);
}
}
}

View File

@ -25,14 +25,14 @@ import ru.windcorp.progressia.common.world.Coordinates;
import ru.windcorp.progressia.common.world.block.BlockData; import ru.windcorp.progressia.common.world.block.BlockData;
import ru.windcorp.progressia.common.world.rels.RelFace; import ru.windcorp.progressia.common.world.rels.RelFace;
import ru.windcorp.progressia.common.world.tile.TileData; import ru.windcorp.progressia.common.world.tile.TileData;
import ru.windcorp.progressia.server.Server; import ru.windcorp.progressia.server.world.tasks.WorldAccessor;
public class UpdateTriggerer implements ChunkDataListener { public class UpdateTriggerer implements ChunkDataListener {
private final Server server; private final WorldAccessor worldAccessor;
public UpdateTriggerer(Server server) { public UpdateTriggerer(WorldAccessor worldAccessor) {
this.server = server; this.worldAccessor = worldAccessor;
} }
@Override @Override
@ -42,7 +42,7 @@ public class UpdateTriggerer implements ChunkDataListener {
BlockData previous, BlockData previous,
BlockData current BlockData current
) { ) {
server.getWorldAccessor().triggerUpdates(Coordinates.getInWorld(chunk.getPosition(), blockInChunk, null)); worldAccessor.triggerUpdates(Coordinates.getInWorld(chunk.getPosition(), blockInChunk, null));
} }
@Override @Override
@ -53,7 +53,7 @@ public class UpdateTriggerer implements ChunkDataListener {
TileData tile, TileData tile,
boolean wasAdded boolean wasAdded
) { ) {
server.getWorldAccessor().triggerUpdates(Coordinates.getInWorld(chunk.getPosition(), blockInChunk, null), face); worldAccessor.triggerUpdates(Coordinates.getInWorld(chunk.getPosition(), blockInChunk, null), face);
} }
} }

View File

@ -21,6 +21,7 @@ package ru.windcorp.progressia.server.world.block;
import ru.windcorp.progressia.common.util.namespaces.Namespaced; import ru.windcorp.progressia.common.util.namespaces.Namespaced;
import ru.windcorp.progressia.common.world.generic.BlockGeneric; import ru.windcorp.progressia.common.world.generic.BlockGeneric;
import ru.windcorp.progressia.common.world.rels.RelFace; import ru.windcorp.progressia.common.world.rels.RelFace;
import ru.windcorp.progressia.server.world.context.ServerBlockContextRO;
public class BlockLogic extends Namespaced implements BlockGeneric { public class BlockLogic extends Namespaced implements BlockGeneric {
@ -28,7 +29,7 @@ public class BlockLogic extends Namespaced implements BlockGeneric {
super(id); super(id);
} }
public boolean isSolid(BlockTickContext context, RelFace face) { public boolean isSolid(ServerBlockContextRO context, RelFace face) {
return isSolid(face); return isSolid(face);
} }
@ -36,7 +37,7 @@ public class BlockLogic extends Namespaced implements BlockGeneric {
return true; return true;
} }
public boolean isTransparent(BlockTickContext context) { public boolean isTransparent(ServerBlockContextRO context) {
return isTransparent(); return isTransparent();
} }

View File

@ -1,114 +0,0 @@
/*
* 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.block;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.function.Function;
import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.world.Coordinates;
import ru.windcorp.progressia.common.world.block.BlockData;
import ru.windcorp.progressia.common.world.rels.AbsFace;
import ru.windcorp.progressia.common.world.rels.BlockRelation;
import ru.windcorp.progressia.server.world.ChunkTickContext;
import ru.windcorp.progressia.server.world.TickContextMutable;
import ru.windcorp.progressia.server.world.tile.TSTickContext;
public interface BlockTickContext extends ChunkTickContext {
/**
* Returns the current world coordinates.
*
* @return the world coordinates of the block being ticked
*/
Vec3i getBlockInWorld();
default Vec3i getBlockInChunk() {
return Coordinates.convertInWorldToInChunk(getBlockInWorld(), null);
}
default BlockLogic getBlock() {
return getWorld().getBlock(getBlockInWorld());
}
default BlockData getBlockData() {
return getWorldData().getBlock(getBlockInWorld());
}
default void forEachFace(Consumer<TSTickContext> action) {
Objects.requireNonNull(action, "action");
TickContextMutable context = TickContextMutable.uninitialized();
for (AbsFace face : AbsFace.getFaces()) {
context.rebuild().withServer(getServer()).withBlock(getBlockInWorld()).withFace(face).build();
action.accept(context);
}
}
default BlockTickContext getNeighbor(Vec3i direction) {
Objects.requireNonNull(direction, "direction");
return TickContextMutable.copyWorld(this).withBlock(getBlockInWorld().add_(direction)).build();
}
default BlockTickContext getNeighbor(BlockRelation relation) {
Objects.requireNonNull(relation, "relation");
return getNeighbor(relation.getVector(getChunkData().getUp()));
}
default <R> R evalNeighbor(Vec3i direction, Function<BlockTickContext, R> action) {
Objects.requireNonNull(action, "action");
Objects.requireNonNull(direction, "direction");
return action.apply(getNeighbor(direction));
}
default <R> R evalNeighbor(BlockRelation relation, Function<BlockTickContext, R> action) {
Objects.requireNonNull(action, "action");
Objects.requireNonNull(relation, "relation");
return evalNeighbor(relation.getVector(getChunkData().getUp()), action);
}
default void forNeighbor(Vec3i direction, Consumer<BlockTickContext> action) {
Objects.requireNonNull(action, "action");
Objects.requireNonNull(direction, "direction");
evalNeighbor(direction, (Function<BlockTickContext, Void>) ctxt -> {
action.accept(ctxt);
return null;
});
}
default void forNeighbor(BlockRelation relation, Consumer<BlockTickContext> action) {
Objects.requireNonNull(action, "action");
Objects.requireNonNull(relation, "relation");
forNeighbor(relation.getVector(getChunkData().getUp()), action);
}
/*
* Convenience methods - changes
*/
default void setThisBlock(BlockData block) {
getAccessor().setBlock(getBlockInWorld(), block);
}
default void setThisBlock(String id) {
getAccessor().setBlock(getBlockInWorld(), id);
}
}

View File

@ -18,12 +18,14 @@
package ru.windcorp.progressia.server.world.block; package ru.windcorp.progressia.server.world.block;
import ru.windcorp.progressia.server.world.context.ServerBlockContext;
import ru.windcorp.progressia.server.world.context.ServerBlockContextRO;
import ru.windcorp.progressia.server.world.ticking.TickingPolicy; import ru.windcorp.progressia.server.world.ticking.TickingPolicy;
public interface TickableBlock { public interface TickableBlock {
void tick(BlockTickContext context); void tick(ServerBlockContext context);
TickingPolicy getTickingPolicy(BlockTickContext context); TickingPolicy getTickingPolicy(ServerBlockContextRO context);
} }

View File

@ -18,18 +18,10 @@
package ru.windcorp.progressia.server.world.block; package ru.windcorp.progressia.server.world.block;
import org.apache.logging.log4j.LogManager; import ru.windcorp.progressia.server.world.context.ServerBlockContext;
public interface UpdateableBlock { public interface UpdateableBlock {
default void update(BlockTickContext context) { void update(ServerBlockContext context);
LogManager.getLogger().info(
"Updating block {} @ ({}; {}; {})",
context.getBlock(),
context.getBlockInWorld().x,
context.getBlockInWorld().y,
context.getBlockInWorld().z
);
}
} }

View File

@ -20,7 +20,7 @@ package ru.windcorp.progressia.server.world.entity;
import ru.windcorp.progressia.common.util.namespaces.Namespaced; import ru.windcorp.progressia.common.util.namespaces.Namespaced;
import ru.windcorp.progressia.common.world.entity.EntityData; import ru.windcorp.progressia.common.world.entity.EntityData;
import ru.windcorp.progressia.server.world.TickContext; import ru.windcorp.progressia.server.world.context.ServerWorldContext;
public class EntityLogic extends Namespaced { public class EntityLogic extends Namespaced {
@ -28,7 +28,7 @@ public class EntityLogic extends Namespaced {
super(id); super(id);
} }
public void tick(EntityData entity, TickContext context) { public void tick(EntityData entity, ServerWorldContext context) {
entity.incrementAge(context.getTickLength()); entity.incrementAge(context.getTickLength());
} }

View File

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

View File

@ -21,6 +21,7 @@ package ru.windcorp.progressia.server.world.tasks;
import java.util.function.Consumer; import java.util.function.Consumer;
import glm.vec._3.i.Vec3i; import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.state.StateChange;
import ru.windcorp.progressia.common.world.entity.EntityData; import ru.windcorp.progressia.common.world.entity.EntityData;
import ru.windcorp.progressia.common.world.entity.PacketChangeEntity; import ru.windcorp.progressia.common.world.entity.PacketChangeEntity;
import ru.windcorp.progressia.server.Server; import ru.windcorp.progressia.server.Server;
@ -36,7 +37,7 @@ class ChangeEntity extends CachedChange {
super(disposer); super(disposer);
} }
public <T extends EntityData> void set(T entity, StateChange<T> change) { public void set(EntityData entity, StateChange<?> change) {
if (this.entity != null) if (this.entity != null)
throw new IllegalStateException("Entity is not null. Current: " + this.entity + "; requested: " + entity); throw new IllegalStateException("Entity is not null. Current: " + this.entity + "; requested: " + entity);

View File

@ -1,24 +0,0 @@
/*
* 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.tasks;
@FunctionalInterface
public interface StateChange<T> {
void change(T object);
}

View File

@ -27,17 +27,22 @@ import com.google.common.collect.ImmutableList;
import glm.vec._3.i.Vec3i; import glm.vec._3.i.Vec3i;
import ru.windcorp.progressia.common.util.FloatMathUtil; import ru.windcorp.progressia.common.util.FloatMathUtil;
import ru.windcorp.progressia.common.util.Vectors;
import ru.windcorp.progressia.common.world.Coordinates;
import ru.windcorp.progressia.common.world.DefaultChunkData; import ru.windcorp.progressia.common.world.DefaultChunkData;
import ru.windcorp.progressia.common.world.rels.AbsFace; import ru.windcorp.progressia.common.world.rels.AbsFace;
import ru.windcorp.progressia.common.world.TileDataStack; import ru.windcorp.progressia.common.world.TileDataStack;
import ru.windcorp.progressia.common.world.generic.ChunkGenericRO;
import ru.windcorp.progressia.server.Server; import ru.windcorp.progressia.server.Server;
import ru.windcorp.progressia.server.world.DefaultChunkLogic; 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.BlockLogic;
import ru.windcorp.progressia.server.world.block.TickableBlock; import ru.windcorp.progressia.server.world.block.TickableBlock;
import ru.windcorp.progressia.server.world.context.ServerBlockContext;
import ru.windcorp.progressia.server.world.context.ServerTileContext;
import ru.windcorp.progressia.server.world.context.ServerTileStackContext;
import ru.windcorp.progressia.server.world.context.ServerWorldContext;
import ru.windcorp.progressia.server.world.ticking.Evaluation; import ru.windcorp.progressia.server.world.ticking.Evaluation;
import ru.windcorp.progressia.server.world.ticking.TickingPolicy; import ru.windcorp.progressia.server.world.ticking.TickingPolicy;
import ru.windcorp.progressia.server.world.tile.TSTickContext;
import ru.windcorp.progressia.server.world.tile.TickableTile; import ru.windcorp.progressia.server.world.tile.TickableTile;
import ru.windcorp.progressia.server.world.tile.TileLogic; import ru.windcorp.progressia.server.world.tile.TileLogic;
import static ru.windcorp.progressia.common.world.DefaultChunkData.BLOCKS_PER_CHUNK; import static ru.windcorp.progressia.common.world.DefaultChunkData.BLOCKS_PER_CHUNK;
@ -82,11 +87,11 @@ public class TickChunk extends Evaluation {
if (!chunk.hasTickingBlocks()) if (!chunk.hasTickingBlocks())
return; return;
TickContextMutable context = TickContextMutable.uninitialized(); ServerWorldContext context = server.createContext();
chunk.forEachTickingBlock((blockInChunk, block) -> { chunk.forEachTickingBlock((blockInChunk, block) -> {
context.rebuild().withChunk(chunk).withBlockInChunk(blockInChunk).build(); ((TickableBlock) block).tick(contextPushBiC(context, chunk, blockInChunk));
((TickableBlock) block).tick(context); context.pop();
}); });
} }
@ -94,11 +99,14 @@ public class TickChunk extends Evaluation {
if (!chunk.hasTickingTiles()) if (!chunk.hasTickingTiles())
return; return;
TickContextMutable context = TickContextMutable.uninitialized(); ServerWorldContext context = server.createContext();
Vec3i blockInWorld = new Vec3i();
chunk.forEachTickingTile((ref, tile) -> { chunk.forEachTickingTile((ref, tile) -> {
context.rebuild().withServer(server).withTile(ref); ((TickableTile) tile).tick(
((TickableTile) tile).tick(context); context.push(ref.getStack().getBlockInWorld(blockInWorld), ref.getStack().getFace(), ref.getIndex())
);
context.pop();
}); });
} }
@ -144,7 +152,7 @@ public class TickChunk extends Evaluation {
return; return;
TickableBlock tickable = (TickableBlock) block; TickableBlock tickable = (TickableBlock) block;
TickContextMutable context = TickContextMutable.start().withChunk(chunk).withBlockInChunk(blockInChunk).build(); ServerBlockContext context = contextPushBiC(server.createContext(), chunk, blockInChunk);
if (tickable.getTickingPolicy(context) != TickingPolicy.RANDOM) if (tickable.getTickingPolicy(context) != TickingPolicy.RANDOM)
return; return;
@ -164,18 +172,22 @@ public class TickChunk extends Evaluation {
if (tiles == null || tiles.isEmpty()) if (tiles == null || tiles.isEmpty())
return; return;
TSTickContext context = TickContextMutable.start().withServer(server).withTS(tiles).build(); ServerTileStackContext context = contextPushBiC(server.createContext(), chunk, blockInChunk).push(face.relativize(chunk.getUp()));
context.forEachTile(tctxt -> { for (int i = 0; i < tiles.size(); ++i) {
TileLogic logic = tctxt.getTile(); ServerTileContext tileContext = context.push(i);
TileLogic logic = tileContext.logic().getTile();
if (!(logic instanceof TickableTile)) if (!(logic instanceof TickableTile))
return; return;
TickableTile tickable = (TickableTile) logic; TickableTile tickable = (TickableTile) logic;
if (tickable.getTickingPolicy(tctxt) != TickingPolicy.RANDOM) if (tickable.getTickingPolicy(tileContext) != TickingPolicy.RANDOM)
return; return;
tickable.tick(tctxt); tickable.tick(tileContext);
});
tileContext.pop();
}
} }
private float computeRandomTicks(Server server) { private float computeRandomTicks(Server server) {
@ -184,6 +196,18 @@ public class TickChunk extends Evaluation {
server.getTickLength()); server.getTickLength());
} }
private ServerBlockContext contextPushBiC(
ServerWorldContext context,
ChunkGenericRO<?, ?, ?, ?, ?> chunk,
Vec3i blockInChunk
) {
Vec3i blockInWorld = Vectors.grab3i();
Coordinates.getInWorld(chunk.getPosition(), blockInChunk, blockInWorld);
ServerBlockContext blockContext = context.push(blockInWorld);
Vectors.release(blockInWorld);
return blockContext;
}
@Override @Override
public void getRelevantChunk(Vec3i output) { public void getRelevantChunk(Vec3i output) {
Vec3i p = chunk.getData().getPosition(); Vec3i p = chunk.getData().getPosition();

View File

@ -25,7 +25,6 @@ import ru.windcorp.progressia.common.world.Coordinates;
import ru.windcorp.progressia.common.world.rels.AbsFace; import ru.windcorp.progressia.common.world.rels.AbsFace;
import ru.windcorp.progressia.server.Server; import ru.windcorp.progressia.server.Server;
import ru.windcorp.progressia.server.world.TickAndUpdateUtil; import ru.windcorp.progressia.server.world.TickAndUpdateUtil;
import ru.windcorp.progressia.server.world.DefaultWorldLogic;
class TileTriggeredUpdate extends CachedEvaluation { class TileTriggeredUpdate extends CachedEvaluation {
@ -40,17 +39,15 @@ class TileTriggeredUpdate extends CachedEvaluation {
public void evaluate(Server server) { public void evaluate(Server server) {
Vec3i cursor = new Vec3i(blockInWorld.x, blockInWorld.y, blockInWorld.z); Vec3i cursor = new Vec3i(blockInWorld.x, blockInWorld.y, blockInWorld.z);
DefaultWorldLogic world = server.getWorld(); // Update facemates (also self)
TickAndUpdateUtil.updateTiles(server, cursor, face);
TickAndUpdateUtil.updateTiles(world, cursor, face); // Update facemates // Update block on one side
// (also self) TickAndUpdateUtil.updateBlock(server, cursor);
TickAndUpdateUtil.updateBlock(world, cursor); // Update block on one
// side
cursor.add(face.getVector()); cursor.add(face.getVector());
TickAndUpdateUtil.updateBlock(world, cursor); // Update block on the // Update block on the other side
// other side TickAndUpdateUtil.updateBlock(server, cursor);
TickAndUpdateUtil.updateTiles(world, cursor, face.getCounter()); // Update // Update complement
// complement TickAndUpdateUtil.updateTiles(server, cursor, face.getCounter());
} }
public void init(Vec3i blockInWorld, AbsFace face) { public void init(Vec3i blockInWorld, AbsFace face) {

View File

@ -21,17 +21,19 @@ package ru.windcorp.progressia.server.world.tasks;
import java.util.function.Consumer; import java.util.function.Consumer;
import glm.vec._3.i.Vec3i; 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.util.MultiLOC; import ru.windcorp.progressia.common.util.MultiLOC;
import ru.windcorp.progressia.common.world.block.BlockData; import ru.windcorp.progressia.common.world.block.BlockData;
import ru.windcorp.progressia.common.world.block.BlockDataRegistry;
import ru.windcorp.progressia.common.world.entity.EntityData; 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.BlockFace;
import ru.windcorp.progressia.common.world.tile.TileData; import ru.windcorp.progressia.common.world.tile.TileData;
import ru.windcorp.progressia.common.world.tile.TileDataRegistry;
import ru.windcorp.progressia.server.Server; import ru.windcorp.progressia.server.Server;
import ru.windcorp.progressia.server.world.context.impl.ReportingServerContext;
import ru.windcorp.progressia.server.world.ticking.TickerTask; import ru.windcorp.progressia.server.world.ticking.TickerTask;
public class WorldAccessor { public class WorldAccessor implements ReportingServerContext.ChangeListener {
private final MultiLOC cache; private final MultiLOC cache;
{ {
@ -54,38 +56,52 @@ public class WorldAccessor {
this.server = server; this.server = server;
} }
public void setBlock(Vec3i blockInWorld, BlockData block) { @Override
public void onBlockSet(Vec3i blockInWorld, BlockData block) {
SetBlock change = cache.grab(SetBlock.class); SetBlock change = cache.grab(SetBlock.class);
change.getPacket().set(block, blockInWorld); change.getPacket().set(block, blockInWorld);
server.requestChange(change); server.requestChange(change);
} }
public void setBlock(Vec3i blockInWorld, String id) { @Override
setBlock(blockInWorld, BlockDataRegistry.getInstance().get(id)); public void onTileAdded(Vec3i blockInWorld, BlockFace face, TileData tile) {
}
public void addTile(Vec3i blockInWorld, BlockFace face, TileData tile) {
AddTile change = cache.grab(AddTile.class); AddTile change = cache.grab(AddTile.class);
change.getPacket().set(tile, blockInWorld, face.resolve(server.getWorld().getUp(blockInWorld))); change.getPacket().set(tile, blockInWorld, face.resolve(server.getWorld().getUp(blockInWorld)));
server.requestChange(change); server.requestChange(change);
} }
public void addTile(Vec3i blockInWorld, BlockFace face, String id) { @Override
addTile(blockInWorld, face, TileDataRegistry.getInstance().get(id)); public void onTileRemoved(Vec3i blockInWorld, BlockFace face, int tag) {
}
public void removeTile(Vec3i blockInWorld, BlockFace face, int tag) {
RemoveTile change = cache.grab(RemoveTile.class); RemoveTile change = cache.grab(RemoveTile.class);
change.getPacket().set(blockInWorld, face.resolve(server.getWorld().getUp(blockInWorld)), tag); change.getPacket().set(blockInWorld, face.resolve(server.getWorld().getUp(blockInWorld)), tag);
server.requestChange(change); server.requestChange(change);
} }
public <T extends EntityData> void changeEntity( @Override
T entity, public void onEntityAdded(EntityData entity) {
StateChange<T> stateChange // TODO Auto-generated method stub
}
@Override
public void onEntityRemoved(long entityId) {
// TODO Auto-generated method stub
}
@Override
public void onTimeChanged(float change) {
// TODO Auto-generated method stub
System.err.println("WorldAccessor.onTimeChanged() NYI!");
}
@Override
public <SE extends StatefulObject & EntityGeneric> void onEntityChanged(
SE entity,
StateChange<SE> stateChange
) { ) {
ChangeEntity change = cache.grab(ChangeEntity.class); ChangeEntity change = cache.grab(ChangeEntity.class);
change.set(entity, stateChange); change.set((EntityData) entity, stateChange);
server.requestChange(change); server.requestChange(change);
} }

View File

@ -19,6 +19,8 @@
package ru.windcorp.progressia.server.world.tile; package ru.windcorp.progressia.server.world.tile;
import ru.windcorp.progressia.server.world.block.BlockLogic; import ru.windcorp.progressia.server.world.block.BlockLogic;
import ru.windcorp.progressia.server.world.context.ServerTileContext;
import ru.windcorp.progressia.server.world.context.ServerTileContextRO;
public class HangingTileLogic extends TileLogic implements UpdateableTile { public class HangingTileLogic extends TileLogic implements UpdateableTile {
@ -27,15 +29,15 @@ public class HangingTileLogic extends TileLogic implements UpdateableTile {
} }
@Override @Override
public void update(TileTickContext context) { public void update(ServerTileContext context) {
if (!canOccupyFace(context)) { if (!canOccupyFace(context)) {
context.removeThisTile(); context.removeTile();
} }
} }
@Override @Override
public boolean canOccupyFace(TileTickContext context) { public boolean canOccupyFace(ServerTileContextRO context) {
BlockLogic host = context.getBlock(); BlockLogic host = context.logic().getBlock();
if (host == null) if (host == null)
return false; return false;
@ -45,13 +47,14 @@ public class HangingTileLogic extends TileLogic implements UpdateableTile {
if (canBeSquashed(context)) if (canBeSquashed(context))
return true; return true;
return context.evalComplementary(ctxt -> { context.pushOpposite();
BlockLogic complHost = ctxt.getBlock(); BlockLogic complHost = context.logic().getBlock();
return complHost == null || !complHost.isSolid(ctxt, context.getFace()); boolean result = complHost == null || !complHost.isSolid(context, context.getFace());
}); context.pop();
return result;
} }
public boolean canBeSquashed(TileTickContext context) { public boolean canBeSquashed(ServerTileContextRO context) {
return false; return false;
} }

View File

@ -1,113 +0,0 @@
/*
* 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.tile;
import java.util.Objects;
import java.util.function.Consumer;
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.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 {
/*
* Specifications
*/
RelFace getFace();
/*
* Getters
*/
default TileLogicStackRO getTLSOrNull() {
DefaultChunkLogic chunkLogic = getChunkLogic();
if (chunkLogic == null)
return null;
return chunkLogic.getTilesOrNull(getBlockInChunk(), getFace());
}
default TileLogicStackRO getTLS() {
return getChunkLogic().getTiles(getBlockInChunk(), getFace());
}
default TileDataStack getTDSOrNull() {
DefaultChunkData chunkData = getChunkData();
if (chunkData == null)
return null;
return chunkData.getTilesOrNull(getBlockInChunk(), getFace());
}
default TileDataStack getTDS() {
return getChunkData().getTiles(getBlockInChunk(), getFace());
}
/*
* Contexts
*/
default TileTickContext forLayer(int layer) {
return TickContextMutable.start().withServer(getServer()).withBlock(getBlockInWorld()).withFace(getFace())
.withLayer(layer);
}
default boolean forEachTile(Consumer<TileTickContext> action) {
TickContextMutable context = TickContextMutable.uninitialized();
TileDataStack stack = getTDSOrNull();
if (stack == null || stack.isEmpty())
return false;
for (int layer = 0; layer < stack.size(); ++layer) {
context.rebuild().withServer(getServer()).withBlock(getBlockInWorld()).withFace(getFace()).withLayer(layer);
action.accept(context);
}
return true;
}
default TSTickContext getComplementary() {
return TickContextMutable.copyWorld(this)
.withBlock(getBlockInWorld().add_(getFace().getVector(getUp())))
.withFace(getFace().getCounter())
.build();
}
default <R> R evalComplementary(Function<TSTickContext, R> action) {
Objects.requireNonNull(action, "action");
return action.apply(getComplementary());
}
default void forComplementary(Consumer<TSTickContext> action) {
Objects.requireNonNull(action, "action");
evalComplementary((Function<TSTickContext, Void>) ctxt -> {
action.accept(ctxt);
return null;
});
}
}

View File

@ -18,12 +18,14 @@
package ru.windcorp.progressia.server.world.tile; package ru.windcorp.progressia.server.world.tile;
import ru.windcorp.progressia.server.world.context.ServerTileContext;
import ru.windcorp.progressia.server.world.context.ServerTileContextRO;
import ru.windcorp.progressia.server.world.ticking.TickingPolicy; import ru.windcorp.progressia.server.world.ticking.TickingPolicy;
public interface TickableTile { public interface TickableTile {
void tick(TileTickContext context); void tick(ServerTileContext context);
TickingPolicy getTickingPolicy(TileTickContext context); TickingPolicy getTickingPolicy(ServerTileContextRO context);
} }

View File

@ -21,6 +21,7 @@ package ru.windcorp.progressia.server.world.tile;
import ru.windcorp.progressia.common.util.namespaces.Namespaced; import ru.windcorp.progressia.common.util.namespaces.Namespaced;
import ru.windcorp.progressia.common.world.generic.TileGeneric; import ru.windcorp.progressia.common.world.generic.TileGeneric;
import ru.windcorp.progressia.common.world.rels.RelFace; import ru.windcorp.progressia.common.world.rels.RelFace;
import ru.windcorp.progressia.server.world.context.ServerTileContextRO;
public class TileLogic extends Namespaced implements TileGeneric { public class TileLogic extends Namespaced implements TileGeneric {
@ -28,7 +29,7 @@ public class TileLogic extends Namespaced implements TileGeneric {
super(id); super(id);
} }
public boolean canOccupyFace(TileTickContext context) { public boolean canOccupyFace(ServerTileContextRO context) {
return canOccupyFace(context.getFace()); return canOccupyFace(context.getFace());
} }
@ -36,7 +37,7 @@ public class TileLogic extends Namespaced implements TileGeneric {
return true; return true;
} }
public boolean isSolid(TileTickContext context) { public boolean isSolid(ServerTileContextRO context) {
return isSolid(); return isSolid();
} }

View File

@ -1,77 +0,0 @@
/*
* 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.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;
public interface TileTickContext extends TSTickContext {
/*
* Specifications
*/
/**
* Returns the current layer.
*
* @return the layer that the tile being ticked occupies in the tile stack
*/
int getLayer();
/*
* Getters
*/
default TileLogic getTile() {
TileLogicStackRO stack = getTLSOrNull();
if (stack == null)
return null;
return stack.get(getLayer());
}
default TileData getTileData() {
TileDataStack stack = getTDSOrNull();
if (stack == null)
return null;
return stack.get(getLayer());
}
default TileDataReference getReference() {
return getTDS().getReference(getLayer());
}
default int getTag() {
return getTDS().getTagByIndex(getLayer());
}
/*
* Contexts
*/
/*
* Convenience methods - changes
*/
default void removeThisTile() {
getAccessor().removeTile(getBlockInWorld(), getFace(), getTag());
}
}

View File

@ -18,8 +18,10 @@
package ru.windcorp.progressia.server.world.tile; package ru.windcorp.progressia.server.world.tile;
import ru.windcorp.progressia.server.world.context.ServerTileContext;
public interface UpdateableTile { public interface UpdateableTile {
void update(TileTickContext context); void update(ServerTileContext context);
} }

View File

@ -383,7 +383,7 @@ public class TestContent {
ru.windcorp.progressia.server.comms.Client client ru.windcorp.progressia.server.comms.Client client
) { ) {
Vec3i blockInWorld = ((ControlBreakBlockData) packet.getControl()).getBlockInWorld(); Vec3i blockInWorld = ((ControlBreakBlockData) packet.getControl()).getBlockInWorld();
server.getWorldAccessor().setBlock(blockInWorld, BlockDataRegistry.getInstance().get("Test:Air")); server.createContext().setBlock(blockInWorld, BlockDataRegistry.getInstance().get("Test:Air"));
} }
private static void onBlockPlaceTrigger(ControlData control) { private static void onBlockPlaceTrigger(ControlData control) {
@ -403,7 +403,7 @@ public class TestContent {
Vec3i blockInWorld = controlData.getBlockInWorld(); Vec3i blockInWorld = controlData.getBlockInWorld();
if (server.getWorld().getData().getChunkByBlock(blockInWorld) == null) if (server.getWorld().getData().getChunkByBlock(blockInWorld) == null)
return; return;
server.getWorldAccessor().setBlock(blockInWorld, block); server.createContext().setBlock(blockInWorld, block);
} }
private static void onTilePlaceTrigger(ControlData control) { private static void onTilePlaceTrigger(ControlData control) {
@ -428,7 +428,7 @@ public class TestContent {
return; return;
if (server.getWorld().getData().getTiles(blockInWorld, face).isFull()) if (server.getWorld().getData().getTiles(blockInWorld, face).isFull())
return; return;
server.getWorldAccessor().addTile(blockInWorld, face, tile); server.createContext().addTile(blockInWorld, face, tile);
} }
private static void registerMisc() { private static void registerMisc() {

View File

@ -19,7 +19,7 @@
package ru.windcorp.progressia.test; package ru.windcorp.progressia.test;
import ru.windcorp.progressia.common.world.entity.EntityData; import ru.windcorp.progressia.common.world.entity.EntityData;
import ru.windcorp.progressia.server.world.TickContext; import ru.windcorp.progressia.server.world.context.ServerWorldContext;
import ru.windcorp.progressia.server.world.entity.EntityLogic; import ru.windcorp.progressia.server.world.entity.EntityLogic;
public class TestEntityLogicStatie extends EntityLogic { public class TestEntityLogicStatie extends EntityLogic {
@ -29,13 +29,13 @@ public class TestEntityLogicStatie extends EntityLogic {
} }
@Override @Override
public void tick(EntityData entity, TickContext context) { public void tick(EntityData entity, ServerWorldContext context) {
super.tick(entity, context); super.tick(entity, context);
TestEntityDataStatie statie = (TestEntityDataStatie) entity; TestEntityDataStatie statie = (TestEntityDataStatie) entity;
int size = (int) (18 + 6 * Math.sin(entity.getAge())); int size = (int) (18 + 6 * Math.sin(entity.getAge()));
context.getServer().getWorldAccessor().changeEntity(statie, e -> e.setSizeNow(size)); context.changeEntity(statie, e -> e.setSizeNow(size));
} }
} }

View File

@ -20,11 +20,11 @@ package ru.windcorp.progressia.test;
import ru.windcorp.progressia.common.world.rels.RelFace; import ru.windcorp.progressia.common.world.rels.RelFace;
import ru.windcorp.progressia.server.world.block.BlockLogic; import ru.windcorp.progressia.server.world.block.BlockLogic;
import ru.windcorp.progressia.server.world.block.BlockTickContext; import ru.windcorp.progressia.server.world.context.ServerTileContext;
import ru.windcorp.progressia.server.world.context.ServerTileContextRO;
import ru.windcorp.progressia.server.world.ticking.TickingPolicy; import ru.windcorp.progressia.server.world.ticking.TickingPolicy;
import ru.windcorp.progressia.server.world.tile.HangingTileLogic; import ru.windcorp.progressia.server.world.tile.HangingTileLogic;
import ru.windcorp.progressia.server.world.tile.TickableTile; import ru.windcorp.progressia.server.world.tile.TickableTile;
import ru.windcorp.progressia.server.world.tile.TileTickContext;
public class TestTileLogicGrass extends HangingTileLogic implements TickableTile { public class TestTileLogicGrass extends HangingTileLogic implements TickableTile {
@ -33,7 +33,7 @@ public class TestTileLogicGrass extends HangingTileLogic implements TickableTile
} }
@Override @Override
public boolean canOccupyFace(TileTickContext context) { public boolean canOccupyFace(ServerTileContextRO context) {
return context.getFace() != RelFace.DOWN && super.canOccupyFace(context); return context.getFace() != RelFace.DOWN && super.canOccupyFace(context);
} }
@ -43,34 +43,31 @@ public class TestTileLogicGrass extends HangingTileLogic implements TickableTile
} }
@Override @Override
public TickingPolicy getTickingPolicy(TileTickContext context) { public TickingPolicy getTickingPolicy(ServerTileContextRO context) {
return TickingPolicy.RANDOM; return TickingPolicy.RANDOM;
} }
@Override @Override
public void tick(TileTickContext context) { public void tick(ServerTileContext context) {
if (!isLocationSuitable(context)) { if (!isLocationSuitable(context)) {
// context.removeThisTile(); // context.removeThisTile();
} }
} }
@Override @Override
public boolean canBeSquashed(TileTickContext context) { public boolean canBeSquashed(ServerTileContextRO context) {
return true; return true;
} }
private boolean isLocationSuitable(TileTickContext context) { private boolean isLocationSuitable(ServerTileContextRO context) {
return canOccupyFace(context) && isBlockAboveTransparent(context); return canOccupyFace(context) && isBlockAboveTransparent(context);
} }
private boolean isBlockAboveTransparent(BlockTickContext context) { private boolean isBlockAboveTransparent(ServerTileContextRO context) {
return context.evalNeighbor(RelFace.UP, bctxt -> { // TODO rework
BlockLogic block = bctxt.getBlock(); context.pushRelative(RelFace.UP.resolve(context.getServer().getWorld().getUp(context.getLocation())));
if (block == null) BlockLogic block = context.logic().getBlock();
return true; return context.popAndReturn(block == null || block.isTransparent(context));
return block.isTransparent(bctxt);
});
} }
} }