diff --git a/api/src/main/java/mrtjp/projectred/api/BlockMover.java b/api/src/main/java/mrtjp/projectred/api/BlockMover.java index 8e1620224..a2e8c6e01 100755 --- a/api/src/main/java/mrtjp/projectred/api/BlockMover.java +++ b/api/src/main/java/mrtjp/projectred/api/BlockMover.java @@ -1,14 +1,24 @@ package mrtjp.projectred.api; -import net.minecraft.core.Direction; import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; import net.minecraft.world.level.Level; /** * Interface for an object that manages the movement of blocks and tiles that are * registered to it. This class should be registered through the {@link IExpansionAPI}. + *
+ * The order of calls are: + *
- * Called server-side only when determining what to move. + * Called server-side only. * - * @param w The world. - * @param pos The position of the block to move. + * @param w The world. + * @param pos The position of the block before movement. * @return True if the block at the given position is able to move. */ boolean canMove(Level w, BlockPos pos); /** * Method used to actually move the tile. Called after the animation - * has run. This is where you should tell the tile that it is time - * to move, and peform any extra checks or calls. This should also - * move the block and tile as well. This is called - * on every block in the moving structure sequentially. + * has run. This is where you should tell the tile that it is time + * to move, and perform any extra checks or calls. This should also + * move the block and tile as well. This is called on every block in + * the moving structure sequentially. *
- * Called on both server and client. + * Called on server then client. Note that after server call, block + * positions of client/server are out of sync. Do not send packets + * to client referencing blocks by position, update neighbors, mark + * chunk for re-render, etc. * * @param w The world. - * @param pos The position of the block to move. + * @param pos The position of the block before movement. * @param dir The ForgeDirection the structure is moving in. */ void move(Level w, BlockPos pos, Direction dir); /** - * Called after all blocks in the group have moved to their - * new locations. This is where you would reload your tile, - * tell it to refresh or reacknowledge its new position. + * Called after all blocks in the group have moved to their new locations. + * This is where you would reload your tile, tell it to refresh or + * re-acknowledge its new position. No need to update neighbors, since + * they are batch-notified after all blocks (moved and adjacent to moved) + * get postMove call. *
- * Called on both server and client.
+ * Called on client then server. At this point, both client and server
+ * blocks are in sync at new positions. It is safe to send packets
+ * with position references.
*
- * @param w The world.
- * @param pos The position of the block to move.
+ * @param w The world.
+ * @param pos The position of the block after movement.
*/
void postMove(Level w, BlockPos pos);
}
diff --git a/api/src/main/java/mrtjp/projectred/api/MovementDescriptor.java b/api/src/main/java/mrtjp/projectred/api/MovementDescriptor.java
index 14202b691..94fa72d18 100644
--- a/api/src/main/java/mrtjp/projectred/api/MovementDescriptor.java
+++ b/api/src/main/java/mrtjp/projectred/api/MovementDescriptor.java
@@ -13,21 +13,30 @@ public interface MovementDescriptor {
enum MovementStatus {
/**
- * Failed to begin movement (collision, unmovable block in structure, etc)
+ * Movement pending start. Default state when first created
*/
- FAILED,
+ PENDING_START,
/**
- * Movement in progress
+ * Movement animation in progress while block remains in initial position
*/
MOVING,
/**
- * Movement finished successfully
+ * Movement animation has completed but still awaiting execution of movement.
+ * Block is still in initial position.
+ */
+ PENDING_FINALIZATION,
+ /**
+ * Movement finished successfully and block is in final position
*/
FINISHED,
/**
* Movement cancelled before completion (chunk was unloaded, etc)
*/
CANCELLED,
+ /**
+ * Failed to begin movement (collision, unmovable block in structure, etc)
+ */
+ FAILED,
/**
* Unknown status
*/
diff --git a/core/src/main/java/mrtjp/projectred/core/tile/IConnectableTile.java b/core/src/main/java/mrtjp/projectred/core/tile/IConnectableTile.java
index 28eaef819..bf6e5d72f 100644
--- a/core/src/main/java/mrtjp/projectred/core/tile/IConnectableTile.java
+++ b/core/src/main/java/mrtjp/projectred/core/tile/IConnectableTile.java
@@ -6,9 +6,9 @@
import codechicken.multipart.block.TileMultipart;
import codechicken.multipart.util.PartMap;
import mrtjp.projectred.api.IConnectable;
+import mrtjp.projectred.core.CenterLookup;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
-import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import javax.annotation.Nullable;
@@ -208,9 +208,9 @@ default boolean discoverCorner(int s, int edgeRot) {
}
default boolean discoverStraightCenterOverride(int s) {
- BlockPos pos = posOfStraight(s);
- BlockEntity tile = getBlockLevel().getBlockEntity(pos);
- if (tile instanceof IConnectable connectable) {
+ CenterLookup lookup = CenterLookup.lookupStraightCenter(getBlockLevel(), getBlockPosition(), s);
+
+ if (lookup.tile instanceof IConnectable connectable) {
return canConnectPart(connectable, s, -1) && connectable.connectStraight(this, s, -1);
}
diff --git a/expansion/src/main/java/mrtjp/projectred/expansion/MovementManager.java b/expansion/src/main/java/mrtjp/projectred/expansion/MovementManager.java
index 6a514bf6c..4c82d1335 100644
--- a/expansion/src/main/java/mrtjp/projectred/expansion/MovementManager.java
+++ b/expansion/src/main/java/mrtjp/projectred/expansion/MovementManager.java
@@ -40,6 +40,7 @@
import java.util.*;
import java.util.function.Consumer;
+import static mrtjp.projectred.api.MovementDescriptor.MovementStatus.*;
import static mrtjp.projectred.expansion.ProjectRedExpansion.LOGGER;
public class MovementManager {
@@ -85,16 +86,20 @@ public static void onChunkUnwatchEvent(ChunkWatchEvent.UnWatch event) {
}
public static void onChunkUnloadEvent(ChunkEvent.Unload event) {
+// LOGGER.debug("Chunk {} unloaded", event.getLevel());
if (event.getLevel() instanceof Level level) {
getInstance(level).cancelMovementsInChunk(level, event.getChunk().getPos());
}
}
public static void onLevelUnload(LevelEvent.Unload event) {
- // Note: Client unloads levels as player changes dimensions, but
+ // Note: Client unloads levels as player changes dimensions or leaves server, but
// server appears to always have all dims loaded and unloads only
// on shutdown
LOGGER.debug("Level {} unloaded", event.getLevel());
+ if (event.getLevel() instanceof Level level) {
+ getInstance(level).cancelMovementsOnUnload(level);
+ }
}
public static void onLevelLoad(LevelEvent.Load event) {
@@ -186,6 +191,16 @@ private void removeChunkWatcher(ChunkPos pos, ServerPlayer player) {
if (watchingPlayersSet != null) watchingPlayersSet.remove(pos);
}
+ private void cancelMovementsOnUnload(Level level) {
+ // Note: Call this on both sides
+ LOGGER.debug("Cancelling {} movements on level {} unload", structures.size(), level);
+ for (var structure : structures.values()) {
+ structure.cancelMove(level); // prob doesn't matter at this point
+ }
+ structures.clear();
+ nextStructureId = 0;
+ }
+
private void cancelMovementsInChunk(Level level, ChunkPos pos) {
if (level.isClientSide) {
return;
@@ -220,7 +235,7 @@ private void tick(Level level) {
for (var e : newWatchers.entrySet()) {
ServerPlayer player = e.getKey();
Set