From d7afe39f00a7e50242ed3376b7e348bd02027232 Mon Sep 17 00:00:00 2001 From: OLEGSHA Date: Thu, 15 Jul 2021 22:26:20 +0300 Subject: [PATCH] Added more generic Contexts. WIP. --- .../common/world/context/Context.java | 105 ++++++++++++ .../context/GenericROBlockContext.java | 119 ++++++++++++++ .../context/GenericROBlockFaceContext.java | 93 +++++++++++ .../generic/context/GenericROTileContext.java | 85 ++++++++++ .../context/GenericROWorldContext.java | 45 ++++++ .../context/GenericRWBlockContext.java | 92 +++++++++++ .../context/GenericRWBlockFaceContext.java | 78 +++++++++ .../generic/context/GenericRWTileContext.java | 50 ++++++ .../context/GenericRWWorldContext.java | 151 ++++++++++++++++++ .../server/world/context/ServerContext.java | 84 ++++++++++ 10 files changed, 902 insertions(+) create mode 100644 src/main/java/ru/windcorp/progressia/common/world/context/Context.java create mode 100644 src/main/java/ru/windcorp/progressia/common/world/generic/context/GenericROBlockContext.java create mode 100644 src/main/java/ru/windcorp/progressia/common/world/generic/context/GenericROBlockFaceContext.java create mode 100644 src/main/java/ru/windcorp/progressia/common/world/generic/context/GenericROTileContext.java create mode 100644 src/main/java/ru/windcorp/progressia/common/world/generic/context/GenericROWorldContext.java create mode 100644 src/main/java/ru/windcorp/progressia/common/world/generic/context/GenericRWBlockContext.java create mode 100644 src/main/java/ru/windcorp/progressia/common/world/generic/context/GenericRWBlockFaceContext.java create mode 100644 src/main/java/ru/windcorp/progressia/common/world/generic/context/GenericRWTileContext.java create mode 100644 src/main/java/ru/windcorp/progressia/common/world/generic/context/GenericRWWorldContext.java create mode 100644 src/main/java/ru/windcorp/progressia/server/world/context/ServerContext.java diff --git a/src/main/java/ru/windcorp/progressia/common/world/context/Context.java b/src/main/java/ru/windcorp/progressia/common/world/context/Context.java new file mode 100644 index 0000000..bfbd164 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/context/Context.java @@ -0,0 +1,105 @@ +/* + * 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.common.world.context; + +/** + * A cursor-like object for retrieving information about an in-game environment. + * A context object typically holds a reference to some sort of data structure + * and a cursor pointing to a location in that data structure. The exact meaning + * of "environment" and "location" is defined by extending interfaces. + *

+ * Context objects are intended to be the primary way of interacting for in-game + * content. Wherever possible, context objects should be preferred over other + * means of accessing game structures. + *

Context Validity

+ * Context objects may only be used while they are valid to avoid undefined + * behavior. There exists no programmatic way to determine a context's validity; + * it is the responsibility of the programmer to avoid interacting with invalid + * contexts. + *

+ * Contexts are usually acquired as method parameters. Unless stated otherwise, + * the context is valid until the invoked method returns; the only exception to + * this rule is subcontexting (see below). Consequently, contexts should never + * be stored outside their intended methods. + *

+ * In practice, context objects are typically highly volatile. They are not + * thread-safe and are often pooled and reused. + *

+ *

Subcontexting

+ * Subcontexting is the invocation of user-provided code with a context + * object derived from an existing one. For example, block context provides a + * convenience method for referencing the block's neighbor: + * + *
+ * blockContextA.forNeighbor(RelFace.UP, blockContextB -> {
+ * 	foo(blockContextA); // undefined behavior!
+ * 	foo(blockContextB); // correct
+ * });
+ * 
+ * + * In this example, {@code forNeighbor} is a subcontexting method, + * {@code blockContextA} is the parent context, {@code blockContextB} is the + * subcontext, and the lambda is the context consumer. + *

+ * Parent contexts are invalid while the subcontexting method is + * running. Referencing {@code blockContextA} from inside the lambda + * creates undefined behavior. + *

+ * This restriction exists because some implementations of contexts may + * implement subcontexting by simply modifying the parent context for the + * duration of the call and presenting the temporarily modified parent context + * as the subcontext: + * + *

+ * public void forNeighbor(BlockFace face, Consumer<BlockContext> action) {
+ * 	this.position.add(face);
+ * 	action.accept(this);
+ * 	this.position.sub(face);
+ * }
+ * 
+ */ +public interface Context { + + /** + * Tests whether the environment is "real". Any actions carried out in an + * environment that is not "real" should not have any side effects outside + * of the environment. + *

+ * A typical "real" environment is the world of the client that is actually + * displayed or a world of the server that the clients actually interact + * with. An example of a non-"real" environment is a fake world used by + * developer tools to query the properties or behaviors of in-game content. + * While in-game events may well trigger global-scope actions, such as + * writing files, this may become an unintended or even harmful byproduct in + * some scenarios that are not actually linked to an actual in-game world. + *

+ * This flag should generally only be consulted before taking action through + * means other than a provided changer object. The interactions with the + * context should otherwise remain unaltered. + *

+ * When querying game content for purposes other than directly applying + * results in-game, {@code isReal()} should return {@code false}. In all + * other cases, where possible, the call should be delegated to a provided + * context object. + * + * @return {@code false} iff side effects outside the environment should be + * suppressed + */ + boolean isReal(); + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/generic/context/GenericROBlockContext.java b/src/main/java/ru/windcorp/progressia/common/world/generic/context/GenericROBlockContext.java new file mode 100644 index 0000000..4411407 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/generic/context/GenericROBlockContext.java @@ -0,0 +1,119 @@ +/* + * 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.common.world.generic.context; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.world.context.Context; +import ru.windcorp.progressia.common.world.generic.GenericBlock; +import ru.windcorp.progressia.common.world.generic.GenericROChunk; +import ru.windcorp.progressia.common.world.generic.GenericEntity; +import ru.windcorp.progressia.common.world.generic.GenericTile; +import ru.windcorp.progressia.common.world.generic.GenericROTileReference; +import ru.windcorp.progressia.common.world.generic.GenericROTileStack; +import ru.windcorp.progressia.common.world.rels.BlockFace; + +/** + * A {@link Context} referencing a world with a block location specified. The + * location may or may not be loaded. + */ +//@formatter:off +public interface GenericROBlockContext< + B extends GenericBlock, + T extends GenericTile, + TS extends GenericROTileStack , + TR extends GenericROTileReference , + C extends GenericROChunk , + E extends GenericEntity +> extends GenericROWorldContext { +//@formatter:on + + /** + * Returns the location of the block. + *

+ * The coordinate system in use is not specified, but it is consistent + * across all methods of this context. + *

+ * The object returned by this method must not be modified. It is only valid + * while the context is {@linkplain valid}. + * + * @return a vector describing the block's position + */ + Vec3i getLocation(); + + /** + * Determines whether the location relevant to this context is currently + * loaded. + * + * @return {@code true} iff the location is loaded + */ + default boolean isLoaded() { + return isBlockLoaded(getLocation()); + } + + /** + * Gets the block relevant in this context. + * + * @return the block or {@code null} if the location is not loaded + */ + default B getBlock() { + return getBlock(getLocation()); + } + + /** + * Gets the tile stack at the specified position; block location is implied + * by the context. + * + * @return the specified tile stack or {@code null} if the location is not + * loaded or the tile stack does not exist + */ + default TS getTilesOrNull(BlockFace face) { + return getTilesOrNull(getLocation(), face); + } + + /** + * Determines whether the location relevant to this context has a tile stack + * at the specified side. + * + * @return {@code true} iff the tile stack exists + */ + default boolean hasTiles(BlockFace face) { + return hasTiles(getLocation(), face); + } + + /** + * Determines whether the specified position has a tile; block location is + * implied by the context. + * + * @return {@code true} iff the tile exists + */ + default boolean hasTile(BlockFace face, int layer) { + return hasTile(getLocation(), face, layer); + } + + /** + * Gets the tile at the specified position; block location is implied by the + * context. + * + * @return the specified tile or {@code null} if the location is not loaded + * or the tile does not exist + */ + default T getTile(BlockFace face, int layer) { + return getTile(getLocation(), face, layer); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/generic/context/GenericROBlockFaceContext.java b/src/main/java/ru/windcorp/progressia/common/world/generic/context/GenericROBlockFaceContext.java new file mode 100644 index 0000000..430a4a3 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/generic/context/GenericROBlockFaceContext.java @@ -0,0 +1,93 @@ +/* + * 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.common.world.generic.context; + +import ru.windcorp.progressia.common.world.context.Context; +import ru.windcorp.progressia.common.world.generic.GenericBlock; +import ru.windcorp.progressia.common.world.generic.GenericROChunk; +import ru.windcorp.progressia.common.world.generic.GenericEntity; +import ru.windcorp.progressia.common.world.generic.GenericTile; +import ru.windcorp.progressia.common.world.generic.GenericROTileReference; +import ru.windcorp.progressia.common.world.generic.GenericROTileStack; +import ru.windcorp.progressia.common.world.rels.RelFace; + +/** + * A {@link Context} referencing a world with a block location and a block face + * specified, effectively pointing to a tile stack. The tile stack may or may + * not actually exist. + */ +//@formatter:off +public interface GenericROBlockFaceContext< + B extends GenericBlock, + T extends GenericTile, + TS extends GenericROTileStack , + TR extends GenericROTileReference , + C extends GenericROChunk , + E extends GenericEntity +> extends GenericROBlockContext { +//@formatter:on + + /** + * Returns the face relevant to this context. + * + * @return the block face + */ + RelFace getFace(); + + /** + * Gets the tile stack at the relevant position. + * + * @return the specified tile stack or {@code null} if the location is not + * loaded or the tile stack does not exist + */ + default TS getTilesOrNull() { + return getTilesOrNull(getLocation(), getFace()); + } + + /** + * Determines whether the location relevant to this context has a tile + * stack. + * + * @return {@code true} iff the tile stack exists + */ + default boolean hasTiles() { + return hasTiles(getLocation(), getFace()); + } + + /** + * Determines whether the specified position has a tile; block location and + * face are implied by the context. + * + * @return {@code true} iff the tile exists + */ + default boolean hasTile(int layer) { + return hasTile(getLocation(), getFace(), layer); + } + + /** + * Gets the tile at the specified position; block location and face are + * implied by the context. + * + * @return the specified tile or {@code null} if the location is not loaded + * or the tile does not exist + */ + default T getTile(int layer) { + return getTile(getLocation(), getFace(), layer); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/generic/context/GenericROTileContext.java b/src/main/java/ru/windcorp/progressia/common/world/generic/context/GenericROTileContext.java new file mode 100644 index 0000000..0ebc768 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/generic/context/GenericROTileContext.java @@ -0,0 +1,85 @@ +/* + * 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.common.world.generic.context; + +import ru.windcorp.progressia.common.world.context.Context; +import ru.windcorp.progressia.common.world.generic.GenericBlock; +import ru.windcorp.progressia.common.world.generic.GenericROChunk; +import ru.windcorp.progressia.common.world.generic.GenericEntity; +import ru.windcorp.progressia.common.world.generic.GenericTile; +import ru.windcorp.progressia.common.world.generic.GenericROTileReference; +import ru.windcorp.progressia.common.world.generic.GenericROTileStack; + +/** + * A {@link Context} referencing a world with a block location, a block face and + * a tile layer specified, effectively pointing to a single tile. The tile may + * or may not actually exist. + */ +//@formatter:off +public interface GenericROTileContext< + B extends GenericBlock, + T extends GenericTile, + TS extends GenericROTileStack , + TR extends GenericROTileReference , + C extends GenericROChunk , + E extends GenericEntity +> extends GenericROBlockFaceContext { +//@formatter:on + + /** + * Returns the tile layer relevant to this context. + * + * @return the tile layer + */ + int getLayer(); + + /** + * Determines whether the location relevant to this context has a tile. + * + * @return {@code true} iff the tile exists + */ + default boolean hasTile() { + return hasTile(getLocation(), getFace(), getLayer()); + } + + /** + * Gets the tile at the relevant position. + * + * @return the specified tile or {@code null} if the location is not loaded + * or the tile does not exist + */ + default T getTile() { + return getTile(getLocation(), getFace(), getLayer()); + } + + /** + * Gets the tag of the tile at the relevant position. + * + * @return the tag of the tile or {@code -1} if the location is not loaded + * or the tile does not exist + */ + default int getTag() { + TS tileStack = getTilesOrNull(); + if (tileStack == null) { + return -1; + } + + return tileStack.getTagByIndex(getLayer()); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/generic/context/GenericROWorldContext.java b/src/main/java/ru/windcorp/progressia/common/world/generic/context/GenericROWorldContext.java new file mode 100644 index 0000000..09629da --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/generic/context/GenericROWorldContext.java @@ -0,0 +1,45 @@ +/* + * 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.common.world.generic.context; + +import ru.windcorp.progressia.common.world.context.Context; +import ru.windcorp.progressia.common.world.generic.GenericBlock; +import ru.windcorp.progressia.common.world.generic.GenericROChunk; +import ru.windcorp.progressia.common.world.generic.GenericEntity; +import ru.windcorp.progressia.common.world.generic.GenericTile; +import ru.windcorp.progressia.common.world.generic.GenericROTileReference; +import ru.windcorp.progressia.common.world.generic.GenericROTileStack; +import ru.windcorp.progressia.common.world.generic.GenericROWorld; + +/** + * A {@link Context} with a world instance. + */ +// @formatter:off +public interface GenericROWorldContext< + B extends GenericBlock, + T extends GenericTile, + TS extends GenericROTileStack , + TR extends GenericROTileReference , + C extends GenericROChunk , + E extends GenericEntity +> extends Context, GenericROWorld { +// @formatter:on + + // currently empty + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/generic/context/GenericRWBlockContext.java b/src/main/java/ru/windcorp/progressia/common/world/generic/context/GenericRWBlockContext.java new file mode 100644 index 0000000..23fb6fe --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/generic/context/GenericRWBlockContext.java @@ -0,0 +1,92 @@ +/* + * 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.common.world.generic.context; + +import ru.windcorp.progressia.common.world.context.Context; +import ru.windcorp.progressia.common.world.generic.*; +import ru.windcorp.progressia.common.world.rels.BlockFace; + +/** + * A writable {@link Context} referencing a world with a block location + * specified. This context provides methods for affecting the world. The + * application of requested changes may or may not be immediate, see + * {@link #isImmediate()}. The location may or may not be loaded. + */ +//@formatter:off +public interface GenericRWBlockContext< + B extends GenericBlock, + T extends GenericTile, + TS extends GenericRWTileStack , + TR extends GenericROTileReference , + C extends GenericRWChunk , + E extends GenericEntity +> extends GenericRWWorldContext, GenericROBlockContext { +//@formatter:on + + /** + * Requests that a block is changed. The object provided may be stored until + * the change is applied. The location of the block is implied by the + * context. + * + * @param block the new block + * @see #isImmediate() + */ + default void setBlock(B block) { + setBlock(getLocation(), block); + } + + /** + * Requests that a tile is added to the top of the tile stack at the given + * location. The object provided may be stored until the change is applied. + * If the tile could not be added at the time of application this method + * fails silently. The location of the block is implied by the context. + * + * @param face the face of the block to add the tile to + * @param tile the tile to add + */ + default void addTile(BlockFace face, T tile) { + addTile(getLocation(), face, tile); + } + + /** + * Requests that a tile identified by its tag is removed from the specified + * tile stack. If the tile could not be found at the time of application + * this method fails silently. The location of the block is implied by the + * context. + * + * @param face the of the block to remove the tile from + * @param tag the tag of the tile to remove + */ + default void removeTile(BlockFace face, int tag) { + removeTile(getLocation(), face, tag); + } + + /** + * Requests that the referenced tile is removed from the specified tile + * stack. If the tile could not be found at the time of application this + * method fails silently. The location of the block is implied by the + * context. + * + * @param face the of the block to remove the tile from + * @param tileReference a reference to the tile + */ + default void removeTile(BlockFace face, TR tileReference) { + removeTile(getLocation(), face, tileReference.getTag()); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/generic/context/GenericRWBlockFaceContext.java b/src/main/java/ru/windcorp/progressia/common/world/generic/context/GenericRWBlockFaceContext.java new file mode 100644 index 0000000..b2152f8 --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/generic/context/GenericRWBlockFaceContext.java @@ -0,0 +1,78 @@ +/* + * 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.common.world.generic.context; + +import ru.windcorp.progressia.common.world.context.Context; +import ru.windcorp.progressia.common.world.generic.*; + +/** + * A writable {@link Context} referencing a world with a block location and a + * block face specified, effectively pointing to a tile stack. This context + * provides methods for affecting the world. The application of requested + * changes may or may not be immediate, see {@link #isImmediate()}. The tile + * stack may or may not actually exist. + */ +//@formatter:off +public interface GenericRWBlockFaceContext< + B extends GenericBlock, + T extends GenericTile, + TS extends GenericRWTileStack , + TR extends GenericROTileReference , + C extends GenericRWChunk , + E extends GenericEntity +> extends GenericRWBlockContext, GenericROBlockFaceContext { +//@formatter:on + + /** + * Requests that a tile is added to the top of the tile stack at the given + * location. The object provided may be stored until the change is applied. + * If the tile could not be added at the time of application this method + * fails silently. The location and the face of the block are implied by the + * context. + * + * @param tile the tile to add + */ + default void addTile(T tile) { + addTile(getLocation(), getFace(), tile); + } + + /** + * Requests that a tile identified by its tag is removed from the specified + * tile stack. If the tile could not be found at the time of application + * this method fails silently. The location and the face of the block are + * implied by the context. + * + * @param tag the tag of the tile to remove + */ + default void removeTile(int tag) { + removeTile(getLocation(), getFace(), tag); + } + + /** + * Requests that the referenced tile is removed from the specified tile + * stack. If the tile could not be found at the time of application this + * method fails silently. The location and the face of the block are implied + * by the context. + * + * @param tileReference a reference to the tile + */ + default void removeTile(TR tileReference) { + removeTile(getLocation(), getFace(), tileReference.getTag()); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/generic/context/GenericRWTileContext.java b/src/main/java/ru/windcorp/progressia/common/world/generic/context/GenericRWTileContext.java new file mode 100644 index 0000000..c7c0d5c --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/generic/context/GenericRWTileContext.java @@ -0,0 +1,50 @@ +/* + * 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.common.world.generic.context; + +import ru.windcorp.progressia.common.world.context.Context; +import ru.windcorp.progressia.common.world.generic.*; + +/** + * A writable {@link Context} referencing a world with a block location, a block + * face and a tile layer specified, effectively pointing to a single tile. This + * context provides methods for affecting the world. The application of + * requested changes may or may not be immediate, see {@link #isImmediate()}. + * The tile may or may not actually exist. + */ +//@formatter:off +public interface GenericRWTileContext< + B extends GenericBlock, + T extends GenericTile, + TS extends GenericRWTileStack , + TR extends GenericROTileReference , + C extends GenericRWChunk , + E extends GenericEntity +> extends GenericRWBlockFaceContext, GenericROTileContext { +//@formatter:on + + /** + * Requests that the tile relevant to this context be removed from its tile + * stack. If the tile could not be found at the time of application this + * method fails silently. + */ + default void removeTile() { + removeTile(getLocation(), getFace(), getTag()); + } + +} diff --git a/src/main/java/ru/windcorp/progressia/common/world/generic/context/GenericRWWorldContext.java b/src/main/java/ru/windcorp/progressia/common/world/generic/context/GenericRWWorldContext.java new file mode 100644 index 0000000..55d83ff --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/common/world/generic/context/GenericRWWorldContext.java @@ -0,0 +1,151 @@ +/* + * 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.common.world.generic.context; + +import glm.vec._3.i.Vec3i; +import ru.windcorp.progressia.common.state.StateChange; +import ru.windcorp.progressia.common.state.StatefulObject; +import ru.windcorp.progressia.common.world.context.Context; +import ru.windcorp.progressia.common.world.generic.*; +import ru.windcorp.progressia.common.world.rels.BlockFace; + +/** + * A writable {@link Context} with a world instance. This context provides + * methods for affecting the world. The application of requested changes may or + * may not be immediate, see {@link #isImmediate()}. + */ +// @formatter:off +public interface GenericRWWorldContext< + B extends GenericBlock, + T extends GenericTile, + TS extends GenericRWTileStack , + TR extends GenericROTileReference , + C extends GenericRWChunk , + E extends GenericEntity +> extends GenericROWorldContext, GenericRWWorld { +// @formatter:on + + /** + * Queries whether changes requested with this context are guaranteed to be + * applied immediately. + *

+ * When the changes are applied immediately, all subsequent queries will + * reflect the change. When the changes are not applied immediately, none of + * the subsequent queries will be affected by the requests while the context + * is {@linkplain Context#validity valid}. Immediate mode does not change + * while the context is valid. + * + * @return {@code true} iff changes are visible immediately + */ + boolean isImmediate(); + + /** + * Requests that a block is changed. The object provided may be stored until + * the change is applied. + * + * @param location the location of the change + * @param block the new block + * @see #isImmediate() + */ + default void setBlock(Vec3i location, B block) { + setBlock(location, block); + } + + /** + * Requests that a tile is added to the top of the tile stack at the given + * location. The object provided may be stored until the change is applied. + * If the tile could not be added at the time of application this method + * fails silently. + * + * @param location the location of the block to which the tile is to be + * added + * @param face the face of the block to add the tile to + * @param tile the tile to add + */ + void addTile(Vec3i location, BlockFace face, T tile); + + /** + * Requests that a tile identified by its tag is removed from the specified + * tile stack. If the tile could not be found at the time of application + * this method fails silently. + * + * @param location the location of the block from which the tile is to be + * removed + * @param face the of the block to remove the tile from + * @param tag the tag of the tile to remove + */ + void removeTile(Vec3i location, BlockFace face, int tag); + + /** + * Requests that the referenced tile is removed from the specified tile + * stack. If the tile could not be found at the time of application this + * method fails silently. + * + * @param location the location of the block from which the tile is to + * be removed + * @param face the of the block to remove the tile from + * @param tileReference a reference to the tile + */ + default void removeTile(Vec3i location, BlockFace face, TR tileReference) { + removeTile(location, face, tileReference.getTag()); + } + + /** + * Requests that an entity is added to the world. The object provided may be + * stored until the change is applied. If the entity was already added to + * the world at the time of application this method does nothing. + * + * @param entity the entity to add + * @see #isImmediate() + */ + @Override + void addEntity(E entity); + + /** + * Requests that an entity with the given entity ID is removed from the + * world. If the entity did not exist at the time of application this method + * fails silently. + * + * @param entityId the ID of the entity to remove + * @see #isImmediate() + * @see #removeEntity(GenericEntity) + */ + @Override + void removeEntity(long entityId); + + /** + * Requests that the entity is removed from the world. If the entity did not + * exist at the time of application this method fails silently. + * + * @param entity the entity to remove + * @see #isImmediate() + * @see #removeEntity(long) + */ + @Override + void removeEntity(E entity); + + /** + * Requests that the specified change is applied to the given entity. The {@code change} object provided may be stored until the change is applied. + * + * @param entity the entity to change + * @param change the change to apply + */ + @Override + void changeEntity(SE entity, StateChange change); + +} diff --git a/src/main/java/ru/windcorp/progressia/server/world/context/ServerContext.java b/src/main/java/ru/windcorp/progressia/server/world/context/ServerContext.java new file mode 100644 index 0000000..bb218ab --- /dev/null +++ b/src/main/java/ru/windcorp/progressia/server/world/context/ServerContext.java @@ -0,0 +1,84 @@ +/* + * 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 java.util.Random; + +import ru.windcorp.progressia.common.world.context.Context; +import ru.windcorp.progressia.server.Server; +import ru.windcorp.progressia.server.ServerState; + +/** + * A server-side {@link Context}. This context has a {@link Server} instance. + */ +public interface ServerContext extends Context { + + /** + * Gets the {@link Server} instance relevant to this context. This method + * should always be preferred to {@link ServerState#getInstance()} when + * possible. + * + * @return the server instance + */ + Server getServer(); + + /** + * Retrieves a context-appropriate source of randomness. This source should + * always be preferred to any other when possible. + * + * @return an intended {@link Random} instance + */ + Random getRandom(); + + /** + * Returns the duration of the last server tick. Server logic should assume + * that this much in-world time has passed. + * + * @return the length of the last server tick + */ + default double getTickLength() { + return getServer().getTickLength(); + } + + /** + * Adjusts the provided value according to tick length assuming the value + * scales linearly. The call {@code ctxt.adjustValue(x)} is equivalent to + * {@code ((float) ctxt.getTickLength()) * x}. + * + * @param valueForOneSecond the value to adjust, normalized to one second + * @return the value adjust to account for the actual tick length + * @see #getTickLength() + */ + default float adjustTime(float valueForOneSecond) { + return ((float) getTickLength()) * valueForOneSecond; + } + + /** + * Adjusts the provided value according to tick length assuming the value + * scales linearly. The call {@code ctxt.adjustValue(x)} is equivalent to + * {@code ctxt.getTickLength() * x}. + * + * @param valueForOneSecond the value to adjust, normalized to one second + * @return the value adjust to account for the actual tick length + * @see #getTickLength() + */ + default double adjustTime(double valueForOneSecond) { + return getTickLength() * valueForOneSecond; + } + +}