From 54c66d28d6579bad0d4973a741a8b8c343908af4 Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Fri, 13 Aug 2021 16:11:46 +0300 Subject: [PATCH] Added TransformingServerContext and RotatingServerContext. WIP There is a problem with faces in contexts when up != POS_Z, world crashes soon after startup - Added TransformingServerContext - a common basis for context wrappers that alter the coordinate space - Added RotatingServerContext - a context wrapper that rotates the coordinate space - Used to ensure positive Z is up - PacketAffectTile now checks the provided tile tag for validity - This causes a crash when the invalid action is requested, not executed - TickChunk task reuses contexts --- .../world/generic/context/WorldContexts.java | 76 +++++ .../common/world/tile/PacketAffectTile.java | 4 + .../ru/windcorp/progressia/server/Server.java | 57 +++- .../server/world/DefaultChunkLogic.java | 30 +- .../server/world/TickAndUpdateUtil.java | 31 +- .../server/world/context/ServerContexts.java | 141 +++++++++ .../impl/DefaultServerContextImpl.java | 36 +++ .../impl/DefaultServerContextLogic.java | 21 ++ .../context/impl/FilterServerContext.java | 21 ++ .../context/impl/RotatingServerContext.java | 54 ++++ .../impl/TransformingServerContext.java | 273 ++++++++++++++++++ .../server/world/tasks/TickChunk.java | 90 +++--- .../windcorp/progressia/test/TestContent.java | 6 +- .../progressia/test/TestTileLogicGrass.java | 3 +- 14 files changed, 749 insertions(+), 94 deletions(-) create mode 100644 src/main/java/ru/windcorp/progressia/server/world/context/ServerContexts.java create mode 100644 src/main/java/ru/windcorp/progressia/server/world/context/impl/RotatingServerContext.java create mode 100644 src/main/java/ru/windcorp/progressia/server/world/context/impl/TransformingServerContext.java diff --git a/src/main/java/ru/windcorp/progressia/common/world/generic/context/WorldContexts.java b/src/main/java/ru/windcorp/progressia/common/world/generic/context/WorldContexts.java index 30b85ce..5cf9f44 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/generic/context/WorldContexts.java +++ b/src/main/java/ru/windcorp/progressia/common/world/generic/context/WorldContexts.java @@ -20,7 +20,9 @@ package ru.windcorp.progressia.common.world.generic.context; import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.common.world.context.Context; +import ru.windcorp.progressia.common.world.rels.AbsFace; import ru.windcorp.progressia.common.world.rels.AbsRelation; +import ru.windcorp.progressia.common.world.rels.BlockFace; import ru.windcorp.progressia.common.world.rels.RelFace; /** @@ -81,6 +83,80 @@ class WorldContexts { */ Tile push(Vec3i location, RelFace face, int layer); + /** + * Converts the provided location given in the context's coordinate + * space to the underlying absolute coordinate space. + *

+ * The definition of "absolute coordinate space" for contexts that are + * not {@linkplain #isReal() real} may be arbitrary, but methods + * {@link #toAbsolute(Vec3i, Vec3i)}, {@link #toAbsolute(BlockFace)}, + * {@link #toContext(Vec3i, Vec3i)} and {@link #toContext(AbsFace)} are + * guaranteed to be consistent for all contexts. + * + * @param contextLocation the location expressed in context coordinate + * system + * @param output the {@link Vec3i} object to output the result + * into, or {@code null} + * @return The location expressed in absolute coordinate system. If + * {@code output} is not {@code null}, {@code output} is + * returned; otherwise, a new {@link Vec3i} is instantiated and + * returned + */ + Vec3i toAbsolute(Vec3i contextLocation, Vec3i output); + + /** + * Converts the provided location given in the absolute coordinate + * space to the context coordinate space. + *

+ * The definition of "absolute coordinate space" for contexts that are + * not {@linkplain #isReal() real} may be arbitrary, but methods + * {@link #toAbsolute(Vec3i, Vec3i)}, {@link #toAbsolute(BlockFace)}, + * {@link #toContext(Vec3i, Vec3i)} and {@link #toContext(AbsFace)} are + * guaranteed to be consistent for all contexts. + * + * @param contextLocation the location expressed in absolute coordinate + * system + * @param output the {@link Vec3i} object to output the result + * into, or {@code null} + * @return The location expressed in context coordinate system. If + * {@code output} is not {@code null}, {@code output} is + * returned; otherwise, a new {@link Vec3i} is instantiated and + * returned + */ + Vec3i toContext(Vec3i absoluteLocation, Vec3i output); + + /** + * Converts the provided face given in the context's coordinate + * space to the underlying absolute coordinate space. + *

+ * The definition of "absolute coordinate space" for contexts that are + * not {@linkplain #isReal() real} may be arbitrary, but methods + * {@link #toAbsolute(Vec3i, Vec3i)}, {@link #toAbsolute(BlockFace)}, + * {@link #toContext(Vec3i, Vec3i)} and {@link #toContext(AbsFace)} are + * guaranteed to be consistent for all contexts. + * + * @param contextLocation the face expressed in context coordinate + * system + * @return the face expressed in absolute coordinate system + */ + AbsFace toAbsolute(BlockFace contextFace); + + /** + * Converts the provided face given in the absolute coordinate + * space to the context coordinate space. + *

+ * The definition of "absolute coordinate space" for contexts that are + * not {@linkplain #isReal() real} may be arbitrary, but methods + * {@link #toAbsolute(Vec3i, Vec3i)}, {@link #toAbsolute(BlockFace)}, + * {@link #toContext(Vec3i, Vec3i)} and {@link #toContext(AbsFace)} are + * guaranteed to be consistent for all contexts. + * + * @param contextLocation the face expressed in absolute coordinate + * system + * @return the face expressed in context coordinate system + */ + RelFace toContext(AbsFace absoluteFace); + } /** diff --git a/src/main/java/ru/windcorp/progressia/common/world/tile/PacketAffectTile.java b/src/main/java/ru/windcorp/progressia/common/world/tile/PacketAffectTile.java index 9be01e2..41cffef 100644 --- a/src/main/java/ru/windcorp/progressia/common/world/tile/PacketAffectTile.java +++ b/src/main/java/ru/windcorp/progressia/common/world/tile/PacketAffectTile.java @@ -51,6 +51,10 @@ public abstract class PacketAffectTile extends PacketAffectChunk { } public void set(Vec3i blockInWorld, AbsFace face, int tag) { + if (tag < 0) { + throw new IllegalArgumentException("Cannot affect tile with tag -1"); + } + this.blockInWorld.set(blockInWorld.x, blockInWorld.y, blockInWorld.z); this.face = face; this.tag = tag; diff --git a/src/main/java/ru/windcorp/progressia/server/Server.java b/src/main/java/ru/windcorp/progressia/server/Server.java index 0efc551..915c262 100644 --- a/src/main/java/ru/windcorp/progressia/server/Server.java +++ b/src/main/java/ru/windcorp/progressia/server/Server.java @@ -24,20 +24,26 @@ import org.apache.logging.log4j.LogManager; import com.google.common.eventbus.EventBus; +import glm.vec._3.i.Vec3i; import ru.windcorp.jputil.functions.ThrowingRunnable; import ru.windcorp.progressia.common.Units; import ru.windcorp.progressia.common.util.TaskQueue; import ru.windcorp.progressia.common.util.crash.ReportingEventBus; import ru.windcorp.progressia.common.world.DefaultWorldData; +import ru.windcorp.progressia.common.world.rels.AbsFace; +import ru.windcorp.progressia.common.world.rels.AxisRotations; import ru.windcorp.progressia.server.comms.ClientManager; 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.DefaultWorldLogic; +import ru.windcorp.progressia.server.world.context.ServerBlockContext; +import ru.windcorp.progressia.server.world.context.ServerTileContext; 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.context.impl.RotatingServerContext; import ru.windcorp.progressia.server.world.tasks.WorldAccessor; import ru.windcorp.progressia.server.world.ticking.Change; import ru.windcorp.progressia.server.world.ticking.Evaluation; @@ -102,15 +108,54 @@ public class Server { /** * 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. + * suitable for read and write access to the server's world. This context + * uses the absolute coordinate space (not rotated to match positive Z = + * up). * * @return the context + * @see #createContext(AbsFace) */ - public ServerWorldContext createContext() { + public ServerWorldContext createAbsoluteContext() { + return doCreateAbsoluteContext(); + } - return new ReportingServerContext(DefaultServerContext.empty().inRealWorldOf(this).build()).withListener(worldAccessor).setPassToParent(false); - + private ServerTileContext doCreateAbsoluteContext() { + return new ReportingServerContext(DefaultServerContext.empty().inRealWorldOf(this).build()) + .withListener(worldAccessor).setPassToParent(false); + } + + /** + * 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. This context uses the + * coordinate space in which positive Z = {@code up}. + * + * @param up the desired up direction + * @return the context + * @see #createContext(Vec3i) + * @see #createAbsoluteContext() + */ + public ServerWorldContext createContext(AbsFace up) { + return new RotatingServerContext(doCreateAbsoluteContext(), up); + } + + /** + * Instantiates and returns an new {@link ServerBlockContext} instance + * suitable for read and write access to the server's world. The context is + * initialized to point to the provided block. This is the preferred way to + * query or change the world. This context uses the coordinate space in + * which positive Z matches the discrete up direction of the provided + * location. + * + * @param up the desired up direction + * @return the context + * @see #createContext(AbsFace) + * @see #createAbsoluteContext() + */ + public ServerBlockContext createContext(Vec3i blockInWorld) { + AbsFace up = getWorld().getUp(blockInWorld); + Vec3i relativeBlockInWorld = AxisRotations.relativize(blockInWorld, up, null); + return new RotatingServerContext(doCreateAbsoluteContext(), up).push(relativeBlockInWorld); } /** @@ -247,7 +292,7 @@ public class Server { // public WorldAccessor getWorldAccessor() { // return worldAccessor; // } - + public WorldAccessor getWorldAccessor___really_bad_dont_use() { return worldAccessor; } diff --git a/src/main/java/ru/windcorp/progressia/server/world/DefaultChunkLogic.java b/src/main/java/ru/windcorp/progressia/server/world/DefaultChunkLogic.java index 978fae3..d18ff11 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/DefaultChunkLogic.java +++ b/src/main/java/ru/windcorp/progressia/server/world/DefaultChunkLogic.java @@ -28,17 +28,18 @@ 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.generic.GenericChunks; 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.BlockLogicRegistry; import ru.windcorp.progressia.server.world.block.TickableBlock; import ru.windcorp.progressia.server.world.context.ServerBlockContextRO; +import ru.windcorp.progressia.server.world.context.ServerContexts; import ru.windcorp.progressia.server.world.context.ServerTileContextRO; import ru.windcorp.progressia.server.world.context.ServerWorldContextRO; import ru.windcorp.progressia.server.world.tasks.TickChunk; @@ -225,15 +226,12 @@ public class DefaultChunkLogic implements ChunkLogic { } private void tmp_generateTickLists() { - ServerWorldContextRO context = Server.getCurrentServer().createContext(); - Vec3i blockInChunk = new Vec3i(); + ServerWorldContextRO context = Server.getCurrentServer().createContext(getUp()); - forEachBiW(location -> { + GenericChunks.forEachBiC(blockInChunk -> { - ServerBlockContextRO blockContext = context.push(location); - - BlockLogic block = blockContext.logic().getBlock(); - Coordinates.convertInWorldToInChunk(location, blockInChunk); + ServerBlockContextRO blockContext = ServerContexts.pushAbs(context, this, blockInChunk); + BlockLogic block = getBlock(blockInChunk); if (!(block instanceof TickableBlock)) { blockContext.pop(); @@ -241,7 +239,7 @@ public class DefaultChunkLogic implements ChunkLogic { } if (((TickableBlock) block).getTickingPolicy(blockContext) == TickingPolicy.REGULAR) { - tickingBlocks.add(blockInChunk); + tickingBlocks.add(blockInChunk.add_(0)); } for (RelFace face : RelFace.getFaces()) { @@ -249,16 +247,14 @@ public class DefaultChunkLogic implements ChunkLogic { if (stack == null || stack.isEmpty()) continue; for (int i = 0; i < stack.size(); ++i) { - ServerTileContextRO tileContext = blockContext.push(face, i); + ServerTileContextRO tileContext = blockContext.push(context.toContext(face.resolve(getUp())), i); TileLogic tile = stack.get(i); - if (!(tile instanceof TickableTile)) { - tileContext.pop(); - continue; - } - - if (((TickableTile) tile).getTickingPolicy(tileContext) == TickingPolicy.REGULAR) { - tickingTiles.add(stack.getData().getReference(i)); + if (tile instanceof TickableTile) { + TickingPolicy policy = ((TickableTile) tile).getTickingPolicy(tileContext); + if (policy == TickingPolicy.REGULAR) { + tickingTiles.add(stack.getData().getReference(i)); + } } tileContext.pop(); diff --git a/src/main/java/ru/windcorp/progressia/server/world/TickAndUpdateUtil.java b/src/main/java/ru/windcorp/progressia/server/world/TickAndUpdateUtil.java index 286a557..9e21c63 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/TickAndUpdateUtil.java +++ b/src/main/java/ru/windcorp/progressia/server/world/TickAndUpdateUtil.java @@ -21,12 +21,13 @@ package ru.windcorp.progressia.server.world; import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.common.util.crash.CrashReports; import ru.windcorp.progressia.common.world.entity.EntityData; -import ru.windcorp.progressia.common.world.rels.BlockFace; +import ru.windcorp.progressia.common.world.rels.AbsFace; import ru.windcorp.progressia.server.Server; import ru.windcorp.progressia.server.world.block.BlockLogic; import ru.windcorp.progressia.server.world.block.TickableBlock; import ru.windcorp.progressia.server.world.block.UpdateableBlock; import ru.windcorp.progressia.server.world.context.ServerBlockContext; +import ru.windcorp.progressia.server.world.context.ServerContexts; import ru.windcorp.progressia.server.world.context.ServerTileContext; import ru.windcorp.progressia.server.world.context.ServerTileStackContext; import ru.windcorp.progressia.server.world.context.ServerWorldContext; @@ -56,7 +57,7 @@ public class TickAndUpdateUtil { if (!(block instanceof TickableBlock)) { return; } - ServerBlockContext context = server.createContext().push(blockInWorld); + ServerBlockContext context = server.createContext(blockInWorld); tickBlock(context); } @@ -73,23 +74,22 @@ public class TickAndUpdateUtil { } } - public static void tickTile(Server server, Vec3i blockInWorld, BlockFace face, int layer) { + public static void tickTile(Server server, Vec3i blockInWorld, AbsFace face, int layer) { TileLogic tile = server.getWorld().getTile(blockInWorld, face, layer); if (!(tile instanceof TickableTile)) { return; } - ServerTileContext context = server.createContext() - .push(blockInWorld, face.relativize(server.getWorld().getUp(blockInWorld)), layer); + ServerTileContext context = ServerContexts.pushAbs(server.createContext(blockInWorld), blockInWorld, face) + .push(layer); tickTile(context); } - public static void tickTiles(Server server, Vec3i blockInWorld, BlockFace face) { + public static void tickTiles(Server server, Vec3i blockInWorld, AbsFace face) { if (!server.getWorld().hasTiles(blockInWorld, face)) { return; } - ServerTileStackContext context = server.createContext() - .push(blockInWorld, face.relativize(server.getWorld().getUp(blockInWorld))); + ServerTileStackContext context = ServerContexts.pushAbs(server.createContext(blockInWorld), blockInWorld, face); for (int i = 0; i < context.getTileCount(); ++i) { tickTile(context.push(i)); context.pop(); @@ -114,7 +114,7 @@ public class TickAndUpdateUtil { if (!(block instanceof UpdateableBlock)) { return; } - ServerBlockContext context = server.createContext().push(blockInWorld); + ServerBlockContext context = server.createContext(blockInWorld); updateBlock(context); } @@ -131,23 +131,22 @@ public class TickAndUpdateUtil { } } - public static void updateTile(Server server, Vec3i blockInWorld, BlockFace face, int layer) { + public static void updateTile(Server server, Vec3i blockInWorld, AbsFace face, int layer) { TileLogic tile = server.getWorld().getTile(blockInWorld, face, layer); if (!(tile instanceof UpdateableTile)) { return; } - ServerTileContext context = server.createContext() - .push(blockInWorld, face.relativize(server.getWorld().getUp(blockInWorld)), layer); + ServerTileContext context = ServerContexts.pushAbs(server.createContext(blockInWorld), blockInWorld, face) + .push(layer); updateTile(context); } - public static void updateTiles(Server server, Vec3i blockInWorld, BlockFace face) { + public static void updateTiles(Server server, Vec3i blockInWorld, AbsFace face) { if (!server.getWorld().hasTiles(blockInWorld, face)) { return; } - ServerTileStackContext context = server.createContext() - .push(blockInWorld, face.relativize(server.getWorld().getUp(blockInWorld))); + ServerTileStackContext context = ServerContexts.pushAbs(server.createContext(blockInWorld), blockInWorld, face); for (int i = 0; i < context.getTileCount(); ++i) { updateTile(context.push(i)); context.pop(); @@ -166,7 +165,7 @@ public class TickAndUpdateUtil { tickEntity( EntityLogicRegistry.getInstance().get(data.getId()), data, - server.createContext() + server.createContext(data.getUpFace()) ); } diff --git a/src/main/java/ru/windcorp/progressia/server/world/context/ServerContexts.java b/src/main/java/ru/windcorp/progressia/server/world/context/ServerContexts.java new file mode 100644 index 0000000..b0cf996 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/world/context/ServerContexts.java @@ -0,0 +1,141 @@ +/* + * Progressia + * Copyright (C) 2020-2021 Wind Corporation and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package ru.windcorp.progressia.server.world.context; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.util.Vectors; +import ru.windcorp.progressia.common.world.Coordinates; +import ru.windcorp.progressia.common.world.generic.ChunkGenericRO; +import ru.windcorp.progressia.common.world.generic.TileGenericReferenceRO; +import ru.windcorp.progressia.common.world.rels.AbsFace; +import ru.windcorp.progressia.common.world.rels.RelFace; + +public class ServerContexts { + + /* + * RW + */ + + public static ServerBlockContext pushAbs(ServerWorldContext context, Vec3i blockInWorld) { + Vec3i contextLocation = Vectors.grab3i(); + context.toContext(blockInWorld, contextLocation); + ServerBlockContext blockContext = context.push(contextLocation); + Vectors.release(contextLocation); + return blockContext; + } + + public static ServerBlockContext pushAbs(ServerWorldContext context, ChunkGenericRO chunk, Vec3i blockInChunk) { + Vec3i contextLocation = Vectors.grab3i(); + Coordinates.getInWorld(chunk.getPosition(), blockInChunk, contextLocation); + context.toContext(contextLocation, contextLocation); + ServerBlockContext blockContext = context.push(contextLocation); + Vectors.release(contextLocation); + return blockContext; + } + + public static ServerTileStackContext pushAbs(ServerWorldContext context, Vec3i blockInWorld, AbsFace face) { + Vec3i contextLocation = Vectors.grab3i(); + context.toContext(blockInWorld, contextLocation); + RelFace contextFace = context.toContext(face); + ServerTileStackContext tileStackContext = context.push(contextLocation, contextFace); + Vectors.release(contextLocation); + return tileStackContext; + } + + public static ServerTileStackContext pushAbs(ServerWorldContext context, ChunkGenericRO chunk, Vec3i blockInChunk, AbsFace face) { + Vec3i contextLocation = Vectors.grab3i(); + Coordinates.getInWorld(chunk.getPosition(), blockInChunk, contextLocation); + context.toContext(contextLocation, contextLocation); + RelFace contextFace = context.toContext(face); + ServerTileStackContext tileStackContext = context.push(contextLocation, contextFace); + Vectors.release(contextLocation); + return tileStackContext; + } + + public static ServerTileStackContext pushAbs(ServerBlockContext context, AbsFace face) { + return context.push(context.toContext(face)); + } + + public static ServerTileContext pushAbs(ServerWorldContext context, AbsFace up, TileGenericReferenceRO ref) { + Vec3i contextLocation = Vectors.grab3i(); + ref.getStack().getBlockInWorld(contextLocation); + context.toContext(contextLocation, contextLocation); + RelFace contextFace = context.toContext(ref.getStack().getFace().resolve(up)); + ServerTileContext tileContext = context.push(contextLocation, contextFace, ref.getIndex()); + Vectors.release(contextLocation); + return tileContext; + } + + /* + * RO + */ + + public static ServerBlockContextRO pushAbs(ServerWorldContextRO context, Vec3i blockInWorld) { + Vec3i contextLocation = Vectors.grab3i(); + context.toContext(blockInWorld, contextLocation); + ServerBlockContextRO blockContextRO = context.push(contextLocation); + Vectors.release(contextLocation); + return blockContextRO; + } + + public static ServerBlockContextRO pushAbs(ServerWorldContextRO context, ChunkGenericRO chunk, Vec3i blockInChunk) { + Vec3i contextLocation = Vectors.grab3i(); + Coordinates.getInWorld(chunk.getPosition(), blockInChunk, contextLocation); + context.toContext(contextLocation, contextLocation); + ServerBlockContextRO blockContextRO = context.push(contextLocation); + Vectors.release(contextLocation); + return blockContextRO; + } + + public static ServerTileStackContextRO pushAbs(ServerWorldContextRO context, Vec3i blockInWorld, AbsFace face) { + Vec3i contextLocation = Vectors.grab3i(); + context.toContext(blockInWorld, contextLocation); + RelFace contextFace = context.toContext(face); + ServerTileStackContextRO tileStackContextRO = context.push(contextLocation, contextFace); + Vectors.release(contextLocation); + return tileStackContextRO; + } + + public static ServerTileStackContextRO pushAbs(ServerWorldContextRO context, ChunkGenericRO chunk, Vec3i blockInChunk, AbsFace face) { + Vec3i contextLocation = Vectors.grab3i(); + Coordinates.getInWorld(chunk.getPosition(), blockInChunk, contextLocation); + context.toContext(contextLocation, contextLocation); + RelFace contextFace = context.toContext(face); + ServerTileStackContextRO tileStackContextRO = context.push(contextLocation, contextFace); + Vectors.release(contextLocation); + return tileStackContextRO; + } + + public static ServerTileStackContextRO pushAbs(ServerBlockContextRO context, AbsFace face) { + return context.push(context.toContext(face)); + } + + public static ServerTileContextRO pushAbs(ServerWorldContextRO context, AbsFace up, TileGenericReferenceRO ref) { + Vec3i contextLocation = Vectors.grab3i(); + ref.getStack().getBlockInWorld(contextLocation); + context.toContext(contextLocation, contextLocation); + RelFace contextFace = context.toContext(ref.getStack().getFace().resolve(up)); + ServerTileContextRO tileContextRO = context.push(contextLocation, contextFace, ref.getIndex()); + Vectors.release(contextLocation); + return tileContextRO; + } + + private ServerContexts() { + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/world/context/impl/DefaultServerContextImpl.java b/src/main/java/ru/windcorp/progressia/server/world/context/impl/DefaultServerContextImpl.java index ec89e47..aeda6e8 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/context/impl/DefaultServerContextImpl.java +++ b/src/main/java/ru/windcorp/progressia/server/world/context/impl/DefaultServerContextImpl.java @@ -29,6 +29,7 @@ import ru.windcorp.progressia.common.world.WorldData; import ru.windcorp.progressia.common.world.block.BlockData; import ru.windcorp.progressia.common.world.entity.EntityData; import ru.windcorp.progressia.common.world.generic.EntityGeneric; +import ru.windcorp.progressia.common.world.rels.AbsFace; import ru.windcorp.progressia.common.world.rels.BlockFace; import ru.windcorp.progressia.common.world.rels.RelFace; import ru.windcorp.progressia.common.world.tile.TileData; @@ -290,6 +291,41 @@ class DefaultServerContextImpl extends DefaultServerContext assert requireContextRole(Role.TILE); return frame.layer; } + + /* + * ABSOLUTE COORDINATE CONVERSIONS + * (or lack thereof) + */ + + @Override + public Vec3i toAbsolute(Vec3i contextLocation, Vec3i output) { + if (output == null) { + output = new Vec3i(); + } + + output.set(contextLocation.x, contextLocation.y, contextLocation.z); + return output; + } + + @Override + public Vec3i toContext(Vec3i absoluteLocation, Vec3i output) { + if (output == null) { + output = new Vec3i(); + } + + output.set(absoluteLocation.x, absoluteLocation.y, absoluteLocation.z); + return output; + } + + @Override + public AbsFace toAbsolute(BlockFace contextFace) { + return contextFace.resolve(AbsFace.POS_Z); + } + + @Override + public RelFace toContext(AbsFace absoluteFace) { + return absoluteFace.relativize(AbsFace.POS_Z); + } /* * RO CONTEXT INTERFACE diff --git a/src/main/java/ru/windcorp/progressia/server/world/context/impl/DefaultServerContextLogic.java b/src/main/java/ru/windcorp/progressia/server/world/context/impl/DefaultServerContextLogic.java index 4461d23..640a475 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/context/impl/DefaultServerContextLogic.java +++ b/src/main/java/ru/windcorp/progressia/server/world/context/impl/DefaultServerContextLogic.java @@ -23,6 +23,7 @@ import java.util.Random; import glm.vec._3.i.Vec3i; import ru.windcorp.progressia.common.world.block.BlockData; import ru.windcorp.progressia.common.world.entity.EntityData; +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.tile.TileData; @@ -84,6 +85,26 @@ public class DefaultServerContextLogic implements ServerTileContext.Logic { parent.push(location, face, layer); return this; } + + @Override + public Vec3i toAbsolute(Vec3i contextLocation, Vec3i output) { + return parent.toAbsolute(contextLocation, output); + } + + @Override + public Vec3i toContext(Vec3i absoluteLocation, Vec3i output) { + return parent.toContext(absoluteLocation, output); + } + + @Override + public AbsFace toAbsolute(BlockFace contextFace) { + return parent.toAbsolute(contextFace); + } + + @Override + public RelFace toContext(AbsFace absoluteFace) { + return parent.toContext(absoluteFace); + } @Override public double getTickLength() { diff --git a/src/main/java/ru/windcorp/progressia/server/world/context/impl/FilterServerContext.java b/src/main/java/ru/windcorp/progressia/server/world/context/impl/FilterServerContext.java index b7b5ff5..677f9d3 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/context/impl/FilterServerContext.java +++ b/src/main/java/ru/windcorp/progressia/server/world/context/impl/FilterServerContext.java @@ -27,6 +27,7 @@ import ru.windcorp.progressia.common.world.GravityModel; import ru.windcorp.progressia.common.world.block.BlockData; import ru.windcorp.progressia.common.world.entity.EntityData; import ru.windcorp.progressia.common.world.generic.EntityGeneric; +import ru.windcorp.progressia.common.world.rels.AbsFace; import ru.windcorp.progressia.common.world.rels.BlockFace; import ru.windcorp.progressia.common.world.rels.RelFace; import ru.windcorp.progressia.common.world.tile.TileData; @@ -199,6 +200,26 @@ public abstract class FilterServerContext implements ServerTileContext { parent.push(location, face, layer); return this; } + + @Override + public Vec3i toAbsolute(Vec3i contextLocation, Vec3i output) { + return parent.toAbsolute(contextLocation, output); + } + + @Override + public Vec3i toContext(Vec3i absoluteLocation, Vec3i output) { + return parent.toContext(absoluteLocation, output); + } + + @Override + public AbsFace toAbsolute(BlockFace contextFace) { + return parent.toAbsolute(contextFace); + } + + @Override + public RelFace toContext(AbsFace absoluteFace) { + return parent.toContext(absoluteFace); + } @Override public Server getServer() { diff --git a/src/main/java/ru/windcorp/progressia/server/world/context/impl/RotatingServerContext.java b/src/main/java/ru/windcorp/progressia/server/world/context/impl/RotatingServerContext.java new file mode 100644 index 0000000..2ccc86c --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/world/context/impl/RotatingServerContext.java @@ -0,0 +1,54 @@ +/* + * Progressia + * Copyright (C) 2020-2021 Wind Corporation and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package ru.windcorp.progressia.server.world.context.impl; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.world.rels.AbsFace; +import ru.windcorp.progressia.common.world.rels.RelFace; +import ru.windcorp.progressia.server.world.context.ServerTileContext; + +public class RotatingServerContext extends TransformingServerContext { + + private final AbsFace up; + + public RotatingServerContext(ServerTileContext parent, AbsFace up) { + super(parent); + this.up = up; + } + + @Override + protected void transform(Vec3i userLocation, Vec3i output) { + output.set(userLocation.x, userLocation.y, userLocation.z); + } + + @Override + protected void untransform(Vec3i parentLocation, Vec3i output) { + output.set(parentLocation.x, parentLocation.y, parentLocation.z); + } + + @Override + protected RelFace transform(RelFace userFace) { + return userFace.resolve(up).relativize(AbsFace.POS_Z); + } + + @Override + protected RelFace untransform(RelFace parentFace) { + return parentFace.resolve(AbsFace.POS_Z).relativize(up); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/world/context/impl/TransformingServerContext.java b/src/main/java/ru/windcorp/progressia/server/world/context/impl/TransformingServerContext.java new file mode 100644 index 0000000..7305876 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/world/context/impl/TransformingServerContext.java @@ -0,0 +1,273 @@ +/* + * Progressia + * Copyright (C) 2020-2021 Wind Corporation and contributors + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package ru.windcorp.progressia.server.world.context.impl; + +import java.util.ArrayList; +import java.util.List; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.world.block.BlockData; +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.tile.TileData; +import ru.windcorp.progressia.server.world.context.ServerBlockContext; +import ru.windcorp.progressia.server.world.context.ServerTileContext; +import ru.windcorp.progressia.server.world.context.ServerTileStackContext; + +public abstract class TransformingServerContext extends FilterServerContext { + + private final Vec3i location = new Vec3i(); + private boolean isLocationValid = false; + + private RelFace face; + + private final List vectorCache = new ArrayList<>(1); + + public TransformingServerContext(ServerTileContext parent) { + super(parent); + } + + protected abstract void transform(Vec3i userLocation, Vec3i output); + + protected abstract void untransform(Vec3i parentLocation, Vec3i output); + + protected abstract RelFace transform(RelFace userFace); + + protected abstract RelFace untransform(RelFace parentFace); + + protected void invalidateCache() { + isLocationValid = false; + face = null; + } + + private Vec3i grabVector(Vec3i userVector) { + Vec3i parentVector; + + if (vectorCache.isEmpty()) { + parentVector = new Vec3i(); + } else { + parentVector = vectorCache.remove(vectorCache.size() - 1); + } + + transform(userVector, parentVector); + + return parentVector; + } + + private void releaseVector(Vec3i parentVector) { + vectorCache.add(parentVector); + } + + @Override + public Vec3i getLocation() { + // Always invoke parent method to allow parent to determine validity + Vec3i parentLocation = super.getLocation(); + + if (!isLocationValid) { + untransform(parentLocation, location); + isLocationValid = true; + } + + return location; + } + + @Override + public RelFace getFace() { + // Always invoke parent method to allow parent to determine validity + RelFace parentFace = super.getFace(); + + if (face == null) { + face = untransform(parentFace); + } + + return face; + } + + @Override + public void pop() { + super.pop(); + invalidateCache(); + } + + @Override + public ServerBlockContext push(Vec3i userLocation) { + transform(userLocation, location); + isLocationValid = true; + super.push(location); + face = null; + return this; + } + + @Override + public ServerTileStackContext push(Vec3i userLocation, RelFace userFace) { + transform(userLocation, location); + isLocationValid = true; + face = transform(userFace); + super.push(location, face); + return this; + } + + @Override + public ServerTileContext push(Vec3i userLocation, RelFace userFace, int layer) { + transform(userLocation, location); + isLocationValid = true; + face = transform(userFace); + super.push(location, face, layer); + return this; + } + + @Override + public Vec3i toAbsolute(Vec3i contextLocation, Vec3i output) { + if (output == null) { + output = new Vec3i(); + } + + transform(contextLocation, output); + + return super.toAbsolute(output, output); + } + + @Override + public Vec3i toContext(Vec3i absoluteLocation, Vec3i output) { + output = super.toContext(absoluteLocation, output); + untransform(output, output); + return output; + } + + @Override + public AbsFace toAbsolute(BlockFace contextFace) { + return super.toAbsolute(transform(contextFace.relativize(AbsFace.POS_Z))); + } + + @Override + public RelFace toContext(AbsFace absoluteFace) { + return untransform(super.toContext(absoluteFace)); + } + + @Override + public boolean isLocationLoaded(Vec3i userLocation) { + Vec3i parentLocation = grabVector(userLocation); + + try { + return super.isLocationLoaded(parentLocation); + } finally { + releaseVector(parentLocation); + } + } + + @Override + public BlockData getBlock(Vec3i userLocation) { + Vec3i parentLocation = grabVector(userLocation); + + try { + return super.getBlock(parentLocation); + } finally { + releaseVector(parentLocation); + } + } + + @Override + public void setBlock(Vec3i userLocation, BlockData block) { + Vec3i parentLocation = grabVector(userLocation); + + try { + super.setBlock(parentLocation, block); + } finally { + releaseVector(parentLocation); + } + } + + @Override + public boolean hasTile(Vec3i userLocation, BlockFace userFace, int layer) { + Vec3i parentLocation = grabVector(userLocation); + + try { + return super.hasTile(parentLocation, transform(userFace.relativize(AbsFace.POS_Z)), layer); + } finally { + releaseVector(parentLocation); + } + } + + @Override + public TileData getTile(Vec3i userLocation, BlockFace userFace, int layer) { + Vec3i parentLocation = grabVector(userLocation); + + try { + return super.getTile(parentLocation, transform(userFace.relativize(AbsFace.POS_Z)), layer); + } finally { + releaseVector(parentLocation); + } + } + + @Override + public boolean isTagValid(Vec3i userLocation, BlockFace userFace, int tag) { + Vec3i parentLocation = grabVector(userLocation); + + try { + return super.isTagValid(parentLocation, transform(userFace.relativize(AbsFace.POS_Z)), tag); + } finally { + releaseVector(parentLocation); + } + } + + @Override + public TileData getTileByTag(Vec3i userLocation, BlockFace userFace, int tag) { + Vec3i parentLocation = grabVector(userLocation); + + try { + return super.getTileByTag(parentLocation, transform(userFace.relativize(AbsFace.POS_Z)), tag); + } finally { + releaseVector(parentLocation); + } + } + + @Override + public int getTileCount(Vec3i userLocation, BlockFace userFace) { + Vec3i parentLocation = grabVector(userLocation); + + try { + return super.getTileCount(parentLocation, transform(userFace.relativize(AbsFace.POS_Z))); + } finally { + releaseVector(parentLocation); + } + } + + @Override + public void addTile(Vec3i userLocation, BlockFace userFace, TileData tile) { + Vec3i parentLocation = grabVector(userLocation); + + try { + super.addTile(parentLocation, transform(userFace.relativize(AbsFace.POS_Z)), tile); + } finally { + releaseVector(parentLocation); + } + } + + @Override + public void removeTile(Vec3i userLocation, BlockFace userFace, int tag) { + Vec3i parentLocation = grabVector(userLocation); + + try { + super.removeTile(parentLocation, transform(userFace.relativize(AbsFace.POS_Z)), tag); + } finally { + releaseVector(parentLocation); + } + } + +} diff --git a/src/main/java/ru/windcorp/progressia/server/world/tasks/TickChunk.java b/src/main/java/ru/windcorp/progressia/server/world/tasks/TickChunk.java index 4ed21f1..a2a59f9 100644 --- a/src/main/java/ru/windcorp/progressia/server/world/tasks/TickChunk.java +++ b/src/main/java/ru/windcorp/progressia/server/world/tasks/TickChunk.java @@ -27,17 +27,15 @@ import com.google.common.collect.ImmutableList; import glm.vec._3.i.Vec3i; 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.rels.AbsFace; 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.world.DefaultChunkLogic; import ru.windcorp.progressia.server.world.block.BlockLogic; import ru.windcorp.progressia.server.world.block.TickableBlock; import ru.windcorp.progressia.server.world.context.ServerBlockContext; +import ru.windcorp.progressia.server.world.context.ServerContexts; import ru.windcorp.progressia.server.world.context.ServerTileContext; import ru.windcorp.progressia.server.world.context.ServerTileStackContext; import ru.windcorp.progressia.server.world.context.ServerWorldContext; @@ -53,20 +51,21 @@ public class TickChunk extends Evaluation { DefaultChunkData.BLOCKS_PER_CHUNK * DefaultChunkData.BLOCKS_PER_CHUNK; - private final List> randomTickMethods; + private final List> randomTickMethods; { - List> randomTickMethods = new ArrayList<>(); + List> randomTickMethods = new ArrayList<>(); randomTickMethods.add(this::tickRandomBlock); for (AbsFace face : AbsFace.getFaces()) { - randomTickMethods.add(s -> this.tickRandomTile(s, face)); + randomTickMethods.add(context -> this.tickRandomTile(face, context)); } this.randomTickMethods = ImmutableList.copyOf(randomTickMethods); } private final DefaultChunkLogic chunk; + private ServerWorldContext context; public TickChunk(DefaultChunkLogic chunk) { this.chunk = chunk; @@ -74,44 +73,41 @@ public class TickChunk extends Evaluation { @Override public void evaluate(Server server) { - tickRegulars(server); - tickRandom(server); + if (context == null || context.getServer() != server) { + context = server.createContext(chunk.getUp()); + } + + tickRegulars(context); + tickRandom(context); } - private void tickRegulars(Server server) { - tickRegularBlocks(server); - tickRegularTiles(server); + private void tickRegulars(ServerWorldContext context) { + tickRegularBlocks(context); + tickRegularTiles(context); } - private void tickRegularBlocks(Server server) { + private void tickRegularBlocks(ServerWorldContext context) { if (!chunk.hasTickingBlocks()) return; - ServerWorldContext context = server.createContext(); - chunk.forEachTickingBlock((blockInChunk, block) -> { - ((TickableBlock) block).tick(contextPushBiC(context, chunk, blockInChunk)); + ((TickableBlock) block).tick(ServerContexts.pushAbs(context, chunk, blockInChunk)); context.pop(); }); } - private void tickRegularTiles(Server server) { + private void tickRegularTiles(ServerWorldContext context) { if (!chunk.hasTickingTiles()) return; - ServerWorldContext context = server.createContext(); - Vec3i blockInWorld = new Vec3i(); - chunk.forEachTickingTile((ref, tile) -> { - ((TickableTile) tile).tick( - context.push(ref.getStack().getBlockInWorld(blockInWorld), ref.getStack().getFace(), ref.getIndex()) - ); + ((TickableTile) tile).tick(ServerContexts.pushAbs(context, chunk.getUp(), ref)); context.pop(); }); } - private void tickRandom(Server server) { - float ticks = computeRandomTicks(server); + private void tickRandom(ServerWorldContext context) { + float ticks = computeRandomTicks(context.getServer()); /* * If we are expected to run 3.25 random ticks per tick @@ -122,23 +118,23 @@ public class TickChunk extends Evaluation { float extraTickChance = ticks - unconditionalTicks; for (int i = 0; i < unconditionalTicks; ++i) { - tickRandomOnce(server); + tickRandomOnce(context); } - if (server.getAdHocRandom().nextFloat() < extraTickChance) { - tickRandomOnce(server); + if (context.getRandom().nextFloat() < extraTickChance) { + tickRandomOnce(context); } } - private void tickRandomOnce(Server server) { + private void tickRandomOnce(ServerWorldContext context) { // Pick a target at random: a block or one of 3 primary block faces randomTickMethods.get( - server.getAdHocRandom().nextInt(randomTickMethods.size()) - ).accept(server); + context.getRandom().nextInt(randomTickMethods.size()) + ).accept(context); } - private void tickRandomBlock(Server server) { - Random random = server.getAdHocRandom(); + private void tickRandomBlock(ServerWorldContext context) { + Random random = context.getRandom(); Vec3i blockInChunk = new Vec3i( random.nextInt(BLOCKS_PER_CHUNK), @@ -152,15 +148,17 @@ public class TickChunk extends Evaluation { return; TickableBlock tickable = (TickableBlock) block; - ServerBlockContext context = contextPushBiC(server.createContext(), chunk, blockInChunk); + ServerBlockContext blockContext = ServerContexts.pushAbs(context, chunk, blockInChunk); - if (tickable.getTickingPolicy(context) != TickingPolicy.RANDOM) + if (tickable.getTickingPolicy(blockContext) != TickingPolicy.RANDOM) return; - tickable.tick(context); + tickable.tick(blockContext); + + blockContext.pop(); } - private void tickRandomTile(Server server, AbsFace face) { - Random random = server.getAdHocRandom(); + private void tickRandomTile(AbsFace face, ServerWorldContext context) { + Random random = context.getRandom(); Vec3i blockInChunk = new Vec3i( random.nextInt(BLOCKS_PER_CHUNK), @@ -172,10 +170,10 @@ public class TickChunk extends Evaluation { if (tiles == null || tiles.isEmpty()) return; - ServerTileStackContext context = contextPushBiC(server.createContext(), chunk, blockInChunk).push(face.relativize(chunk.getUp())); + ServerTileStackContext tsContext = ServerContexts.pushAbs(context, chunk, blockInChunk, face); for (int i = 0; i < tiles.size(); ++i) { - ServerTileContext tileContext = context.push(i); + ServerTileContext tileContext = tsContext.push(i); TileLogic logic = tileContext.logic().getTile(); if (!(logic instanceof TickableTile)) { @@ -192,6 +190,8 @@ public class TickChunk extends Evaluation { tileContext.pop(); } + + tsContext.pop(); } private float computeRandomTicks(Server server) { @@ -200,18 +200,6 @@ public class TickChunk extends Evaluation { 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 public void getRelevantChunk(Vec3i output) { Vec3i p = chunk.getData().getPosition(); diff --git a/src/main/java/ru/windcorp/progressia/test/TestContent.java b/src/main/java/ru/windcorp/progressia/test/TestContent.java index 0ddc3e5..7b9122f 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestContent.java +++ b/src/main/java/ru/windcorp/progressia/test/TestContent.java @@ -383,7 +383,7 @@ public class TestContent { ru.windcorp.progressia.server.comms.Client client ) { Vec3i blockInWorld = ((ControlBreakBlockData) packet.getControl()).getBlockInWorld(); - server.createContext().setBlock(blockInWorld, BlockDataRegistry.getInstance().get("Test:Air")); + server.createAbsoluteContext().setBlock(blockInWorld, BlockDataRegistry.getInstance().get("Test:Air")); } private static void onBlockPlaceTrigger(ControlData control) { @@ -403,7 +403,7 @@ public class TestContent { Vec3i blockInWorld = controlData.getBlockInWorld(); if (server.getWorld().getData().getChunkByBlock(blockInWorld) == null) return; - server.createContext().setBlock(blockInWorld, block); + server.createAbsoluteContext().setBlock(blockInWorld, block); } private static void onTilePlaceTrigger(ControlData control) { @@ -428,7 +428,7 @@ public class TestContent { return; if (server.getWorld().getData().getTiles(blockInWorld, face).isFull()) return; - server.createContext().addTile(blockInWorld, face, tile); + server.createAbsoluteContext().addTile(blockInWorld, face, tile); } private static void registerMisc() { diff --git a/src/main/java/ru/windcorp/progressia/test/TestTileLogicGrass.java b/src/main/java/ru/windcorp/progressia/test/TestTileLogicGrass.java index bfe438f..c197ce1 100644 --- a/src/main/java/ru/windcorp/progressia/test/TestTileLogicGrass.java +++ b/src/main/java/ru/windcorp/progressia/test/TestTileLogicGrass.java @@ -18,6 +18,7 @@ package ru.windcorp.progressia.test; +import ru.windcorp.progressia.common.world.rels.AbsFace; import ru.windcorp.progressia.common.world.rels.RelFace; import ru.windcorp.progressia.server.world.block.BlockLogic; import ru.windcorp.progressia.server.world.context.ServerTileContext; @@ -65,7 +66,7 @@ public class TestTileLogicGrass extends HangingTileLogic implements TickableTile private boolean isBlockAboveTransparent(ServerTileContextRO context) { // TODO rework - context.pushRelative(RelFace.UP.resolve(context.getServer().getWorld().getUp(context.getLocation()))); + context.pushRelative(RelFace.UP.resolve(AbsFace.POS_Z)); BlockLogic block = context.logic().getBlock(); return context.popAndReturn(block == null || block.isTransparent(context)); }