From 32da097c26f90f56bfc6f24aba92dc29642848a6 Mon Sep 17 00:00:00 2001 From: simibubi <31564874+simibubi@users.noreply.github.com> Date: Sun, 15 May 2022 21:19:02 +0200 Subject: [PATCH] Interdimensional Railways - Trackgraphs and trains now properly differentiate between the dimensions they are located in - Tracks placed next to a nether portal will attempt to connect to a second track piece in the nether --- src/generated/resources/.cache/cache | 2 +- .../assets/create/blockstates/track.json | 30 + .../java/com/simibubi/create/AllShapes.java | 1 + .../actors/PloughMovementBehaviour.java | 3 + .../AbstractContraptionEntity.java | 12 +- .../structureMovement/Contraption.java | 18 + .../ContraptionCollider.java | 3 +- .../ContraptionHandlerClient.java | 4 +- .../glue/SuperGlueEntity.java | 7 + .../interaction/controls/ControlsHandler.java | 2 +- .../controls/ControlsMovementBehaviour.java | 6 + .../controls/ControlsServerHandler.java | 15 +- .../render/ActorInstance.java | 2 +- .../render/ContraptionEntityRenderer.java | 2 + .../render/ContraptionInstanceManager.java | 17 +- .../render/ContraptionRenderDispatcher.java | 53 +- .../render/ContraptionRenderInfo.java | 2 +- .../render/FlwContraption.java | 11 +- .../logistics/trains/DimensionPalette.java | 68 ++ .../trains/GlobalRailwayManager.java | 20 +- .../content/logistics/trains/ITrackBlock.java | 35 +- .../logistics/trains/RailwaySavedData.java | 28 +- .../content/logistics/trains/TrackEdge.java | 47 +- .../content/logistics/trains/TrackGraph.java | 35 +- .../logistics/trains/TrackGraphHelper.java | 4 +- .../logistics/trains/TrackGraphSync.java | 5 +- .../trains/TrackGraphSyncPacket.java | 43 +- .../trains/TrackGraphVisualizer.java | 31 +- .../logistics/trains/TrackNodeLocation.java | 63 +- .../logistics/trains/TrackPropagator.java | 6 +- .../trains/entity/BogeyInstance.java | 118 ++- .../logistics/trains/entity/Carriage.java | 743 +++++++++++++++--- .../trains/entity/CarriageBogey.java | 33 +- .../trains/entity/CarriageContraption.java | 78 ++ .../entity/CarriageContraptionEntity.java | 150 +++- .../CarriageContraptionEntityRenderer.java | 44 +- .../entity/CarriageContraptionInstance.java | 47 +- .../entity/CarriageCouplingRenderer.java | 8 +- .../trains/entity/CarriageSyncData.java | 34 +- .../logistics/trains/entity/Navigation.java | 28 +- .../logistics/trains/entity/Train.java | 101 ++- .../trains/entity/TrainMigration.java | 13 +- .../trains/entity/TrainRelocator.java | 11 +- .../logistics/trains/entity/TrainStatus.java | 7 + .../trains/entity/TravellingPoint.java | 86 +- .../trains/management/edgePoint/EdgeData.java | 11 +- .../edgePoint/EdgePointStorage.java | 9 +- .../management/edgePoint/EdgePointType.java | 5 +- .../edgePoint/TrackEdgeIntersection.java | 11 +- .../edgePoint/TrackTargetingBehaviour.java | 3 +- .../edgePoint/TrackTargetingBlockItem.java | 3 + .../edgePoint/signal/SignalBoundary.java | 17 +- .../edgePoint/signal/SignalPropagator.java | 16 +- .../edgePoint/signal/SingleTileEdgePoint.java | 10 +- .../edgePoint/signal/TrackEdgePoint.java | 22 +- .../edgePoint/station/GlobalStation.java | 17 +- .../edgePoint/station/StationTileEntity.java | 9 +- .../trains/track/StandardBogeyBlock.java | 2 +- .../logistics/trains/track/TrackBlock.java | 207 ++++- .../trains/track/TrackBlockOutline.java | 12 + .../trains/track/TrackPlacement.java | 21 +- .../logistics/trains/track/TrackShape.java | 31 +- .../trains/track/TrackTileEntity.java | 49 +- .../trains/track/TrackVoxelShapes.java | 4 + .../create/models/block/track/teleport.json | 171 ++++ .../create/textures/block/portal_track.png | Bin 0 -> 1534 bytes .../textures/block/portal_track_mip.png | Bin 0 -> 944 bytes .../create/textures/block/standard_track.png | Bin 1004 -> 1004 bytes 68 files changed, 2182 insertions(+), 524 deletions(-) create mode 100644 src/main/java/com/simibubi/create/content/logistics/trains/DimensionPalette.java create mode 100644 src/main/resources/assets/create/models/block/track/teleport.json create mode 100644 src/main/resources/assets/create/textures/block/portal_track.png create mode 100644 src/main/resources/assets/create/textures/block/portal_track_mip.png diff --git a/src/generated/resources/.cache/cache b/src/generated/resources/.cache/cache index 75d5b5836..594f9615a 100644 --- a/src/generated/resources/.cache/cache +++ b/src/generated/resources/.cache/cache @@ -486,7 +486,7 @@ f385988cb6fa9c48b5d59a6942ec50ed2b60c8bf assets/create/blockstates/stockpile_swi e815bfd854c2653f10828bb11950f7fb991d7efc assets/create/blockstates/stressometer.json 8b0c2c7ac72529565b3339aa8df7565858100afa assets/create/blockstates/tiled_glass.json a2454400b1cf9889f70aebdc89c52a1be25f543c assets/create/blockstates/tiled_glass_pane.json -85b57776edf426c2f8df6698b2482ea925914a5c assets/create/blockstates/track.json +a64fcf2bee9b49f1448d1ff691bd92d7590a59b0 assets/create/blockstates/track.json 408ae1009ee8bb2f2b83753d5909c53744f7865f assets/create/blockstates/track_signal.json 60609cfbcc9be6f7e41fb493ef3147beb9750b60 assets/create/blockstates/track_station.json 29af21c8d82891139d48d69f0393f612f2b6f8f1 assets/create/blockstates/tuff_pillar.json diff --git a/src/generated/resources/assets/create/blockstates/track.json b/src/generated/resources/assets/create/blockstates/track.json index a58f5794e..904650424 100644 --- a/src/generated/resources/assets/create/blockstates/track.json +++ b/src/generated/resources/assets/create/blockstates/track.json @@ -30,6 +30,21 @@ "model": "create:block/track/ascending", "y": 90 }, + "shape=tn,turn=false": { + "model": "create:block/track/teleport", + "y": 180 + }, + "shape=ts,turn=false": { + "model": "create:block/track/teleport" + }, + "shape=te,turn=false": { + "model": "create:block/track/teleport", + "y": 270 + }, + "shape=tw,turn=false": { + "model": "create:block/track/teleport", + "y": 90 + }, "shape=cr_o,turn=false": { "model": "create:block/track/cross_ortho" }, @@ -78,6 +93,21 @@ "model": "create:block/track/ascending", "y": 90 }, + "shape=tn,turn=true": { + "model": "create:block/track/teleport", + "y": 180 + }, + "shape=ts,turn=true": { + "model": "create:block/track/teleport" + }, + "shape=te,turn=true": { + "model": "create:block/track/teleport", + "y": 270 + }, + "shape=tw,turn=true": { + "model": "create:block/track/teleport", + "y": 90 + }, "shape=cr_o,turn=true": { "model": "create:block/track/cross_ortho" }, diff --git a/src/main/java/com/simibubi/create/AllShapes.java b/src/main/java/com/simibubi/create/AllShapes.java index 79aa796da..4d606f80c 100644 --- a/src/main/java/com/simibubi/create/AllShapes.java +++ b/src/main/java/com/simibubi/create/AllShapes.java @@ -148,6 +148,7 @@ public class AllShapes { TRACK_ORTHO = shape(TrackVoxelShapes.orthogonal()).forHorizontal(NORTH), TRACK_ASC = shape(TrackVoxelShapes.ascending()).forHorizontal(SOUTH), TRACK_DIAG = shape(TrackVoxelShapes.diagonal()).forHorizontal(SOUTH), + TRACK_ORTHO_LONG = shape(TrackVoxelShapes.longOrthogonalZOffset()).forHorizontal(SOUTH), WHISTLE_BASE = shape(1, 0, 1, 15, 3, 15).add(5, 0, 5, 11, 8, 11) .forDirectional(UP) diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/actors/PloughMovementBehaviour.java b/src/main/java/com/simibubi/create/content/contraptions/components/actors/PloughMovementBehaviour.java index c8a683f04..decdfab78 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/actors/PloughMovementBehaviour.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/actors/PloughMovementBehaviour.java @@ -18,6 +18,7 @@ import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.BubbleColumnBlock; import net.minecraft.world.level.block.FarmBlock; import net.minecraft.world.level.block.LiquidBlock; +import net.minecraft.world.level.block.NetherPortalBlock; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.storage.loot.LootContext; import net.minecraft.world.level.storage.loot.parameters.LootContextParams; @@ -80,6 +81,8 @@ public class PloughMovementBehaviour extends BlockBreakingMovementBehaviour { return false; if (state.getBlock() instanceof BubbleColumnBlock) return false; + if (state.getBlock() instanceof NetherPortalBlock) + return false; return state.getCollisionShape(world, breakingPos) .isEmpty(); } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/AbstractContraptionEntity.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/AbstractContraptionEntity.java index f705856d5..45c07c655 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/AbstractContraptionEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/AbstractContraptionEntity.java @@ -297,7 +297,7 @@ public abstract class AbstractContraptionEntity extends Entity implements IEntit contraption.onEntityTick(level); tickContraption(); super.tick(); - + if (!(level instanceof ServerLevelAccessor sl)) return; @@ -362,7 +362,7 @@ public abstract class AbstractContraptionEntity extends Entity implements IEntit context.rotation = v -> applyRotation(v, 1); context.position = actorPosition; - if (!actor.isActive(context)) + if (!isActorActive(context, actor)) continue; if (newPosVisited && !context.stall) { actor.visitNewPosition(context, gridPosition); @@ -408,6 +408,10 @@ public abstract class AbstractContraptionEntity extends Entity implements IEntit contraption.stalled = isStalled(); } + protected boolean isActorActive(MovementContext context, MovementBehaviour actor) { + return actor.isActive(context); + } + protected void onContraptionStalled() { AllPackets.channel.send(PacketDistributor.TRACKING_ENTITY.with(() -> this), new ContraptionStallPacket(getId(), getX(), getY(), getZ(), getStalledAngle())); @@ -790,4 +794,8 @@ public abstract class AbstractContraptionEntity extends Entity implements IEntit // Contraptions no longer catch fire } + public boolean isReadyForRender() { + return initialized; + } + } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/Contraption.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/Contraption.java index 2ec35d58f..d11a03226 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/Contraption.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/Contraption.java @@ -5,6 +5,7 @@ import static com.simibubi.create.content.contraptions.components.structureMovem import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; @@ -1372,6 +1373,22 @@ public abstract class Contraption { // return pos.equals(te.getBlockPos()); // } // } + + public Collection getRenderedBlocks() { + return blocks.values(); + } + + public Collection getSpecialRenderedTEs() { + return specialRenderedTileEntities; + } + + public boolean isHiddenInPortal(BlockPos localPos) { + return false; + } + + public Optional> getSimplifiedEntityColliders() { + return simplifiedEntityColliders; + } public static class ContraptionInvWrapper extends CombinedInvWrapper { protected final boolean isExternal; @@ -1392,4 +1409,5 @@ public abstract class Contraption { return handler instanceof ContraptionInvWrapper && ((ContraptionInvWrapper) handler).isSlotExternal(slot); } } + } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionCollider.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionCollider.java index c0ed4f5fc..255f22459 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionCollider.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionCollider.java @@ -116,7 +116,7 @@ public class ContraptionCollider { // Use simplified bbs when present final Vec3 motionCopy = motion; - List collidableBBs = contraption.simplifiedEntityColliders.orElseGet(() -> { + List collidableBBs = contraption.getSimplifiedEntityColliders().orElseGet(() -> { // Else find 'nearby' individual block shapes to collide with List bbs = new ArrayList<>(); @@ -490,6 +490,7 @@ public class ContraptionCollider { List potentialHits = BlockPos.betweenClosedStream(min, max) .filter(contraption.getBlocks()::containsKey) + .filter(Predicates.not(contraption::isHiddenInPortal)) .map(p -> { BlockState blockState = contraption.getBlocks() .get(p).state; diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionHandlerClient.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionHandlerClient.java index cb7b24959..0243d9566 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionHandlerClient.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionHandlerClient.java @@ -82,7 +82,7 @@ public class ContraptionHandlerClient { Couple rayInputs = getRayInputs(player); Vec3 origin = rayInputs.getFirst(); Vec3 target = rayInputs.getSecond(); - AABB aabb = new AABB(origin, target).inflate(4); + AABB aabb = new AABB(origin, target).inflate(16); List intersectingContraptions = mc.level.getEntitiesOfClass(AbstractContraptionEntity.class, aabb); @@ -143,6 +143,8 @@ public class ContraptionHandlerClient { VoxelShape raytraceShape = state.getShape(Minecraft.getInstance().level, BlockPos.ZERO.below()); if (raytraceShape.isEmpty()) return false; + if (contraption.isHiddenInPortal(p)) + return false; BlockHitResult rayTrace = raytraceShape.clip(localOrigin, localTarget, p); if (rayTrace != null) { mutableResult.setValue(rayTrace); diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/glue/SuperGlueEntity.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/glue/SuperGlueEntity.java index f2bc5447d..5f7e307ce 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/glue/SuperGlueEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/glue/SuperGlueEntity.java @@ -47,6 +47,7 @@ import net.minecraft.world.level.block.Mirror; import net.minecraft.world.level.block.Rotation; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.BooleanProperty; +import net.minecraft.world.level.portal.PortalInfo; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; import net.minecraftforge.entity.IEntityAdditionalSpawnData; @@ -287,6 +288,12 @@ public class SuperGlueEntity extends Entity implements IEntityAdditionalSpawnDat return getBoundingBox().contains(Vec3.atCenterOf(pos)); } + @Override + public PortalInfo findDimensionEntryPoint(ServerLevel pDestination) { + portalEntrancePos = blockPosition(); + return super.findDimensionEntryPoint(pDestination); + } + public void spawnParticles() { AABB bb = getBoundingBox(); Vec3 origin = new Vec3(bb.minX, bb.minY, bb.minZ); diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/interaction/controls/ControlsHandler.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/interaction/controls/ControlsHandler.java index bdda992e7..405485ff7 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/interaction/controls/ControlsHandler.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/interaction/controls/ControlsHandler.java @@ -68,7 +68,7 @@ public class ControlsHandler { if (packetCooldown > 0) packetCooldown--; - if (InputConstants.isKeyDown(Minecraft.getInstance() + if (entity.isRemoved() || InputConstants.isKeyDown(Minecraft.getInstance() .getWindow() .getWindow(), GLFW.GLFW_KEY_ESCAPE)) { BlockPos pos = controlsPos; diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/interaction/controls/ControlsMovementBehaviour.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/interaction/controls/ControlsMovementBehaviour.java index d103936c1..abff6a032 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/interaction/controls/ControlsMovementBehaviour.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/interaction/controls/ControlsMovementBehaviour.java @@ -27,6 +27,12 @@ public class ControlsMovementBehaviour implements MovementBehaviour { LerpedFloat equipAnimation = LerpedFloat.linear(); } + @Override + public void stopMoving(MovementContext context) { + context.contraption.entity.stopControlling(context.localPos); + MovementBehaviour.super.stopMoving(context); + } + @Override public void tick(MovementContext context) { MovementBehaviour.super.tick(context); diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/interaction/controls/ControlsServerHandler.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/interaction/controls/ControlsServerHandler.java index 7f06b7cd1..f3d78b7c2 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/interaction/controls/ControlsServerHandler.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/interaction/controls/ControlsServerHandler.java @@ -13,6 +13,7 @@ import com.simibubi.create.foundation.utility.IntAttached; import com.simibubi.create.foundation.utility.WorldAttached; import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.LevelAccessor; public class ControlsServerHandler { @@ -29,6 +30,11 @@ public class ControlsServerHandler { ControlsContext ctx = entry.getValue(); Collection list = ctx.keys; + if (ctx.entity.isRemoved()) { + iterator.remove(); + continue; + } + for (Iterator entryIterator = list.iterator(); entryIterator.hasNext();) { ManuallyPressedKey pressedKey = entryIterator.next(); pressedKey.decrement(); @@ -36,9 +42,16 @@ public class ControlsServerHandler { entryIterator.remove(); // key released } + Player player = world.getPlayerByUUID(entry.getKey()); + if (player == null) { + ctx.entity.stopControlling(ctx.controlsLocalPos); + iterator.remove(); + continue; + } + if (!ctx.entity.control(ctx.controlsLocalPos, list.stream() .map(ManuallyPressedKey::getSecond) - .toList(), world.getPlayerByUUID(entry.getKey()))) { + .toList(), player)) { ctx.entity.stopControlling(ctx.controlsLocalPos); } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ActorInstance.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ActorInstance.java index 6ce2d95a5..0097df6e5 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ActorInstance.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ActorInstance.java @@ -17,7 +17,7 @@ public abstract class ActorInstance { this.context = context; } - public void tick() { } + public void tick() { } public void beginFrame() { } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionEntityRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionEntityRenderer.java index 7646cb39c..55c5e0c88 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionEntityRenderer.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionEntityRenderer.java @@ -28,6 +28,8 @@ public class ContraptionEntityRenderer exte return false; if (!entity.isAlive()) return false; + if (!entity.isReadyForRender()) + return false; return super.shouldRender(entity, clippingHelper, cameraX, cameraY, cameraZ); } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionInstanceManager.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionInstanceManager.java index 2454e13f0..97a8925a1 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionInstanceManager.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionInstanceManager.java @@ -12,10 +12,12 @@ import com.jozufozu.flywheel.backend.instancing.TaskEngine; import com.jozufozu.flywheel.backend.instancing.blockentity.BlockEntityInstanceManager; import com.jozufozu.flywheel.core.virtual.VirtualRenderWorld; import com.simibubi.create.AllMovementBehaviours; +import com.simibubi.create.content.contraptions.components.structureMovement.Contraption; import com.simibubi.create.content.contraptions.components.structureMovement.MovementBehaviour; import com.simibubi.create.content.contraptions.components.structureMovement.MovementContext; import net.minecraft.client.Camera; +import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate.StructureBlockInfo; public class ContraptionInstanceManager extends BlockEntityInstanceManager { @@ -24,14 +26,22 @@ public class ContraptionInstanceManager extends BlockEntityInstanceManager { private final VirtualRenderWorld renderWorld; - ContraptionInstanceManager(MaterialManager materialManager, VirtualRenderWorld contraption) { + private Contraption contraption; + + ContraptionInstanceManager(MaterialManager materialManager, VirtualRenderWorld renderWorld, Contraption contraption) { super(materialManager); - this.renderWorld = contraption; + this.renderWorld = renderWorld; + this.contraption = contraption; } public void tick() { actors.forEach(ActorInstance::tick); } + + @Override + protected boolean canCreateInstance(BlockEntity blockEntity) { + return !contraption.isHiddenInPortal(blockEntity.getBlockPos()); + } @Override public void beginFrame(TaskEngine taskEngine, Camera info) { @@ -49,6 +59,9 @@ public class ContraptionInstanceManager extends BlockEntityInstanceManager { public ActorInstance createActor(Pair actor) { StructureBlockInfo blockInfo = actor.getLeft(); MovementContext context = actor.getRight(); + + if (contraption.isHiddenInPortal(context.localPos)) + return null; MovementBehaviour movementBehaviour = AllMovementBehaviours.of(blockInfo.state); diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionRenderDispatcher.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionRenderDispatcher.java index b86d5261f..9b7944a71 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionRenderDispatcher.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionRenderDispatcher.java @@ -48,29 +48,36 @@ public class ContraptionRenderDispatcher { /** * Reset a contraption's renderer. + * * @param contraption The contraption to invalidate. * @return true if there was a renderer associated with the given contraption. */ public static boolean invalidate(Contraption contraption) { Level level = contraption.entity.level; - return WORLDS.get(level).invalidate(contraption); + return WORLDS.get(level) + .invalidate(contraption); } public static void tick(Level world) { - if (Minecraft.getInstance().isPaused()) return; + if (Minecraft.getInstance() + .isPaused()) + return; - WORLDS.get(world).tick(); + WORLDS.get(world) + .tick(); } @SubscribeEvent public static void beginFrame(BeginFrameEvent event) { - WORLDS.get(event.getWorld()).beginFrame(event); + WORLDS.get(event.getWorld()) + .beginFrame(event); } @SubscribeEvent public static void renderLayer(RenderLayerEvent event) { - WORLDS.get(event.getWorld()).renderLayer(event); + WORLDS.get(event.getWorld()) + .renderLayer(event); GlError.pollAndThrow(() -> "contraption layer: " + event.getLayer()); } @@ -84,15 +91,17 @@ public class ContraptionRenderDispatcher { reset(); } - public static void renderFromEntity(AbstractContraptionEntity entity, Contraption contraption, MultiBufferSource buffers) { + public static void renderFromEntity(AbstractContraptionEntity entity, Contraption contraption, + MultiBufferSource buffers) { Level world = entity.level; ContraptionRenderInfo renderInfo = WORLDS.get(world) - .getRenderInfo(contraption); + .getRenderInfo(contraption); ContraptionMatrices matrices = renderInfo.getMatrices(); // something went wrong with the other rendering - if (!matrices.isReady()) return; + if (!matrices.isReady()) + return; VirtualRenderWorld renderWorld = renderInfo.renderWorld; @@ -106,28 +115,30 @@ public class ContraptionRenderDispatcher { public static VirtualRenderWorld setupRenderWorld(Level world, Contraption c) { ContraptionWorld contraptionWorld = c.getContraptionWorld(); - VirtualRenderWorld renderWorld = new VirtualRenderWorld(world, c.anchor, contraptionWorld.getHeight(), contraptionWorld.getMinBuildHeight()); + + BlockPos origin = c.anchor; + int height = contraptionWorld.getHeight(); + int minBuildHeight = contraptionWorld.getMinBuildHeight(); + VirtualRenderWorld renderWorld = new VirtualRenderWorld(world, origin, height, minBuildHeight); renderWorld.setBlockEntities(c.presentTileEntities.values()); - for (StructureTemplate.StructureBlockInfo info : c.getBlocks() - .values()) + .values()) // Skip individual lighting updates to prevent lag with large contraptions renderWorld.setBlock(info.pos, info.state, Block.UPDATE_SUPPRESS_LIGHT); renderWorld.runLightingEngine(); - return renderWorld; } public static void renderTileEntities(Level world, VirtualRenderWorld renderWorld, Contraption c, - ContraptionMatrices matrices, MultiBufferSource buffer) { - TileEntityRenderHelper.renderTileEntities(world, renderWorld, c.specialRenderedTileEntities, - matrices.getModelViewProjection(), matrices.getLight(), buffer); + ContraptionMatrices matrices, MultiBufferSource buffer) { + TileEntityRenderHelper.renderTileEntities(world, renderWorld, c.getSpecialRenderedTEs(), + matrices.getModelViewProjection(), matrices.getLight(), buffer); } protected static void renderActors(Level world, VirtualRenderWorld renderWorld, Contraption c, - ContraptionMatrices matrices, MultiBufferSource buffer) { + ContraptionMatrices matrices, MultiBufferSource buffer) { PoseStack m = matrices.getModel(); for (Pair actor : c.getActors()) { @@ -140,18 +151,20 @@ public class ContraptionRenderDispatcher { MovementBehaviour movementBehaviour = AllMovementBehaviours.of(blockInfo.state); if (movementBehaviour != null) { + if (c.isHiddenInPortal(blockInfo.pos)) + continue; m.pushPose(); TransformStack.cast(m) - .translate(blockInfo.pos); + .translate(blockInfo.pos); movementBehaviour.renderInContraption(context, renderWorld, matrices, buffer); m.popPose(); } } } - public static SuperByteBuffer buildStructureBuffer(VirtualRenderWorld renderWorld, Contraption c, RenderType layer) { - Collection values = c.getBlocks() - .values(); + public static SuperByteBuffer buildStructureBuffer(VirtualRenderWorld renderWorld, Contraption c, + RenderType layer) { + Collection values = c.getRenderedBlocks(); BufferBuilder builder = ModelUtil.getBufferBuilderFromTemplate(renderWorld, layer, values); return new SuperByteBuffer(builder); } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionRenderInfo.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionRenderInfo.java index c9e18059f..a903397f7 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionRenderInfo.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/ContraptionRenderInfo.java @@ -38,7 +38,7 @@ public class ContraptionRenderInfo { } public boolean isVisible() { - return visible && contraption.entity.isAlive(); + return visible && contraption.entity.isAlive() && contraption.entity.isReadyForRender(); } /** diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/FlwContraption.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/FlwContraption.java index e937d4347..f641522e9 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/FlwContraption.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/render/FlwContraption.java @@ -29,6 +29,7 @@ import net.minecraft.client.renderer.RenderType; import net.minecraft.util.Mth; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate.StructureBlockInfo; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; @@ -137,10 +138,10 @@ public class FlwContraption extends ContraptionRenderInfo { renderLayers.clear(); List blockLayers = RenderType.chunkBufferLayers(); - + Collection renderedBlocks = contraption.getRenderedBlocks(); + for (RenderType layer : blockLayers) { - Model layerModel = new WorldModel(renderWorld, layer, contraption.getBlocks().values(), layer + "_" + contraption.entity.getId()); - + Model layerModel = new WorldModel(renderWorld, layer, renderedBlocks, layer + "_" + contraption.entity.getId()); renderLayers.put(layer, new ArrayModelRenderer(layerModel)); } } @@ -187,14 +188,14 @@ public class FlwContraption extends ContraptionRenderInfo { .setGroupFactory(ContraptionGroup.forContraption(parent)) .setIgnoreOriginCoordinate(true) .build(); - tileInstanceManager = new ContraptionInstanceManager(engine, parent.renderWorld); + tileInstanceManager = new ContraptionInstanceManager(engine, parent.renderWorld, parent.contraption); engine.addListener(tileInstanceManager); this.engine = engine; } case BATCHING -> { engine = new BatchingEngine(); - tileInstanceManager = new ContraptionInstanceManager(engine, parent.renderWorld); + tileInstanceManager = new ContraptionInstanceManager(engine, parent.renderWorld, parent.contraption); } default -> throw new IllegalArgumentException("Unknown engine type"); } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/DimensionPalette.java b/src/main/java/com/simibubi/create/content/logistics/trains/DimensionPalette.java new file mode 100644 index 000000000..8ab827f42 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/logistics/trains/DimensionPalette.java @@ -0,0 +1,68 @@ +package com.simibubi.create.content.logistics.trains; + +import java.util.ArrayList; +import java.util.List; + +import com.simibubi.create.foundation.utility.NBTHelper; + +import net.minecraft.core.Registry; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.Tag; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.Level; + +public class DimensionPalette { + + List> gatheredDims; + + public DimensionPalette() { + gatheredDims = new ArrayList<>(); + } + + public int encode(ResourceKey dimension) { + int indexOf = gatheredDims.indexOf(dimension); + if (indexOf == -1) { + indexOf = gatheredDims.size(); + gatheredDims.add(dimension); + } + return indexOf; + } + + public ResourceKey decode(int index) { + if (gatheredDims.size() <= index || index < 0) + return Level.OVERWORLD; + return gatheredDims.get(index); + } + + public void send(FriendlyByteBuf buffer) { + buffer.writeInt(gatheredDims.size()); + gatheredDims.forEach(rk -> buffer.writeResourceLocation(rk.location())); + } + + public static DimensionPalette receive(FriendlyByteBuf buffer) { + DimensionPalette palette = new DimensionPalette(); + int length = buffer.readInt(); + for (int i = 0; i < length; i++) + palette.gatheredDims.add(ResourceKey.create(Registry.DIMENSION_REGISTRY, buffer.readResourceLocation())); + return palette; + } + + public void write(CompoundTag tag) { + tag.put("DimensionPalette", NBTHelper.writeCompoundList(gatheredDims, rk -> { + CompoundTag c = new CompoundTag(); + c.putString("Id", rk.location() + .toString()); + return c; + })); + } + + public static DimensionPalette read(CompoundTag tag) { + DimensionPalette palette = new DimensionPalette(); + NBTHelper.iterateCompoundList(tag.getList("DimensionPalette", Tag.TAG_COMPOUND), c -> palette.gatheredDims + .add(ResourceKey.create(Registry.DIMENSION_REGISTRY, new ResourceLocation(c.getString("Id"))))); + return palette; + } + +} diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/GlobalRailwayManager.java b/src/main/java/com/simibubi/create/content/logistics/trains/GlobalRailwayManager.java index a087cba9d..d5022e84a 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/GlobalRailwayManager.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/GlobalRailwayManager.java @@ -146,8 +146,8 @@ public class GlobalRailwayManager { markTracksDirty(); } - public void updateSplitGraph(TrackGraph graph) { - Set disconnected = graph.findDisconnectedGraphs(null); + public void updateSplitGraph(LevelAccessor level, TrackGraph graph) { + Set disconnected = graph.findDisconnectedGraphs(level, null); disconnected.forEach(this::putGraphWithDefaultGroup); if (!disconnected.isEmpty()) { sync.graphSplit(graph, disconnected); @@ -219,6 +219,13 @@ public class GlobalRailwayManager { for (Iterator iterator = waitingTrains.iterator(); iterator.hasNext();) { Train train = iterator.next(); + + if (train.invalid) { + iterator.remove(); + trains.remove(train.id); + continue; + } + if (train.navigation.waitingForSignal != null) continue; movingTrains.add(train); @@ -227,11 +234,20 @@ public class GlobalRailwayManager { for (Iterator iterator = movingTrains.iterator(); iterator.hasNext();) { Train train = iterator.next(); + + if (train.invalid) { + iterator.remove(); + trains.remove(train.id); + continue; + } + if (train.navigation.waitingForSignal == null) continue; waitingTrains.add(train); iterator.remove(); } + + } public void tickSignalOverlay() { diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/ITrackBlock.java b/src/main/java/com/simibubi/create/content/logistics/trains/ITrackBlock.java index 920a4a212..33946c2a5 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/ITrackBlock.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/ITrackBlock.java @@ -21,7 +21,10 @@ import com.simibubi.create.foundation.utility.Pair; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.Direction.AxisDirection; +import net.minecraft.resources.ResourceKey; +import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.Vec3; import net.minecraftforge.api.distmarker.Dist; @@ -47,8 +50,10 @@ public interface ITrackBlock { return isSlope(world, pos, state) ? .5 : 0; } - public static Collection walkConnectedTracks(BlockGetter world, TrackNodeLocation location, + public static Collection walkConnectedTracks(BlockGetter worldIn, TrackNodeLocation location, boolean linear) { + BlockGetter world = location != null && worldIn instanceof ServerLevel sl ? sl.getServer() + .getLevel(location.dimension) : worldIn; List list = new ArrayList<>(); for (BlockPos blockPos : location.allAdjacent()) { BlockState blockState = world.getBlockState(blockPos); @@ -58,15 +63,18 @@ public interface ITrackBlock { return list; } - public default Collection getConnected(BlockGetter world, BlockPos pos, BlockState state, + public default Collection getConnected(BlockGetter worldIn, BlockPos pos, BlockState state, boolean linear, @Nullable TrackNodeLocation connectedTo) { + BlockGetter world = connectedTo != null && worldIn instanceof ServerLevel sl ? sl.getServer() + .getLevel(connectedTo.dimension) : worldIn; Vec3 center = Vec3.atBottomCenterOf(pos) .add(0, getElevationAtCenter(world, pos, state), 0); List list = new ArrayList<>(); TrackShape shape = state.getValue(TrackBlock.SHAPE); getTrackAxes(world, pos, state).forEach(axis -> { addToListIfConnected(connectedTo, list, (d, b) -> axis.scale(b ? d : -d) - .add(center), b -> shape.getNormal(), axis, null); + .add(center), b -> shape.getNormal(), b -> world instanceof Level l ? l.dimension() : Level.OVERWORLD, + axis, null); }); return list; @@ -74,15 +82,22 @@ public interface ITrackBlock { public static void addToListIfConnected(@Nullable TrackNodeLocation fromEnd, Collection list, BiFunction offsetFactory, Function normalFactory, - Vec3 axis, BezierConnection viaTurn) { + Function> dimensionFactory, Vec3 axis, BezierConnection viaTurn) { - DiscoveredLocation firstLocation = new DiscoveredLocation(offsetFactory.apply(0.5d, true)).viaTurn(viaTurn) - .withNormal(normalFactory.apply(true)) - .withDirection(axis); - DiscoveredLocation secondLocation = new DiscoveredLocation(offsetFactory.apply(0.5d, false)).viaTurn(viaTurn) - .withNormal(normalFactory.apply(false)) - .withDirection(axis); + DiscoveredLocation firstLocation = + new DiscoveredLocation(dimensionFactory.apply(true), offsetFactory.apply(0.5d, true)).viaTurn(viaTurn) + .withNormal(normalFactory.apply(true)) + .withDirection(axis); + DiscoveredLocation secondLocation = + new DiscoveredLocation(dimensionFactory.apply(false), offsetFactory.apply(0.5d, false)).viaTurn(viaTurn) + .withNormal(normalFactory.apply(false)) + .withDirection(axis); + if (!firstLocation.dimension.equals(secondLocation.dimension)) { + firstLocation.forceNode(); + secondLocation.forceNode(); + } + boolean skipFirst = false; boolean skipSecond = false; diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/RailwaySavedData.java b/src/main/java/com/simibubi/create/content/logistics/trains/RailwaySavedData.java index 00622e92d..fc2b42097 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/RailwaySavedData.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/RailwaySavedData.java @@ -26,14 +26,15 @@ public class RailwaySavedData extends SavedData { public CompoundTag save(CompoundTag nbt) { GlobalRailwayManager railways = Create.RAILWAYS; Create.LOGGER.info("Saving Railway Information..."); - nbt.put("RailGraphs", NBTHelper.writeCompoundList(railways.trackNetworks.values(), TrackGraph::write)); - nbt.put("SignalBlocks", - NBTHelper.writeCompoundList(railways.signalEdgeGroups.values(), seg -> { - if (seg.fallbackGroup && !railways.trackNetworks.containsKey(seg.id)) - return null; - return seg.write(); - })); - nbt.put("Trains", NBTHelper.writeCompoundList(railways.trains.values(), Train::write)); + DimensionPalette dimensions = new DimensionPalette(); + nbt.put("RailGraphs", NBTHelper.writeCompoundList(railways.trackNetworks.values(), tg -> tg.write(dimensions))); + nbt.put("SignalBlocks", NBTHelper.writeCompoundList(railways.signalEdgeGroups.values(), seg -> { + if (seg.fallbackGroup && !railways.trackNetworks.containsKey(seg.id)) + return null; + return seg.write(); + })); + nbt.put("Trains", NBTHelper.writeCompoundList(railways.trains.values(), t -> t.write(dimensions))); + dimensions.write(nbt); return nbt; } @@ -44,18 +45,19 @@ public class RailwaySavedData extends SavedData { sd.trains = new HashMap<>(); Create.LOGGER.info("Loading Railway Information..."); + DimensionPalette dimensions = DimensionPalette.read(nbt); NBTHelper.iterateCompoundList(nbt.getList("RailGraphs", Tag.TAG_COMPOUND), c -> { - TrackGraph graph = TrackGraph.read(c); + TrackGraph graph = TrackGraph.read(c, dimensions); sd.trackNetworks.put(graph.id, graph); }); - NBTHelper.iterateCompoundList(nbt.getList("Trains", Tag.TAG_COMPOUND), c -> { - Train train = Train.read(c, sd.trackNetworks); - sd.trains.put(train.id, train); - }); NBTHelper.iterateCompoundList(nbt.getList("SignalBlocks", Tag.TAG_COMPOUND), c -> { SignalEdgeGroup group = SignalEdgeGroup.read(c); sd.signalEdgeGroups.put(group.id, group); }); + NBTHelper.iterateCompoundList(nbt.getList("Trains", Tag.TAG_COMPOUND), c -> { + Train train = Train.read(c, sd.trackNetworks, dimensions); + sd.trains.put(train.id, train); + }); for (TrackGraph graph : sd.trackNetworks.values()) { for (SignalBoundary signal : graph.getPoints(EdgePointType.SIGNAL)) { diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/TrackEdge.java b/src/main/java/com/simibubi/create/content/logistics/trains/TrackEdge.java index d21eca81f..35a630b47 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/TrackEdge.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/TrackEdge.java @@ -11,7 +11,6 @@ import com.simibubi.create.foundation.utility.VecHelper; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction.Axis; import net.minecraft.nbt.CompoundTag; -import net.minecraft.network.FriendlyByteBuf; import net.minecraft.util.Mth; import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; @@ -22,8 +21,10 @@ public class TrackEdge { public TrackNode node2; BezierConnection turn; EdgeData edgeData; + boolean interDimensional; public TrackEdge(TrackNode node1, TrackNode node2, BezierConnection turn) { + this.interDimensional = !node1.location.dimension.equals(node2.location.dimension); this.edgeData = new EdgeData(this); this.node1 = node1; this.node2 = node2; @@ -34,6 +35,10 @@ public class TrackEdge { return turn != null; } + public boolean isInterDimensional() { + return interDimensional; + } + public EdgeData getEdgeData() { return edgeData; } @@ -47,15 +52,24 @@ public class TrackEdge { .normalize(); } + public boolean canTravelTo(TrackEdge other) { + if (isInterDimensional() || other.isInterDimensional()) + return true; + Vec3 newDirection = other.getDirection(true); + return getDirection(false).dot(newDirection) > 7 / 8f; + } + public double getLength() { - return isTurn() ? turn.getLength() - : node1.location.getLocation() - .distanceTo(node2.location.getLocation()); + return isInterDimensional() ? 0 + : isTurn() ? turn.getLength() + : node1.location.getLocation() + .distanceTo(node2.location.getLocation()); } public double incrementT(double currentT, double distance) { boolean tooFar = Math.abs(distance) > 5; - distance = distance / getLength(); + double length = getLength(); + distance = distance / (length == 0 ? 1 : length); return !tooFar && isTurn() ? turn.incrementT(currentT, distance) : currentT + distance; } @@ -71,6 +85,8 @@ public class TrackEdge { Vec3 w1 = other1.location.getLocation(); Vec3 w2 = other2.location.getLocation(); + if (isInterDimensional() || other.isInterDimensional()) + return Collections.emptyList(); if (v1.y != v2.y || v1.y != w1.y || v1.y != w2.y) return Collections.emptyList(); @@ -152,26 +168,17 @@ public class TrackEdge { return isTurn() ? turn.getNormal(Mth.clamp(t, 0, 1)) : node1.getNormal(); } - public void write(FriendlyByteBuf buffer) { - buffer.writeBoolean(isTurn()); - if (isTurn()) - turn.write(buffer); - } - - public static TrackEdge read(FriendlyByteBuf buffer) { - return new TrackEdge(null, null, buffer.readBoolean() ? new BezierConnection(buffer) : null); - } - - public CompoundTag write() { + public CompoundTag write(DimensionPalette dimensions) { CompoundTag baseCompound = isTurn() ? turn.write(BlockPos.ZERO) : new CompoundTag(); - baseCompound.put("Signals", edgeData.write()); + baseCompound.put("Signals", edgeData.write(dimensions)); return baseCompound; } - public static TrackEdge read(CompoundTag tag, TrackGraph graph) { + public static TrackEdge read(TrackNode node1, TrackNode node2, CompoundTag tag, TrackGraph graph, + DimensionPalette dimensions) { TrackEdge trackEdge = - new TrackEdge(null, null, tag.contains("Positions") ? new BezierConnection(tag, BlockPos.ZERO) : null); - trackEdge.edgeData = EdgeData.read(tag.getCompound("Signals"), trackEdge, graph); + new TrackEdge(node1, node2, tag.contains("Positions") ? new BezierConnection(tag, BlockPos.ZERO) : null); + trackEdge.edgeData = EdgeData.read(tag.getCompound("Signals"), trackEdge, graph, dimensions); return trackEdge; } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraph.java b/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraph.java index 712f78da4..f472249d7 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraph.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraph.java @@ -31,10 +31,8 @@ import com.simibubi.create.foundation.utility.Couple; import com.simibubi.create.foundation.utility.NBTHelper; import com.simibubi.create.foundation.utility.VecHelper; -import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; -import net.minecraft.nbt.NbtUtils; import net.minecraft.nbt.Tag; import net.minecraft.resources.ResourceKey; import net.minecraft.world.level.Level; @@ -117,8 +115,8 @@ public class TrackGraph { return nodes.keySet(); } - public TrackNode locateNode(Vec3 position) { - return locateNode(new TrackNodeLocation(position)); + public TrackNode locateNode(Level level, Vec3 position) { + return locateNode(new TrackNodeLocation(position).in(level)); } public TrackNode locateNode(TrackNodeLocation position) { @@ -269,7 +267,8 @@ public class TrackGraph { } } - public Set findDisconnectedGraphs(@Nullable Map preAssignedIds) { + public Set findDisconnectedGraphs(@Nullable LevelAccessor level, + @Nullable Map preAssignedIds) { Set dicovered = new HashSet<>(); Set vertices = new HashSet<>(nodes.keySet()); List frontier = new ArrayList<>(); @@ -295,7 +294,7 @@ public class TrackGraph { frontier.add(connected.getLocation()); if (target != null) { - transfer(currentNode, target); + transfer(level, currentNode, target); if (preAssignedIds != null && preAssignedIds.containsKey(currentNode.getNetId())) target.setId(preAssignedIds.get(currentNode.getNetId())); } @@ -313,13 +312,13 @@ public class TrackGraph { color = Color.rainbowColor(new Random(id.getLeastSignificantBits()).nextInt()); } - public void transfer(TrackNode node, TrackGraph target) { + public void transfer(LevelAccessor level, TrackNode node, TrackGraph target) { target.addNode(node); target.invalidateBounds(); TrackNodeLocation nodeLoc = node.getLocation(); Map connections = getConnectionsFrom(node); - Map trains = Create.RAILWAYS.trains; + Map trains = Create.RAILWAYS.sided(level).trains; if (!connections.isEmpty()) { target.connectionsByNode.put(node, connections); @@ -487,7 +486,7 @@ public class TrackGraph { Create.RAILWAYS.markTracksDirty(); } - public CompoundTag write() { + public CompoundTag write(DimensionPalette dimensions) { CompoundTag tag = new CompoundTag(); tag.putUUID("Id", id); tag.putInt("Color", color.getRGB()); @@ -499,7 +498,8 @@ public class TrackGraph { for (TrackNode railNode : nodes.values()) { indexTracker.put(railNode, i); CompoundTag nodeTag = new CompoundTag(); - nodeTag.put("Location", NbtUtils.writeBlockPos(new BlockPos(railNode.getLocation()))); + nodeTag.put("Location", railNode.getLocation() + .write(dimensions)); nodeTag.put("Normal", VecHelper.writeNBT(railNode.getNormal())); nodesList.add(nodeTag); i++; @@ -517,21 +517,21 @@ public class TrackGraph { if (index2 == null) return; connectionTag.putInt("To", index2); - connectionTag.put("EdgeData", edge.write()); + connectionTag.put("EdgeData", edge.write(dimensions)); connectionsList.add(connectionTag); }); nodeTag.put("Connections", connectionsList); }); tag.put("Nodes", nodesList); - tag.put("Points", edgePoints.write()); + tag.put("Points", edgePoints.write(dimensions)); return tag; } - public static TrackGraph read(CompoundTag tag) { + public static TrackGraph read(CompoundTag tag, DimensionPalette dimensions) { TrackGraph graph = new TrackGraph(tag.getUUID("Id")); graph.color = new Color(tag.getInt("Color")); - graph.edgePoints.read(tag.getCompound("Points")); + graph.edgePoints.read(tag.getCompound("Points"), dimensions); Map indexTracker = new HashMap<>(); ListTag nodesList = tag.getList("Nodes", Tag.TAG_COMPOUND); @@ -539,8 +539,7 @@ public class TrackGraph { int i = 0; for (Tag t : nodesList) { CompoundTag nodeTag = (CompoundTag) t; - TrackNodeLocation location = - TrackNodeLocation.fromPackedPos(NbtUtils.readBlockPos(nodeTag.getCompound("Location"))); + TrackNodeLocation location = TrackNodeLocation.read(nodeTag.getCompound("Location"), dimensions); Vec3 normal = VecHelper.readNBT(nodeTag.getList("Normal", Tag.TAG_DOUBLE)); graph.loadNode(location, nextNodeId(), normal); indexTracker.put(i, graph.locateNode(location)); @@ -557,9 +556,7 @@ public class TrackGraph { continue; NBTHelper.iterateCompoundList(nodeTag.getList("Connections", Tag.TAG_COMPOUND), c -> { TrackNode node2 = indexTracker.get(c.getInt("To")); - TrackEdge edge = TrackEdge.read(c.getCompound("EdgeData"), graph); - edge.node1 = node1; - edge.node2 = node2; + TrackEdge edge = TrackEdge.read(node1, node2, c.getCompound("EdgeData"), graph, dimensions); graph.putConnection(node1, node2, edge); }); } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraphHelper.java b/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraphHelper.java index f99444e7f..8400d1501 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraphHelper.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraphHelper.java @@ -34,7 +34,7 @@ public class TrackGraphHelper { // Case 1: Centre of block lies on a node TrackNodeLocation location = new TrackNodeLocation(Vec3.atBottomCenterOf(pos) - .add(0, track.getElevationAtCenter(level, pos, trackBlockState), 0)); + .add(0, track.getElevationAtCenter(level, pos, trackBlockState), 0)).in(level); graph = Create.RAILWAYS.sided(level) .getGraph(level, location); if (graph != null) { @@ -142,7 +142,7 @@ public class TrackGraphHelper { if (bc == null || !bc.isPrimary()) return null; - TrackNodeLocation targetLoc = new TrackNodeLocation(bc.starts.getSecond()); + TrackNodeLocation targetLoc = new TrackNodeLocation(bc.starts.getSecond()).in(level); for (DiscoveredLocation location : track.getConnected(level, pos, state, true, null)) { TrackGraph graph = Create.RAILWAYS.sided(level) .getGraph(level, location); diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraphSync.java b/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraphSync.java index 860f1c296..b025763ee 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraphSync.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraphSync.java @@ -46,7 +46,8 @@ public class TrackGraphSync { public void edgeAdded(TrackGraph graph, TrackNode node1, TrackNode node2, TrackEdge edge) { flushGraphPacket(graph.id); - currentGraphSyncPacket.addedEdges.add(Pair.of(Couple.create(node1.getNetId(), node2.getNetId()), edge)); + currentGraphSyncPacket.addedEdges + .add(Pair.of(Couple.create(node1.getNetId(), node2.getNetId()), edge.getTurn())); } public void pointAdded(TrackGraph graph, TrackEdgePoint point) { @@ -127,7 +128,7 @@ public class TrackGraphSync { graph.connectionsByNode.get(node) .forEach((node2, edge) -> { Couple key = Couple.create(node.getNetId(), node2.getNetId()); - currentPacket.addedEdges.add(Pair.of(key, edge)); + currentPacket.addedEdges.add(Pair.of(key, edge.getTurn())); currentPacket.syncEdgeData(node, node2, edge); }); diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraphSyncPacket.java b/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraphSyncPacket.java index 22a0eccbc..c85ef8ec2 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraphSyncPacket.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraphSyncPacket.java @@ -15,14 +15,13 @@ import com.simibubi.create.foundation.utility.Couple; import com.simibubi.create.foundation.utility.Pair; import com.simibubi.create.foundation.utility.VecHelper; -import net.minecraft.core.BlockPos; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.world.phys.Vec3; public class TrackGraphSyncPacket extends TrackGraphPacket { Map> addedNodes; - List, TrackEdge>> addedEdges; + List, BezierConnection>> addedEdges; List removedNodes; List addedEdgePoints; List removedEdgePoints; @@ -52,6 +51,8 @@ public class TrackGraphSyncPacket extends TrackGraphPacket { if (packetDeletesGraph) return; + DimensionPalette dimensions = DimensionPalette.receive(buffer); + addedNodes = new HashMap<>(); addedEdges = new ArrayList<>(); addedEdgePoints = new ArrayList<>(); @@ -67,15 +68,16 @@ public class TrackGraphSyncPacket extends TrackGraphPacket { size = buffer.readVarInt(); for (int i = 0; i < size; i++) addedNodes.put(buffer.readVarInt(), - Pair.of(TrackNodeLocation.fromPackedPos(buffer.readBlockPos()), VecHelper.read(buffer))); + Pair.of(TrackNodeLocation.receive(buffer, dimensions), VecHelper.read(buffer))); size = buffer.readVarInt(); for (int i = 0; i < size; i++) - addedEdges.add(Pair.of(Couple.create(buffer::readVarInt), TrackEdge.read(buffer))); + addedEdges.add( + Pair.of(Couple.create(buffer::readVarInt), buffer.readBoolean() ? new BezierConnection(buffer) : null)); size = buffer.readVarInt(); for (int i = 0; i < size; i++) - addedEdgePoints.add(EdgePointType.read(buffer)); + addedEdgePoints.add(EdgePointType.read(buffer, dimensions)); size = buffer.readVarInt(); for (int i = 0; i < size; i++) @@ -99,19 +101,26 @@ public class TrackGraphSyncPacket extends TrackGraphPacket { @Override public void write(FriendlyByteBuf buffer) { - buffer.writeUUID(graphId); buffer.writeBoolean(packetDeletesGraph); + if (packetDeletesGraph) return; + // Populate and send palette ahead of time + DimensionPalette dimensions = new DimensionPalette(); + addedNodes.forEach((node, loc) -> dimensions.encode(loc.getFirst().dimension)); + addedEdgePoints.forEach(ep -> ep.edgeLocation.forEach(loc -> dimensions.encode(loc.dimension))); + dimensions.send(buffer); + buffer.writeVarInt(removedNodes.size()); removedNodes.forEach(buffer::writeVarInt); buffer.writeVarInt(addedNodes.size()); addedNodes.forEach((node, loc) -> { buffer.writeVarInt(node); - buffer.writeBlockPos(new BlockPos(loc.getFirst())); + loc.getFirst() + .send(buffer, dimensions); VecHelper.write(loc.getSecond(), buffer); }); @@ -119,12 +128,14 @@ public class TrackGraphSyncPacket extends TrackGraphPacket { addedEdges.forEach(pair -> { pair.getFirst() .forEach(buffer::writeVarInt); - pair.getSecond() - .write(buffer); + BezierConnection turn = pair.getSecond(); + buffer.writeBoolean(turn != null); + if (turn != null) + turn.write(buffer); }); buffer.writeVarInt(addedEdgePoints.size()); - addedEdgePoints.forEach(ep -> ep.write(buffer)); + addedEdgePoints.forEach(ep -> ep.write(buffer, dimensions)); buffer.writeVarInt(removedEdgePoints.size()); removedEdgePoints.forEach(buffer::writeUUID); @@ -166,17 +177,13 @@ public class TrackGraphSyncPacket extends TrackGraphPacket { graph.loadNode(nodeLocation.getFirst(), nodeId, nodeLocation.getSecond()); } - for (Pair, TrackEdge> pair : addedEdges) { + for (Pair, BezierConnection> pair : addedEdges) { Couple nodes = pair.getFirst() .map(graph::getNode); TrackNode node1 = nodes.getFirst(); TrackNode node2 = nodes.getSecond(); - if (node1 != null && node2 != null) { - TrackEdge edge = pair.getSecond(); - edge.node1 = node1; - edge.node2 = node2; - graph.putConnection(node1, node2, edge); - } + if (node1 != null && node2 != null) + graph.putConnection(node1, node2, new TrackEdge(node1, node2, pair.getSecond())); } for (TrackEdgePoint edgePoint : addedEdgePoints) @@ -189,7 +196,7 @@ public class TrackGraphSyncPacket extends TrackGraphPacket { handleEdgeData(manager, graph); if (!splitSubGraphs.isEmpty()) - graph.findDisconnectedGraphs(splitSubGraphs) + graph.findDisconnectedGraphs(null, splitSubGraphs) .forEach(manager::putGraph); } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraphVisualizer.java b/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraphVisualizer.java index a4ef2605d..68fb5dc3d 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraphVisualizer.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/TrackGraphVisualizer.java @@ -11,7 +11,6 @@ import com.simibubi.create.AllKeys; import com.simibubi.create.Create; import com.simibubi.create.CreateClient; import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgeData; -import com.simibubi.create.content.logistics.trains.management.edgePoint.TrackEdgeIntersection; import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalBoundary; import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalEdgeGroup; import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.TrackEdgePoint; @@ -51,6 +50,9 @@ public class TrackGraphVisualizer { Vec3 location = nodeLocation.getLocation(); if (location.distanceTo(camera) > 50) continue; + if (!mc.level.dimension() + .equals(nodeLocation.dimension)) + continue; Map map = graph.connectionsByNode.get(node); if (map == null) @@ -62,16 +64,8 @@ public class TrackGraphVisualizer { TrackEdge edge = entry.getValue(); EdgeData signalData = edge.getEdgeData(); - // temporary - if (other.hashCode() > hashCode == ctrl) - for (TrackEdgeIntersection intersection : signalData.getIntersections()) { - Vec3 v1 = edge.getPosition(intersection.location / edge.getLength()); - Vec3 v2 = v1.add(node.normal.scale(8 / 16f)); - outliner.showLine(intersection, v1, v2) - .colored(Color.mixColors(Color.WHITE, graph.color, 1)) - .lineWidth(width); - } // - + if (!edge.node1.location.dimension.equals(edge.node2.location.dimension)) + continue; if (other.hashCode() > hashCode && !ctrl) continue; @@ -234,6 +228,9 @@ public class TrackGraphVisualizer { Vec3 location = nodeLocation.getLocation(); if (location.distanceTo(camera) > 50) continue; + if (!mc.level.dimension() + .equals(nodeLocation.dimension)) + continue; Vec3 yOffset = new Vec3(0, 3 / 16f, 0); Vec3 v1 = location.add(yOffset); @@ -249,12 +246,20 @@ public class TrackGraphVisualizer { int hashCode = node.hashCode(); for (Entry entry : map.entrySet()) { TrackNode other = entry.getKey(); + TrackEdge edge = entry.getValue(); + if (!edge.node1.location.dimension.equals(edge.node2.location.dimension)) { + v1 = location.add(yOffset); + v2 = v1.add(node.normal.scale(3 / 16f)); + CreateClient.OUTLINER.showLine(Integer.valueOf(node.netId), v1, v2) + .colored(Color.mixColors(Color.WHITE, graph.color, 1)) + .lineWidth(1 / 4f); + continue; + } if (other.hashCode() > hashCode && !AllKeys.isKeyDown(GLFW.GLFW_KEY_LEFT_CONTROL)) continue; - yOffset = new Vec3(0, (other.hashCode() > hashCode ? 6 : 4) / 16f, 0); - TrackEdge edge = entry.getValue(); + yOffset = new Vec3(0, (other.hashCode() > hashCode ? 6 : 4) / 16f, 0); if (!edge.isTurn()) { CreateClient.OUTLINER.showLine(edge, edge.getPosition(0) .add(yOffset), diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/TrackNodeLocation.java b/src/main/java/com/simibubi/create/content/logistics/trains/TrackNodeLocation.java index e34cd76c4..89acfea55 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/TrackNodeLocation.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/TrackNodeLocation.java @@ -2,16 +2,24 @@ package com.simibubi.create.content.logistics.trains; import java.util.Collection; import java.util.HashSet; +import java.util.Objects; import java.util.Set; import com.simibubi.create.foundation.utility.Iterate; import net.minecraft.core.BlockPos; import net.minecraft.core.Vec3i; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtUtils; +import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.resources.ResourceKey; +import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; public class TrackNodeLocation extends Vec3i { + public ResourceKey dimension; + public TrackNodeLocation(Vec3 vec) { this(vec.x, vec.y, vec.z); } @@ -20,7 +28,16 @@ public class TrackNodeLocation extends Vec3i { super(Math.round(p_121865_ * 2), Math.floor(p_121866_ * 2), Math.round(p_121867_ * 2)); } - public static TrackNodeLocation fromPackedPos(BlockPos bufferPos) { + public TrackNodeLocation in(Level level) { + return in(level.dimension()); + } + + public TrackNodeLocation in(ResourceKey dimension) { + this.dimension = dimension; + return this; + } + + private static TrackNodeLocation fromPackedPos(BlockPos bufferPos) { return new TrackNodeLocation(bufferPos); } @@ -32,14 +49,44 @@ public class TrackNodeLocation extends Vec3i { return new Vec3(getX() / 2f, getY() / 2f, getZ() / 2f); } + public ResourceKey getDimension() { + return dimension; + } + @Override public boolean equals(Object pOther) { - return super.equals(pOther); + return super.equals(pOther) && pOther instanceof TrackNodeLocation tnl + && Objects.equals(tnl.dimension, dimension); } @Override public int hashCode() { - return (this.getY() + this.getZ() * 31) * 31 + this.getX(); + return (this.getY() + (this.getZ() * 31 + dimension.hashCode()) * 31) * 31 + this.getX(); + } + + public CompoundTag write(DimensionPalette dimensions) { + CompoundTag c = NbtUtils.writeBlockPos(new BlockPos(this)); + if (dimensions != null) + c.putInt("D", dimensions.encode(dimension)); + return c; + } + + public static TrackNodeLocation read(CompoundTag tag, DimensionPalette dimensions) { + TrackNodeLocation location = fromPackedPos(NbtUtils.readBlockPos(tag)); + if (dimensions != null) + location.dimension = dimensions.decode(tag.getInt("D")); + return location; + } + + public void send(FriendlyByteBuf buffer, DimensionPalette dimensions) { + buffer.writeBlockPos(new BlockPos(this)); + buffer.writeVarInt(dimensions.encode(dimension)); + } + + public static TrackNodeLocation receive(FriendlyByteBuf buffer, DimensionPalette dimensions) { + TrackNodeLocation location = fromPackedPos(buffer.readBlockPos()); + location.dimension = dimensions.decode(buffer.readVarInt()); + return location; } public Collection allAdjacent() { @@ -60,12 +107,18 @@ public class TrackNodeLocation extends Vec3i { Vec3 direction; Vec3 normal; - public DiscoveredLocation(double p_121865_, double p_121866_, double p_121867_) { + public DiscoveredLocation(Level level, double p_121865_, double p_121866_, double p_121867_) { super(p_121865_, p_121866_, p_121867_); + in(level); } - public DiscoveredLocation(Vec3 vec) { + public DiscoveredLocation(ResourceKey dimension, Vec3 vec) { super(vec); + in(dimension); + } + + public DiscoveredLocation(Level level, Vec3 vec) { + this(level.dimension(), vec); } public DiscoveredLocation viaTurn(BezierConnection turn) { diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/TrackPropagator.java b/src/main/java/com/simibubi/create/content/logistics/trains/TrackPropagator.java index 2ec855cb7..02cbba4c1 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/TrackPropagator.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/TrackPropagator.java @@ -73,7 +73,7 @@ public class TrackPropagator { // 3. Ensure any affected graph gets checked for segmentation for (TrackGraph railGraph : toUpdate) - manager.updateSplitGraph(railGraph); + manager.updateSplitGraph(reader, railGraph); manager.markTracksDirty(); } @@ -235,9 +235,9 @@ public class TrackPropagator { if (location.shouldForceNode()) return true; if (next.stream() - .anyMatch(DiscoveredLocation::connectedViaTurn)) + .anyMatch(DiscoveredLocation::shouldForceNode)) return true; - + Vec3 direction = location.direction; if (direction != null && next.stream() .anyMatch(dl -> dl.notInLineWith(direction))) diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/entity/BogeyInstance.java b/src/main/java/com/simibubi/create/content/logistics/trains/entity/BogeyInstance.java index caf75f6f8..9a8a6c9c1 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/entity/BogeyInstance.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/entity/BogeyInstance.java @@ -29,10 +29,10 @@ public sealed class BogeyInstance { shafts = new ModelData[2]; materialManager.defaultSolid() - .material(Materials.TRANSFORMED) - .getModel(AllBlocks.SHAFT.getDefaultState() - .setValue(ShaftBlock.AXIS, Direction.Axis.Z)) - .createInstances(shafts); + .material(Materials.TRANSFORMED) + .getModel(AllBlocks.SHAFT.getDefaultState() + .setValue(ShaftBlock.AXIS, Direction.Axis.Z)) + .createInstances(shafts); } @@ -41,14 +41,23 @@ public sealed class BogeyInstance { shaft.delete(); } + public void hiddenFrame() { + beginFrame(0, null); + } + public void beginFrame(float wheelAngle, PoseStack ms) { + if (ms == null) { + for (int i : Iterate.zeroAndOne) + shafts[i].setEmptyTransform(); + return; + } for (int i : Iterate.zeroAndOne) shafts[i].setTransform(ms) - .translate(-.5f, .25f, i * -1) - .centre() - .rotateZ(wheelAngle) - .unCentre(); + .translate(-.5f, .25f, i * -1) + .centre() + .rotateZ(wheelAngle) + .unCentre(); } public void updateLight(BlockAndTintGetter world, CarriageContraptionEntity entity) { @@ -67,7 +76,8 @@ public sealed class BogeyInstance { public void updateLight(int blockLight, int skyLight) { for (ModelData shaft : shafts) { - shaft.setBlockLight(blockLight).setSkyLight(skyLight); + shaft.setBlockLight(blockLight) + .setSkyLight(skyLight); } } @@ -80,37 +90,46 @@ public sealed class BogeyInstance { super(bogey, materialManager); frame = materialManager.defaultSolid() - .material(Materials.TRANSFORMED) - .getModel(AllBlockPartials.BOGEY_FRAME) - .createInstance(); + .material(Materials.TRANSFORMED) + .getModel(AllBlockPartials.BOGEY_FRAME) + .createInstance(); wheels = new ModelData[2]; materialManager.defaultSolid() - .material(Materials.TRANSFORMED) - .getModel(AllBlockPartials.SMALL_BOGEY_WHEELS) - .createInstances(wheels); + .material(Materials.TRANSFORMED) + .getModel(AllBlockPartials.SMALL_BOGEY_WHEELS) + .createInstances(wheels); } @Override public void beginFrame(float wheelAngle, PoseStack ms) { super.beginFrame(wheelAngle, ms); + if (ms == null) { + frame.setEmptyTransform(); + for (int side : Iterate.positiveAndNegative) + wheels[(side + 1) / 2].setEmptyTransform(); + return; + } + frame.setTransform(ms); for (int side : Iterate.positiveAndNegative) { wheels[(side + 1) / 2].setTransform(ms) - .translate(0, 12 / 16f, side) - .rotateX(wheelAngle); + .translate(0, 12 / 16f, side) + .rotateX(wheelAngle); } } @Override public void updateLight(int blockLight, int skyLight) { super.updateLight(blockLight, skyLight); - frame.setBlockLight(blockLight).setSkyLight(skyLight); + frame.setBlockLight(blockLight) + .setSkyLight(skyLight); for (ModelData wheel : wheels) - wheel.setBlockLight(blockLight).setSkyLight(skyLight); + wheel.setBlockLight(blockLight) + .setSkyLight(skyLight); } @Override @@ -133,58 +152,73 @@ public sealed class BogeyInstance { public Drive(CarriageBogey bogey, MaterialManager materialManager) { super(bogey, materialManager); Material mat = materialManager.defaultSolid() - .material(Materials.TRANSFORMED); + .material(Materials.TRANSFORMED); secondShaft = new ModelData[2]; mat.getModel(AllBlocks.SHAFT.getDefaultState() - .setValue(ShaftBlock.AXIS, Direction.Axis.X)) - .createInstances(secondShaft); + .setValue(ShaftBlock.AXIS, Direction.Axis.X)) + .createInstances(secondShaft); drive = mat.getModel(AllBlockPartials.BOGEY_DRIVE) - .createInstance(); + .createInstance(); piston = mat.getModel(AllBlockPartials.BOGEY_PISTON) - .createInstance(); + .createInstance(); wheels = mat.getModel(AllBlockPartials.LARGE_BOGEY_WHEELS) - .createInstance(); + .createInstance(); pin = mat.getModel(AllBlockPartials.BOGEY_PIN) - .createInstance(); + .createInstance(); } @Override public void beginFrame(float wheelAngle, PoseStack ms) { super.beginFrame(wheelAngle, ms); + if (ms == null) { + for (int i : Iterate.zeroAndOne) + secondShaft[i].setEmptyTransform(); + drive.setEmptyTransform(); + piston.setEmptyTransform(); + wheels.setEmptyTransform(); + pin.setEmptyTransform(); + return; + } + for (int i : Iterate.zeroAndOne) secondShaft[i].setTransform(ms) - .translate(-.5f, .25f, .5f + i * -2) - .centre() - .rotateX(wheelAngle) - .unCentre(); + .translate(-.5f, .25f, .5f + i * -2) + .centre() + .rotateX(wheelAngle) + .unCentre(); drive.setTransform(ms); piston.setTransform(ms) - .translate(0, 0, 1 / 4f * Math.sin(AngleHelper.rad(wheelAngle))); + .translate(0, 0, 1 / 4f * Math.sin(AngleHelper.rad(wheelAngle))); wheels.setTransform(ms) - .translate(0, 1, 0) - .rotateX(wheelAngle); + .translate(0, 1, 0) + .rotateX(wheelAngle); pin.setTransform(ms) - .translate(0, 1, 0) - .rotateX(wheelAngle) - .translate(0, 1 / 4f, 0) - .rotateX(-wheelAngle); + .translate(0, 1, 0) + .rotateX(wheelAngle) + .translate(0, 1 / 4f, 0) + .rotateX(-wheelAngle); } @Override public void updateLight(int blockLight, int skyLight) { super.updateLight(blockLight, skyLight); for (ModelData shaft : secondShaft) - shaft.setBlockLight(blockLight).setSkyLight(skyLight); - drive.setBlockLight(blockLight).setSkyLight(skyLight); - piston.setBlockLight(blockLight).setSkyLight(skyLight); - wheels.setBlockLight(blockLight).setSkyLight(skyLight); - pin.setBlockLight(blockLight).setSkyLight(skyLight); + shaft.setBlockLight(blockLight) + .setSkyLight(skyLight); + drive.setBlockLight(blockLight) + .setSkyLight(skyLight); + piston.setBlockLight(blockLight) + .setSkyLight(skyLight); + wheels.setBlockLight(blockLight) + .setSkyLight(skyLight); + pin.setBlockLight(blockLight) + .setSkyLight(skyLight); } @Override diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/entity/Carriage.java b/src/main/java/com/simibubi/create/content/logistics/trains/entity/Carriage.java index 818ac2972..ad8af7f94 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/entity/Carriage.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/entity/Carriage.java @@ -2,25 +2,41 @@ package com.simibubi.create.content.logistics.trains.entity; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Objects; +import java.util.Set; +import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; import java.util.function.Function; import javax.annotation.Nullable; import org.apache.commons.lang3.mutable.MutableDouble; +import com.simibubi.create.content.contraptions.components.structureMovement.Contraption; +import com.simibubi.create.content.contraptions.components.structureMovement.render.ContraptionRenderDispatcher; +import com.simibubi.create.content.logistics.trains.DimensionPalette; import com.simibubi.create.content.logistics.trains.TrackGraph; +import com.simibubi.create.content.logistics.trains.TrackNodeLocation; import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.IEdgePointListener; import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.ITrackSelector; import com.simibubi.create.foundation.utility.Couple; import com.simibubi.create.foundation.utility.Iterate; +import com.simibubi.create.foundation.utility.NBTHelper; import com.simibubi.create.foundation.utility.VecHelper; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.Tag; +import net.minecraft.resources.ResourceKey; import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; import net.minecraft.util.Mth; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity.RemovalReason; @@ -28,6 +44,9 @@ import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.fml.DistExecutor; public class Carriage { @@ -42,29 +61,21 @@ public class Carriage { public int bogeySpacing; public Couple bogeys; - public Vec3 positionAnchor; - public Couple rotationAnchors; - CompoundTag serialisedEntity; - WeakReference entity; - - // client - public boolean pointsInitialised; + Map serialisedPassengers; + private Map, DimensionalCarriageEntity> entities; static final int FIRST = 0, MIDDLE = 1, LAST = 2, BOTH = 3; public Carriage(CarriageBogey bogey1, @Nullable CarriageBogey bogey2, int bogeySpacing) { this.bogeySpacing = bogeySpacing; this.bogeys = Couple.create(bogey1, bogey2); - this.entity = new WeakReference<>(null); this.id = netIdGenerator.incrementAndGet(); this.serialisedEntity = new CompoundTag(); - this.pointsInitialised = false; - this.rotationAnchors = Couple.create(null, null); this.presentConductors = Couple.create(false, false); - - updateContraptionAnchors(); + this.serialisedPassengers = new HashMap<>(); + this.entities = new HashMap<>(); bogey1.setLeading(); bogey1.carriage = this; @@ -76,13 +87,18 @@ public class Carriage { this.train = train; } + public boolean presentInMultipleDimensions() { + return entities.size() > 1; + } + public void setContraption(Level level, CarriageContraption contraption) { CarriageContraptionEntity entity = CarriageContraptionEntity.create(level, contraption); entity.setCarriage(this); contraption.startMoving(level); contraption.onEntityInitialize(level, entity); updateContraptionAnchors(); - alignEntity(entity); + + getDimensional(level).alignEntity(entity); List players = new ArrayList<>(); for (Entity passenger : entity.getPassengers()) @@ -96,11 +112,24 @@ public class Carriage { serialisedEntity = entity.serializeNBT(); } + public DimensionalCarriageEntity getDimensional(Level level) { + return getDimensional(level.dimension()); + } + + public DimensionalCarriageEntity getDimensional(ResourceKey dimension) { + return entities.computeIfAbsent(dimension, $ -> new DimensionalCarriageEntity()); + } + + @Nullable + public DimensionalCarriageEntity getDimensionalIfPresent(ResourceKey dimension) { + return entities.get(dimension); + } + public double travel(Level level, TrackGraph graph, double distance, Function forwardControl, Function backwardControl, int type) { boolean onTwoBogeys = isOnTwoBogeys(); - double stress = onTwoBogeys ? bogeySpacing - leadingAnchor().distanceTo(trailingAnchor()) : 0; + double stress = onTwoBogeys ? bogeySpacing - getAnchorDiff() : 0; blocked = false; MutableDouble distanceMoved = new MutableDouble(distance); @@ -140,12 +169,23 @@ public class Carriage { IEdgePointListener passiveListener = point.ignoreEdgePoints(); toMove += correction + bogeyCorrection; - double moved = - point - .travel(graph, toMove, toMove > 0 ? frontTrackSelector : backTrackSelector, - toMove > 0 ? atFront ? frontListener : atBack ? backListener : passiveListener - : atFront ? backListener : atBack ? frontListener : passiveListener, - point.ignoreTurns()); + + ITrackSelector trackSelector = toMove > 0 ? frontTrackSelector : backTrackSelector; + IEdgePointListener signalListener = + toMove > 0 ? atFront ? frontListener : atBack ? backListener : passiveListener + : atFront ? backListener : atBack ? frontListener : passiveListener; + + double moved = point.travel(graph, toMove, trackSelector, signalListener, point.ignoreTurns(), c -> { + for (DimensionalCarriageEntity dce : entities.values()) + if (c.either(tnl -> tnl.equals(dce.pivot))) + return false; + if (entities.size() > 1) { + train.status.doublePortal(); + return true; + } + return false; + }); + blocked |= point.blocked; distanceMoved.setValue(moved); @@ -153,98 +193,185 @@ public class Carriage { } updateContraptionAnchors(); - manageEntity(level); + manageEntities(level); return distanceMoved.getValue(); } - public void updateConductors() { - CarriageContraptionEntity entity = this.entity.get(); - if (entity != null && entity.isAlive()) - presentConductors = entity.checkConductors(); + private double getAnchorDiff() { + double diff = 0; + int entries = 0; + + TravellingPoint leadingPoint = getLeadingPoint(); + TravellingPoint trailingPoint = getTrailingPoint(); + if (leadingPoint.node1 != null && trailingPoint.node1 != null) + if (!leadingPoint.node1.getLocation().dimension.equals(trailingPoint.node1.getLocation().dimension)) + return bogeySpacing; + + for (DimensionalCarriageEntity dce : entities.values()) + if (dce.leadingAnchor() != null && dce.trailingAnchor() != null) { + entries++; + diff += dce.leadingAnchor() + .distanceTo(dce.trailingAnchor()); + } + + if (entries == 0) + return bogeySpacing; + return diff / entries; } - public void createEntity(Level level) { - Entity entity = EntityType.loadEntityRecursive(serialisedEntity, level, e -> { - e.moveTo(positionAnchor); - return e; + public void updateConductors() { + if (anyAvailableEntity() == null || entities.size() > 1) + return; + presentConductors.replace($ -> false); + for (DimensionalCarriageEntity dimensionalCarriageEntity : entities.values()) { + CarriageContraptionEntity entity = dimensionalCarriageEntity.entity.get(); + if (entity != null && entity.isAlive()) + presentConductors.replaceWithParams((current, checked) -> current || checked, entity.checkConductors()); + } + } + + private Set> currentlyTraversedDimensions = new HashSet<>(); + + public void manageEntities(Level level) { + currentlyTraversedDimensions.clear(); + + bogeys.forEach(cb -> { + if (cb == null) + return; + cb.points.forEach(tp -> { + if (tp.node1 == null) + return; + currentlyTraversedDimensions.add(tp.node1.getLocation().dimension); + }); }); - if (!(entity instanceof CarriageContraptionEntity cce)) - return; + for (Iterator, DimensionalCarriageEntity>> iterator = entities.entrySet() + .iterator(); iterator.hasNext();) { + Entry, DimensionalCarriageEntity> entry = iterator.next(); - cce.setGraph(train.graph == null ? null : train.graph.id); - cce.setCarriage(this); - cce.syncCarriage(); + boolean discard = + !currentlyTraversedDimensions.isEmpty() && !currentlyTraversedDimensions.contains(entry.getKey()); + ServerLevel currentLevel = level.getServer() + .getLevel(entry.getKey()); + if (currentLevel == null) + continue; - this.entity = new WeakReference<>(cce); + DimensionalCarriageEntity dimensionalCarriageEntity = entry.getValue(); + CarriageContraptionEntity entity = dimensionalCarriageEntity.entity.get(); - if (level instanceof ServerLevel sl) - sl.tryAddFreshEntityWithPassengers(entity); - } + if (entity == null) { + if (discard) + iterator.remove(); + else if (dimensionalCarriageEntity.positionAnchor != null && CarriageEntityHandler + .isActiveChunk(currentLevel, new BlockPos(dimensionalCarriageEntity.positionAnchor))) + dimensionalCarriageEntity.createEntity(currentLevel, anyAvailableEntity() == null); - public void manageEntity(Level level) { - CarriageContraptionEntity entity = this.entity.get(); - if (entity == null) { - if (CarriageEntityHandler.isActiveChunk(level, new BlockPos(positionAnchor))) - createEntity(level); - } else { - CarriageEntityHandler.validateCarriageEntity(entity); - if (!entity.isAlive() || entity.leftTickingChunks) { - removeAndSaveEntity(entity); - return; + } else { + if (discard) { + discard = dimensionalCarriageEntity.discardTicks > 3; + dimensionalCarriageEntity.discardTicks++; + } else + dimensionalCarriageEntity.discardTicks = 0; + + CarriageEntityHandler.validateCarriageEntity(entity); + if (!entity.isAlive() || entity.leftTickingChunks || discard) { + dimensionalCarriageEntity.removeAndSaveEntity(entity, discard); + if (discard) + iterator.remove(); + continue; + } + } + + entity = dimensionalCarriageEntity.entity.get(); + if (entity != null && dimensionalCarriageEntity.positionAnchor != null) { + dimensionalCarriageEntity.alignEntity(entity); + entity.syncCarriage(); } } - entity = this.entity.get(); - if (entity == null) - return; - - alignEntity(entity); - entity.syncCarriage(); - } - - private void removeAndSaveEntity(CarriageContraptionEntity entity) { - serialisedEntity = entity.serializeNBT(); - - for (Entity passenger : entity.getPassengers()) - if (!(passenger instanceof Player)) - passenger.discard(); - entity.discard(); - - this.entity.clear(); } public void updateContraptionAnchors() { CarriageBogey leadingBogey = leadingBogey(); if (leadingBogey.points.either(t -> t.edge == null)) return; - positionAnchor = leadingBogey.getAnchorPosition(); - rotationAnchors = bogeys.mapWithContext((b, first) -> isOnTwoBogeys() ? b.getAnchorPosition() - : leadingBogey.points.get(first) - .getPosition()); + CarriageBogey trailingBogey = trailingBogey(); + if (trailingBogey.points.either(t -> t.edge == null)) + return; + + ResourceKey leadingBogeyDim = leadingBogey.getDimension(); + ResourceKey trailingBogeyDim = trailingBogey.getDimension(); + double leadingWheelSpacing = leadingBogey.type.getWheelPointSpacing(); + double trailingWheelSpacing = trailingBogey.type.getWheelPointSpacing(); + + for (boolean leading : Iterate.trueAndFalse) { + TravellingPoint point = leading ? getLeadingPoint() : getTrailingPoint(); + TravellingPoint otherPoint = !leading ? getLeadingPoint() : getTrailingPoint(); + ResourceKey dimension = point.node1.getLocation().dimension; + ResourceKey otherDimension = otherPoint.node1.getLocation().dimension; + + if (dimension.equals(otherDimension) && leading) { + getDimensional(dimension).discardPivot(); + continue; + } + + DimensionalCarriageEntity dce = getDimensional(dimension); + + dce.positionAnchor = dimension.equals(leadingBogeyDim) ? leadingBogey.getAnchorPosition() + : pivoted(dce, dimension, point, + leading ? leadingWheelSpacing / 2 : bogeySpacing + trailingWheelSpacing / 2); + + int prevmin = dce.minAllowedLocalCoord(); + int prevmax = dce.maxAllowedLocalCoord(); + + dce.updateCutoff(leading); + if (prevmin != dce.minAllowedLocalCoord() || prevmax != dce.maxAllowedLocalCoord()) { + dce.updateRenderedCutoff(); + dce.updatePassengerLoadout(); + } + + if (isOnTwoBogeys()) { + dce.rotationAnchors.setFirst(dimension.equals(leadingBogeyDim) ? leadingBogey.getAnchorPosition() + : pivoted(dce, dimension, point, + leading ? leadingWheelSpacing / 2 : bogeySpacing + trailingWheelSpacing / 2)); + dce.rotationAnchors.setSecond(dimension.equals(trailingBogeyDim) ? trailingBogey.getAnchorPosition() + : pivoted(dce, dimension, point, + leading ? leadingWheelSpacing / 2 + bogeySpacing : trailingWheelSpacing / 2)); + continue; + } + + if (dimension.equals(otherDimension)) { + dce.rotationAnchors = leadingBogey.points.map(TravellingPoint::getPosition); + continue; + } + + dce.rotationAnchors.setFirst(leadingBogey.points.getFirst() == point ? point.getPosition() + : pivoted(dce, dimension, point, leadingWheelSpacing)); + dce.rotationAnchors.setSecond(leadingBogey.points.getSecond() == point ? point.getPosition() + : pivoted(dce, dimension, point, leadingWheelSpacing)); + } + } - public void alignEntity(CarriageContraptionEntity entity) { - Vec3 positionVec = rotationAnchors.getFirst(); - Vec3 coupledVec = rotationAnchors.getSecond(); + private Vec3 pivoted(DimensionalCarriageEntity dce, ResourceKey dimension, TravellingPoint start, + double offset) { + if (train.graph == null) + return dce.pivot == null ? null : dce.pivot.getLocation(); + TrackNodeLocation pivot = dce.findPivot(dimension, start == getLeadingPoint()); + if (pivot == null) + return null; + Vec3 startVec = start.getPosition(); + Vec3 portalVec = pivot.getLocation() + .add(0, 1, 0); + return VecHelper.lerp((float) (offset / startVec.distanceTo(portalVec)), startVec, portalVec); + } - double diffX = positionVec.x - coupledVec.x; - double diffY = positionVec.y - coupledVec.y; - double diffZ = positionVec.z - coupledVec.z; - - entity.setPos(positionAnchor); - entity.prevYaw = entity.yaw; - entity.prevPitch = entity.pitch; - - entity.yaw = (float) (Mth.atan2(diffZ, diffX) * 180 / Math.PI) + 180; - entity.pitch = (float) (Math.atan2(diffY, Math.sqrt(diffX * diffX + diffZ * diffZ)) * 180 / Math.PI) * -1; - - if (entity.firstPositionUpdate) { - entity.xo = entity.getX(); - entity.yo = entity.getY(); - entity.zo = entity.getZ(); - entity.prevYaw = entity.yaw; - entity.prevPitch = entity.pitch; + public void alignEntity(Level level) { + DimensionalCarriageEntity dimensionalCarriageEntity = entities.get(level.dimension()); + if (dimensionalCarriageEntity != null) { + CarriageContraptionEntity entity = dimensionalCarriageEntity.entity.get(); + if (entity != null) + dimensionalCarriageEntity.alignEntity(entity); } } @@ -268,41 +395,79 @@ public class Carriage { return bogeys.getSecond() != null; } - public Vec3 leadingAnchor() { - return isOnTwoBogeys() ? rotationAnchors.getFirst() : positionAnchor; + public CarriageContraptionEntity anyAvailableEntity() { + for (DimensionalCarriageEntity dimensionalCarriageEntity : entities.values()) { + CarriageContraptionEntity entity = dimensionalCarriageEntity.entity.get(); + if (entity != null) + return entity; + } + return null; } - public Vec3 trailingAnchor() { - return isOnTwoBogeys() ? rotationAnchors.getSecond() : positionAnchor; + public void forEachPresentEntity(Consumer callback) { + for (DimensionalCarriageEntity dimensionalCarriageEntity : entities.values()) { + CarriageContraptionEntity entity = dimensionalCarriageEntity.entity.get(); + if (entity != null) + callback.accept(entity); + } } - public CompoundTag write() { + public CompoundTag write(DimensionPalette dimensions) { CompoundTag tag = new CompoundTag(); tag.put("FirstBogey", bogeys.getFirst() - .write()); + .write(dimensions)); if (isOnTwoBogeys()) tag.put("SecondBogey", bogeys.getSecond() - .write()); + .write(dimensions)); tag.putInt("Spacing", bogeySpacing); tag.putBoolean("FrontConductor", presentConductors.getFirst()); tag.putBoolean("BackConductor", presentConductors.getSecond()); tag.putBoolean("Stalled", stalled); - CarriageContraptionEntity entity = this.entity.get(); - if (entity != null) - serialisedEntity = entity.serializeNBT(); + Map passengerMap = new HashMap<>(); + + for (DimensionalCarriageEntity dimensionalCarriageEntity : entities.values()) { + CarriageContraptionEntity entity = dimensionalCarriageEntity.entity.get(); + if (entity == null) + continue; + serialize(entity); + Contraption contraption = entity.getContraption(); + if (contraption == null) + continue; + Map mapping = contraption.getSeatMapping(); + for (Entity passenger : entity.getPassengers()) + if (mapping.containsKey(passenger.getUUID())) + passengerMap.put(mapping.get(passenger.getUUID()), passenger.serializeNBT()); + } tag.put("Entity", serialisedEntity.copy()); - tag.put("PositionAnchor", VecHelper.writeNBT(positionAnchor)); - tag.put("RotationAnchors", rotationAnchors.serializeEach(VecHelper::writeNBTCompound)); + + CompoundTag passengerTag = new CompoundTag(); + passengerMap.putAll(serialisedPassengers); + passengerMap.forEach((seat, nbt) -> passengerTag.put("Seat" + seat, nbt.copy())); + tag.put("Passengers", passengerTag); + + tag.put("EntityPositioning", NBTHelper.writeCompoundList(entities.entrySet(), e -> { + CompoundTag c = e.getValue() + .write(); + c.putInt("Dim", dimensions.encode(e.getKey())); + return c; + })); return tag; } - public static Carriage read(CompoundTag tag, TrackGraph graph) { - CarriageBogey bogey1 = CarriageBogey.read(tag.getCompound("FirstBogey"), graph); + private void serialize(Entity entity) { + serialisedEntity = entity.serializeNBT(); + serialisedEntity.remove("Passengers"); + serialisedEntity.getCompound("Contraption") + .remove("Passengers"); + } + + public static Carriage read(CompoundTag tag, TrackGraph graph, DimensionPalette dimensions) { + CarriageBogey bogey1 = CarriageBogey.read(tag.getCompound("FirstBogey"), graph, dimensions); CarriageBogey bogey2 = - tag.contains("SecondBogey") ? CarriageBogey.read(tag.getCompound("SecondBogey"), graph) : null; + tag.contains("SecondBogey") ? CarriageBogey.read(tag.getCompound("SecondBogey"), graph, dimensions) : null; Carriage carriage = new Carriage(bogey1, bogey2, tag.getInt("Spacing")); @@ -311,13 +476,373 @@ public class Carriage { carriage.serialisedEntity = tag.getCompound("Entity") .copy(); - if (carriage.positionAnchor == null) { - carriage.positionAnchor = VecHelper.readNBT(tag.getList("PositionAnchor", Tag.TAG_DOUBLE)); - carriage.rotationAnchors = - Couple.deserializeEach(tag.getList("RotationAnchors", Tag.TAG_COMPOUND), VecHelper::readNBTCompound); - } + NBTHelper.iterateCompoundList(tag.getList("EntityPositioning", Tag.TAG_COMPOUND), + c -> carriage.getDimensional(dimensions.decode(c.getInt("Dim"))) + .read(c)); + + CompoundTag passengersTag = tag.getCompound("Passengers"); + passengersTag.getAllKeys() + .forEach(key -> carriage.serialisedPassengers.put(Integer.valueOf(key.substring(4)), + passengersTag.getCompound(key))); return carriage; } + private TravellingPoint portalScout = new TravellingPoint(); + + public class DimensionalCarriageEntity { + public Vec3 positionAnchor; + public Couple rotationAnchors; + public WeakReference entity; + + public TrackNodeLocation pivot; + int discardTicks; + + // 0 == whole, 0..1 = fading out, -1..0 = fading in + public float cutoff; + + // client + public boolean pointsInitialised; + + public DimensionalCarriageEntity() { + this.entity = new WeakReference<>(null); + this.rotationAnchors = Couple.create(null, null); + this.pointsInitialised = false; + } + + public void discardPivot() { + float prevCutoff = cutoff; + cutoff = 0; + pivot = null; + if (!serialisedPassengers.isEmpty() || !Mth.equal(prevCutoff, cutoff)) { + updatePassengerLoadout(); + updateRenderedCutoff(); + } + } + + public void updateCutoff(boolean leadingIsCurrent) { + Vec3 leadingAnchor = rotationAnchors.getFirst(); + Vec3 trailingAnchor = rotationAnchors.getSecond(); + + if (leadingAnchor == null || trailingAnchor == null) + return; + if (pivot == null) { + cutoff = 0; + return; + } + + Vec3 pivotLoc = pivot.getLocation() + .add(0, 1, 0); + + double leadingSpacing = leadingBogey().type.getWheelPointSpacing() / 2; + double trailingSpacing = trailingBogey().type.getWheelPointSpacing() / 2; + double anchorSpacing = leadingSpacing + bogeySpacing + trailingSpacing; + + if (isOnTwoBogeys()) { + Vec3 diff = trailingAnchor.subtract(leadingAnchor) + .normalize(); + trailingAnchor = trailingAnchor.add(diff.scale(trailingSpacing)); + leadingAnchor = leadingAnchor.add(diff.scale(-leadingSpacing)); + } + + double leadingDiff = leadingAnchor.distanceTo(pivotLoc); + double trailingDiff = trailingAnchor.distanceTo(pivotLoc); + + leadingDiff /= anchorSpacing; + trailingDiff /= anchorSpacing; + + if (leadingIsCurrent && leadingDiff > trailingDiff && leadingDiff > 1) + cutoff = 0; + else if (leadingIsCurrent && leadingDiff < trailingDiff && trailingDiff > 1) + cutoff = 1; + else if (!leadingIsCurrent && leadingDiff > trailingDiff && leadingDiff > 1) + cutoff = -1; + else if (!leadingIsCurrent && leadingDiff < trailingDiff && trailingDiff > 1) + cutoff = 0; + else + cutoff = (float) Mth.clamp(1 - (leadingIsCurrent ? leadingDiff : trailingDiff), 0, 1) + * (leadingIsCurrent ? 1 : -1); + } + + public TrackNodeLocation findPivot(ResourceKey dimension, boolean leading) { + if (pivot != null) + return pivot; + + TravellingPoint start = leading ? getLeadingPoint() : getTrailingPoint(); + TravellingPoint end = !leading ? getLeadingPoint() : getTrailingPoint(); + + portalScout.node1 = start.node1; + portalScout.node2 = start.node2; + portalScout.edge = start.edge; + portalScout.position = start.position; + + ITrackSelector trackSelector = portalScout.follow(end); + int distance = bogeySpacing + 10; + int direction = leading ? -1 : 1; + + portalScout.travel(train.graph, direction * distance, trackSelector, portalScout.ignoreEdgePoints(), + portalScout.ignoreTurns(), nodes -> { + for (boolean b : Iterate.trueAndFalse) + if (nodes.get(b).dimension.equals(dimension)) + pivot = nodes.get(b); + return true; + }); + + return pivot; + } + + public CompoundTag write() { + CompoundTag tag = new CompoundTag(); + tag.putFloat("Cutoff", cutoff); + tag.putInt("DiscardTicks", discardTicks); + if (pivot != null) + tag.put("Pivot", pivot.write(null)); + if (positionAnchor != null) + tag.put("PositionAnchor", VecHelper.writeNBT(positionAnchor)); + if (rotationAnchors.both(Objects::nonNull)) + tag.put("RotationAnchors", rotationAnchors.serializeEach(VecHelper::writeNBTCompound)); + return tag; + } + + public void read(CompoundTag tag) { + cutoff = tag.getFloat("Cutoff"); + discardTicks = tag.getInt("DiscardTicks"); + if (tag.contains("Pivot")) + pivot = TrackNodeLocation.read(tag.getCompound("Pivot"), null); + if (positionAnchor != null) + return; + if (tag.contains("PositionAnchor")) + positionAnchor = VecHelper.readNBT(tag.getList("PositionAnchor", Tag.TAG_DOUBLE)); + if (tag.contains("RotationAnchors")) + rotationAnchors = Couple.deserializeEach(tag.getList("RotationAnchors", Tag.TAG_COMPOUND), + VecHelper::readNBTCompound); + } + + public Vec3 leadingAnchor() { + return isOnTwoBogeys() ? rotationAnchors.getFirst() : positionAnchor; + } + + public Vec3 trailingAnchor() { + return isOnTwoBogeys() ? rotationAnchors.getSecond() : positionAnchor; + } + + public int minAllowedLocalCoord() { + if (cutoff <= 0) + return Integer.MIN_VALUE; + if (cutoff >= 1) + return Integer.MAX_VALUE; + return Mth.floor(-bogeySpacing + -1 + (2 + bogeySpacing) * cutoff); + } + + public int maxAllowedLocalCoord() { + if (cutoff >= 0) + return Integer.MAX_VALUE; + if (cutoff <= -1) + return Integer.MIN_VALUE; + return Mth.ceil(-bogeySpacing + -1 + (2 + bogeySpacing) * (cutoff + 1)); + } + + public void updatePassengerLoadout() { + Entity entity = this.entity.get(); + if (!(entity instanceof CarriageContraptionEntity cce)) + return; + if (!(entity.level instanceof ServerLevel sLevel)) + return; + + Set loadedPassengers = new HashSet<>(); + int min = minAllowedLocalCoord(); + int max = maxAllowedLocalCoord(); + + for (Entry entry : serialisedPassengers.entrySet()) { + Integer seatId = entry.getKey(); + List seats = cce.getContraption() + .getSeats(); + if (seatId >= seats.size()) + continue; + + BlockPos localPos = seats.get(seatId); + if (!cce.isLocalCoordWithin(localPos, min, max)) + continue; + + CompoundTag tag = entry.getValue(); + Entity passenger = null; + + if (tag.contains("PlayerPassenger")) { + passenger = sLevel.getServer() + .getPlayerList() + .getPlayer(tag.getUUID("PlayerPassenger")); + + } else { + passenger = EntityType.loadEntityRecursive(tag, entity.level, e -> { + e.moveTo(positionAnchor); + return e; + }); + if (passenger != null) + sLevel.tryAddFreshEntityWithPassengers(passenger); + } + + if (passenger != null) { + ResourceKey passengerDimension = passenger.level.dimension(); + if (!passengerDimension.equals(sLevel.dimension()) && passenger instanceof ServerPlayer sp) + continue; + cce.addSittingPassenger(passenger, seatId); + } + + loadedPassengers.add(seatId); + } + + loadedPassengers.forEach(serialisedPassengers::remove); + + Map mapping = cce.getContraption() + .getSeatMapping(); + for (Entity passenger : entity.getPassengers()) { + BlockPos localPos = cce.getContraption() + .getSeatOf(passenger.getUUID()); + if (cce.isLocalCoordWithin(localPos, min, max)) + continue; + if (!mapping.containsKey(passenger.getUUID())) + continue; + + Integer seat = mapping.get(passenger.getUUID()); + if ((passenger instanceof ServerPlayer sp)) { + dismountPlayer(sLevel, sp, seat, true); + continue; + } + + serialisedPassengers.put(seat, passenger.serializeNBT()); + passenger.discard(); + } + + } + + private void dismountPlayer(ServerLevel sLevel, ServerPlayer sp, Integer seat, boolean portal) { + if (!portal) { + sp.stopRiding(); + return; + } + + CompoundTag tag = new CompoundTag(); + tag.putUUID("PlayerPassenger", sp.getUUID()); + serialisedPassengers.put(seat, tag); + sp.stopRiding(); + sp.getPersistentData() + .remove("ContraptionDismountLocation"); + + for (Entry, DimensionalCarriageEntity> other : entities.entrySet()) { + DimensionalCarriageEntity otherDce = other.getValue(); + if (otherDce == this) + continue; + if (sp.level.dimension() + .equals(other.getKey())) + continue; + if (otherDce.pivot == null) + continue; + Vec3 loc = otherDce.pivot.getLocation(); + ServerLevel level = sLevel.getServer() + .getLevel(other.getKey()); + sp.teleportTo(level, loc.x, loc.y, loc.z, sp.getYRot(), sp.getXRot()); + sp.setPortalCooldown(); + } + } + + public void updateRenderedCutoff() { + Entity entity = this.entity.get(); + if (!(entity instanceof CarriageContraptionEntity cce)) + return; + if (!entity.level.isClientSide()) + return; + Contraption contraption = cce.getContraption(); + if (!(contraption instanceof CarriageContraption cc)) + return; + cc.portalCutoffMin = minAllowedLocalCoord(); + cc.portalCutoffMax = maxAllowedLocalCoord(); + DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> invalidate(cce)); + } + + @OnlyIn(Dist.CLIENT) + private void invalidate(CarriageContraptionEntity entity) { + ContraptionRenderDispatcher.invalidate(entity.getContraption()); + entity.updateRenderedPortalCutoff(); + } + + private void createEntity(Level level, boolean loadPassengers) { + Entity entity = EntityType.loadEntityRecursive(serialisedEntity, level, e -> { + e.moveTo(positionAnchor); + return e; + }); + + if (!(entity instanceof CarriageContraptionEntity cce)) { + train.invalid = true; + return; + } + + cce.setGraph(train.graph == null ? null : train.graph.id); + cce.setCarriage(Carriage.this); + cce.syncCarriage(); + + this.entity = new WeakReference<>(cce); + + if (level instanceof ServerLevel sl) + sl.tryAddFreshEntityWithPassengers(entity); + + updatePassengerLoadout(); + } + + private void removeAndSaveEntity(CarriageContraptionEntity entity, boolean portal) { + Contraption contraption = entity.getContraption(); + if (contraption != null) { + Map mapping = contraption.getSeatMapping(); + for (Entity passenger : entity.getPassengers()) { + if (!mapping.containsKey(passenger.getUUID())) + continue; + + Integer seat = mapping.get(passenger.getUUID()); + + if (passenger instanceof ServerPlayer sp) { + dismountPlayer(sp.getLevel(), sp, seat, portal); + continue; + } + + serialisedPassengers.put(seat, passenger.serializeNBT()); + } + } + + for (Entity passenger : entity.getPassengers()) + if (!(passenger instanceof Player)) + passenger.discard(); + + serialize(entity); + entity.discard(); + this.entity.clear(); + } + + public void alignEntity(CarriageContraptionEntity entity) { + if (rotationAnchors.either(Objects::isNull)) + return; + + Vec3 positionVec = rotationAnchors.getFirst(); + Vec3 coupledVec = rotationAnchors.getSecond(); + + double diffX = positionVec.x - coupledVec.x; + double diffY = positionVec.y - coupledVec.y; + double diffZ = positionVec.z - coupledVec.z; + + entity.setPos(positionAnchor); + entity.prevYaw = entity.yaw; + entity.prevPitch = entity.pitch; + + entity.yaw = (float) (Mth.atan2(diffZ, diffX) * 180 / Math.PI) + 180; + entity.pitch = (float) (Math.atan2(diffY, Math.sqrt(diffX * diffX + diffZ * diffZ)) * 180 / Math.PI) * -1; + + if (!entity.firstPositionUpdate) + return; + + entity.xo = entity.getX(); + entity.yo = entity.getY(); + entity.zo = entity.getZ(); + entity.prevYaw = entity.yaw; + entity.prevPitch = entity.pitch; + } + } + } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageBogey.java b/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageBogey.java index dd1285718..381a800fc 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageBogey.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageBogey.java @@ -4,6 +4,7 @@ import javax.annotation.Nullable; import com.jozufozu.flywheel.api.MaterialManager; import com.simibubi.create.Create; +import com.simibubi.create.content.logistics.trains.DimensionPalette; import com.simibubi.create.content.logistics.trains.IBogeyBlock; import com.simibubi.create.content.logistics.trains.TrackGraph; import com.simibubi.create.foundation.utility.AngleHelper; @@ -15,8 +16,10 @@ import com.simibubi.create.foundation.utility.animation.LerpedFloat; import net.minecraft.core.Direction.Axis; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.Tag; +import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.Mth; +import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; import net.minecraft.world.phys.Vec3; import net.minecraftforge.registries.ForgeRegistries; @@ -48,6 +51,20 @@ public class CarriageBogey { couplingAnchors = Couple.create(null, null); } + public ResourceKey getDimension() { + TravellingPoint leading = leading(); + TravellingPoint trailing = trailing(); + if (leading.edge == null || trailing.edge == null) + return null; + if (leading.edge.isInterDimensional() || trailing.edge.isInterDimensional()) + return null; + ResourceKey dimension1 = leading.node1.getLocation().dimension; + ResourceKey dimension2 = trailing.node1.getLocation().dimension; + if (dimension1.equals(dimension2)) + return dimension1; + return null; + } + public void updateAngles(CarriageContraptionEntity entity, double distanceMoved) { double angleDiff = 360 * distanceMoved / (Math.PI * 2 * type.getWheelRadius()); @@ -56,6 +73,10 @@ public class CarriageBogey { if (leading().edge == null || carriage.train.derailed) { yRot = -90 + entity.yaw - derailAngle; + } else if (!entity.level.dimension() + .equals(getDimension())) { + yRot = -90 + entity.yaw; + xRot = 0; } else { Vec3 positionVec = leading().getPosition(); Vec3 coupledVec = trailing().getPosition(); @@ -86,6 +107,8 @@ public class CarriageBogey { } public double getStress() { + if (getDimension() == null) + return 0; return type.getWheelPointSpacing() - leading().getPosition() .distanceTo(trailing().getPosition()); } @@ -119,19 +142,19 @@ public class CarriageBogey { couplingAnchors.set(leading, entityPos.add(thisOffset)); } - public CompoundTag write() { + public CompoundTag write(DimensionPalette dimensions) { CompoundTag tag = new CompoundTag(); tag.putString("Type", ((Block) type).getRegistryName() .toString()); - tag.put("Points", points.serializeEach(TravellingPoint::write)); + tag.put("Points", points.serializeEach(tp -> tp.write(dimensions))); return tag; } - public static CarriageBogey read(CompoundTag tag, TrackGraph graph) { + public static CarriageBogey read(CompoundTag tag, TrackGraph graph, DimensionPalette dimensions) { ResourceLocation location = new ResourceLocation(tag.getString("Type")); IBogeyBlock type = (IBogeyBlock) ForgeRegistries.BLOCKS.getValue(location); - Couple points = - Couple.deserializeEach(tag.getList("Points", Tag.TAG_COMPOUND), c -> TravellingPoint.read(c, graph)); + Couple points = Couple.deserializeEach(tag.getList("Points", Tag.TAG_COMPOUND), + c -> TravellingPoint.read(c, graph, dimensions)); CarriageBogey carriageBogey = new CarriageBogey(type, points.getFirst(), points.getSecond()); return carriageBogey; } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageContraption.java b/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageContraption.java index 967b081c4..491168418 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageContraption.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageContraption.java @@ -1,9 +1,12 @@ package com.simibubi.create.content.logistics.trains.entity; import java.util.ArrayList; +import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; +import java.util.Optional; import org.apache.commons.lang3.tuple.Pair; @@ -25,14 +28,17 @@ import com.simibubi.create.foundation.utility.VecHelper; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.core.Direction.Axis; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; import net.minecraft.nbt.NbtUtils; import net.minecraft.nbt.Tag; import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate.StructureBlockInfo; +import net.minecraft.world.phys.AABB; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; @@ -51,11 +57,17 @@ public class CarriageContraption extends Contraption { private BlockPos secondBogeyPos; private List assembledBlazeBurners; + // render + public int portalCutoffMin; + public int portalCutoffMax; + public CarriageContraption() { conductorSeats = new HashMap<>(); assembledBlazeBurners = new ArrayList<>(); blazeBurnerConductors = Couple.create(false, false); soundQueue = new ArrivalSoundQueue(); + portalCutoffMin = Integer.MIN_VALUE; + portalCutoffMax = Integer.MAX_VALUE; } public void setSoundQueueOffset(int offset) { @@ -216,4 +228,70 @@ public class CarriageContraption extends Contraption { return secondBogeyPos; } + private Collection specialRenderedTEsOutsidePortal = new ArrayList<>(); + + @Override + public Collection getRenderedBlocks() { + if (notInPortal()) + return super.getRenderedBlocks(); + + specialRenderedTEsOutsidePortal = new ArrayList<>(); + specialRenderedTileEntities.stream() + .filter(te -> !isHiddenInPortal(te.getBlockPos())) + .forEach(specialRenderedTEsOutsidePortal::add); + + Collection values = new ArrayList<>(); + for (Entry entry : blocks.entrySet()) { + BlockPos pos = entry.getKey(); + if (withinVisible(pos)) + values.add(entry.getValue()); + else if (atSeam(pos)) + values.add(new StructureBlockInfo(pos, Blocks.PURPLE_STAINED_GLASS.defaultBlockState(), null)); + } + return values; + } + + @Override + public Collection getSpecialRenderedTEs() { + if (notInPortal()) + return super.getSpecialRenderedTEs(); + return specialRenderedTEsOutsidePortal; + } + + @Override + public Optional> getSimplifiedEntityColliders() { + if (notInPortal()) + return super.getSimplifiedEntityColliders(); + return Optional.empty(); + } + + @Override + public boolean isHiddenInPortal(BlockPos localPos) { + if (notInPortal()) + return super.isHiddenInPortal(localPos); + return !withinVisible(localPos) || atSeam(localPos); + } + + private boolean notInPortal() { + return portalCutoffMin == Integer.MIN_VALUE && portalCutoffMax == Integer.MAX_VALUE; + } + + public boolean atSeam(BlockPos localPos) { + Direction facing = assemblyDirection; + Axis axis = facing.getClockWise() + .getAxis(); + int coord = axis.choose(localPos.getZ(), localPos.getY(), localPos.getX()) * -facing.getAxisDirection() + .getStep(); + return coord == portalCutoffMin || coord == portalCutoffMax; + } + + public boolean withinVisible(BlockPos localPos) { + Direction facing = assemblyDirection; + Axis axis = facing.getClockWise() + .getAxis(); + int coord = axis.choose(localPos.getZ(), localPos.getY(), localPos.getX()) * -facing.getAxisDirection() + .getStep(); + return coord > portalCutoffMin && coord < portalCutoffMax; + } + } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageContraptionEntity.java b/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageContraptionEntity.java index fe9c8ad80..e82385e50 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageContraptionEntity.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageContraptionEntity.java @@ -2,8 +2,10 @@ package com.simibubi.create.content.logistics.trains.entity; import java.lang.ref.WeakReference; import java.util.Collection; +import java.util.HashSet; import java.util.List; import java.util.Optional; +import java.util.Set; import java.util.UUID; import javax.annotation.Nullable; @@ -14,9 +16,13 @@ import com.simibubi.create.AllEntityDataSerializers; import com.simibubi.create.AllEntityTypes; import com.simibubi.create.Create; import com.simibubi.create.CreateClient; +import com.simibubi.create.content.contraptions.components.structureMovement.MovementBehaviour; +import com.simibubi.create.content.contraptions.components.structureMovement.MovementContext; import com.simibubi.create.content.contraptions.components.structureMovement.OrientedContraptionEntity; import com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls.ControlsBlock; +import com.simibubi.create.content.contraptions.particle.CubeParticleData; import com.simibubi.create.content.logistics.trains.TrackGraph; +import com.simibubi.create.content.logistics.trains.entity.Carriage.DimensionalCarriageEntity; import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.SteerDirection; import com.simibubi.create.content.logistics.trains.management.edgePoint.station.GlobalStation; import com.simibubi.create.foundation.config.AllConfigs; @@ -44,6 +50,8 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.level.Level; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate.StructureBlockInfo; import net.minecraft.world.phys.Vec3; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; public class CarriageContraptionEntity extends OrientedContraptionEntity { @@ -125,6 +133,17 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity { return entityData.get(SCHEDULED); } + public boolean isLocalCoordWithin(BlockPos localPos, int min, int max) { + if (!(getContraption()instanceof CarriageContraption cc)) + return false; + Direction facing = cc.getAssemblyDirection(); + Axis axis = facing.getClockWise() + .getAxis(); + int coord = axis.choose(localPos.getZ(), localPos.getY(), localPos.getX()) * -facing.getAxisDirection() + .getStep(); + return coord >= min && coord <= max; + } + public static CarriageContraptionEntity create(Level world, CarriageContraption contraption) { CarriageContraptionEntity entity = new CarriageContraptionEntity(AllEntityTypes.CARRIAGE_CONTRAPTION.get(), world); @@ -162,8 +181,12 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity { if (train == null || train.carriages.size() <= carriageIndex) return; carriage = train.carriages.get(carriageIndex); - if (carriage != null) - carriage.entity = new WeakReference<>(this); + if (carriage != null) { + DimensionalCarriageEntity dimensional = carriage.getDimensional(level); + dimensional.entity = new WeakReference<>(this); + dimensional.pivot = null; + carriage.updateContraptionAnchors(); + } updateTrackGraph(); } else discard(); @@ -201,7 +224,9 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity { return; } - if (!carriage.pointsInitialised) + DimensionalCarriageEntity dce = carriage.getDimensional(level); + + if (!dce.pointsInitialised) return; carriageData.approach(this, carriage, 1f / getType().updateInterval()); @@ -213,7 +238,7 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity { yo = getY(); zo = getZ(); - carriage.alignEntity(this); + dce.alignEntity(this); double distanceTo = 0; if (!firstPositionUpdate) { @@ -232,6 +257,8 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity { if (carriage.train.derailed) spawnDerailParticles(carriage); + if (dce.pivot != null) + spawnPortalParticles(dce); firstPositionUpdate = false; validForRender = true; @@ -247,7 +274,7 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity { for (int index = 0; index < carriageCount; index++) { int i = arrivalSoundReversed ? carriageCount - 1 - index : index; Carriage carriage = carriages.get(i); - CarriageContraptionEntity entity = carriage.entity.get(); + CarriageContraptionEntity entity = carriage.getDimensional(level).entity.get(); if (entity == null || !(entity.contraption instanceof CarriageContraption otherCC)) break; tick = arrivalSoundReversed ? otherCC.soundQueue.lastTick() : otherCC.soundQueue.firstTick(); @@ -268,7 +295,7 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity { boolean keepTicking = false; for (Carriage c : carriages) { - CarriageContraptionEntity entity = c.entity.get(); + CarriageContraptionEntity entity = c.getDimensional(level).entity.get(); if (entity == null || !(entity.contraption instanceof CarriageContraption otherCC)) continue; keepTicking |= otherCC.soundQueue.tick(entity, arrivalSoundTicks, arrivalSoundReversed); @@ -287,6 +314,11 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity { super.tickActors(); } + @Override + protected boolean isActorActive(MovementContext context, MovementBehaviour actor) { + return !getContraption().isHiddenInPortal(context.localPos) && super.isActorActive(context, actor); + } + @Override protected void handleStallInformation(float x, float y, float z, float angle) {} @@ -300,12 +332,49 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity { } } + private Set particleSlice = new HashSet<>(); + private float particleAvgY = 0; + + private void spawnPortalParticles(DimensionalCarriageEntity dce) { + Vec3 pivot = dce.pivot.getLocation() + .add(0, 1.5f, 0); + if (particleSlice.isEmpty()) + return; + + boolean alongX = Mth.equal(pivot.x, Math.round(pivot.x)); + int extraFlip = Direction.fromYRot(yaw) + .getAxisDirection() + .getStep(); + + Vec3 emitter = pivot.add(0, particleAvgY, 0); + double speed = position().distanceTo(getPrevPositionVec()); + int size = (int) (particleSlice.size() * Mth.clamp(4 - speed * 4, 0, 4)); + + for (BlockPos pos : particleSlice) { + if (size != 0 && random.nextInt(size) != 0) + continue; + if (alongX) + pos = new BlockPos(0, pos.getY(), pos.getX()); + Vec3 v = pivot.add(pos.getX() * extraFlip, pos.getY(), pos.getZ() * extraFlip); + CubeParticleData data = + new CubeParticleData(.25f, 0, .5f, .65f + (random.nextFloat() - .5f) * .25f, 4, false); + Vec3 m = v.subtract(emitter) + .normalize() + .scale(.325f); + m = VecHelper.rotate(m, random.nextFloat() * 360, alongX ? Axis.X : Axis.Z); + m = m.add(VecHelper.offsetRandomly(Vec3.ZERO, random, 0.25f)); + level.addParticle(data, v.x, v.y, v.z, m.x, m.y, m.z); + } + + } + @Override public void onClientRemoval() { super.onClientRemoval(); entityData.set(CARRIAGE_DATA, new CarriageSyncData()); if (carriage != null) { - carriage.pointsInitialised = false; + DimensionalCarriageEntity dce = carriage.getDimensional(level); + dce.pointsInitialised = false; carriage.leadingBogey().couplingAnchors = Couple.create(null, null); carriage.trailingBogey().couplingAnchors = Couple.create(null, null); } @@ -467,7 +536,9 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity { if (lookAhead != null) { if (spaceDown) { + carriage.train.manualTick = true; nav.startNavigation(lookAhead, -1, false); + carriage.train.manualTick = false; navDistanceTotal = nav.distanceToDestination; return true; } @@ -541,4 +612,69 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity { prevPosInvalid = true; } + @Override + public boolean isReadyForRender() { + return super.isReadyForRender() && validForRender && !firstPositionUpdate; + } + + @OnlyIn(Dist.CLIENT) + private WeakReference instanceHolder; + + @OnlyIn(Dist.CLIENT) + public void bindInstance(CarriageContraptionInstance instance) { + this.instanceHolder = new WeakReference<>(instance); + updateRenderedPortalCutoff(); + } + + @OnlyIn(Dist.CLIENT) + public void updateRenderedPortalCutoff() { + if (carriage == null) + return; + + // update portal slice + particleSlice.clear(); + particleAvgY = 0; + + if (contraption instanceof CarriageContraption cc) { + Direction forward = cc.getAssemblyDirection() + .getClockWise(); + Axis axis = forward.getAxis(); + boolean x = axis == Axis.X; + boolean flip = true; + + for (BlockPos pos : contraption.getBlocks() + .keySet()) { + if (!cc.atSeam(pos)) + continue; + int pX = x ? pos.getX() : pos.getZ(); + pX *= forward.getAxisDirection() + .getStep() * (flip ? 1 : -1); + pos = new BlockPos(pX, pos.getY(), 0); + particleSlice.add(pos); + particleAvgY += pos.getY(); + } + + } + if (particleSlice.size() > 0) + particleAvgY /= particleSlice.size(); + + // update hidden bogeys (if instanced) + if (instanceHolder == null) + return; + CarriageContraptionInstance instance = instanceHolder.get(); + if (instance == null) + return; + + int bogeySpacing = carriage.bogeySpacing; + + carriage.bogeys.forEachWithContext((bogey, first) -> { + if (bogey == null) + return; + + BlockPos bogeyPos = bogey.isLeading ? BlockPos.ZERO + : BlockPos.ZERO.relative(getInitialOrientation().getCounterClockWise(), bogeySpacing); + instance.setBogeyVisibility(first, !contraption.isHiddenInPortal(bogeyPos)); + }); + } + } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageContraptionEntityRenderer.java b/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageContraptionEntityRenderer.java index 00d06a501..ac3949750 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageContraptionEntityRenderer.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageContraptionEntityRenderer.java @@ -29,9 +29,7 @@ public class CarriageContraptionEntityRenderer extends ContraptionEntityRenderer for (CarriageBogey bogey : carriage.bogeys) if (bogey != null) bogey.couplingAnchors.replace(v -> null); - if (!super.shouldRender(entity, clippingHelper, cameraX, cameraY, cameraZ)) - return false; - return entity.validForRender && !entity.firstPositionUpdate; + return super.shouldRender(entity, clippingHelper, cameraX, cameraY, cameraZ); } @Override @@ -53,12 +51,19 @@ public class CarriageContraptionEntityRenderer extends ContraptionEntityRenderer if (bogey == null) return; - if (!Backend.isOn()) { + BlockPos bogeyPos = bogey.isLeading ? BlockPos.ZERO + : BlockPos.ZERO.relative(entity.getInitialOrientation() + .getCounterClockWise(), bogeySpacing); + + if (!Backend.isOn() && !entity.getContraption() + .isHiddenInPortal(bogeyPos)) { + ms.pushPose(); translateBogey(ms, bogey, bogeySpacing, viewYRot, viewXRot, partialTicks); int light = getBogeyLightCoords(entity, bogey, partialTicks); - bogey.type.render(null, bogey.wheelAngle.getValue(partialTicks), ms, partialTicks, buffers, light, overlay); + bogey.type.render(null, bogey.wheelAngle.getValue(partialTicks), ms, partialTicks, buffers, light, + overlay); ms.popPose(); } @@ -70,25 +75,28 @@ public class CarriageContraptionEntityRenderer extends ContraptionEntityRenderer }); } - public static void translateBogey(PoseStack ms, CarriageBogey bogey, int bogeySpacing, float viewYRot, float viewXRot, float partialTicks) { + public static void translateBogey(PoseStack ms, CarriageBogey bogey, int bogeySpacing, float viewYRot, + float viewXRot, float partialTicks) { TransformStack.cast(ms) - .rotateY(viewYRot + 90) - .rotateX(-viewXRot) - .rotateY(180) - .translate(0, 0, bogey.isLeading ? 0 : -bogeySpacing) - .rotateY(-180) - .rotateX(viewXRot) - .rotateY(-viewYRot - 90) - .rotateY(bogey.yaw.getValue(partialTicks)) - .rotateX(bogey.pitch.getValue(partialTicks)) - .translate(0, .5f, 0); + .rotateY(viewYRot + 90) + .rotateX(-viewXRot) + .rotateY(180) + .translate(0, 0, bogey.isLeading ? 0 : -bogeySpacing) + .rotateY(-180) + .rotateX(viewXRot) + .rotateY(-viewYRot - 90) + .rotateY(bogey.yaw.getValue(partialTicks)) + .rotateX(bogey.pitch.getValue(partialTicks)) + .translate(0, .5f, 0); } public static int getBogeyLightCoords(CarriageContraptionEntity entity, CarriageBogey bogey, float partialTicks) { - var lightPos = new BlockPos(Objects.requireNonNullElseGet(bogey.getAnchorPosition(), () -> entity.getLightProbePosition(partialTicks))); + var lightPos = new BlockPos( + Objects.requireNonNullElseGet(bogey.getAnchorPosition(), () -> entity.getLightProbePosition(partialTicks))); - return LightTexture.pack(entity.level.getBrightness(LightLayer.BLOCK, lightPos), entity.level.getBrightness(LightLayer.SKY, lightPos)); + return LightTexture.pack(entity.level.getBrightness(LightLayer.BLOCK, lightPos), + entity.level.getBrightness(LightLayer.SKY, lightPos)); } } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageContraptionInstance.java b/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageContraptionInstance.java index d45ed3a37..d8e527a8d 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageContraptionInstance.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageContraptionInstance.java @@ -7,6 +7,7 @@ import com.jozufozu.flywheel.util.AnimationTickHolder; import com.jozufozu.flywheel.util.transform.TransformStack; import com.mojang.blaze3d.vertex.PoseStack; import com.simibubi.create.foundation.utility.Couple; +import com.simibubi.create.foundation.utility.Iterate; public class CarriageContraptionInstance extends EntityInstance implements DynamicInstance { @@ -14,25 +15,34 @@ public class CarriageContraptionInstance extends EntityInstance bogeys; + private Couple bogeyHidden; public CarriageContraptionInstance(MaterialManager materialManager, CarriageContraptionEntity entity) { super(materialManager, entity); + bogeyHidden = Couple.create(() -> false); + entity.bindInstance(this); } @Override public void init() { carriage = entity.getCarriage(); - if (carriage == null) return; + if (carriage == null) + return; bogeys = carriage.bogeys.mapNotNullWithParam(CarriageBogey::createInstance, materialManager); updateLight(); } + public void setBogeyVisibility(boolean first, boolean visible) { + bogeyHidden.set(first, !visible); + } + @Override public void beginFrame() { if (bogeys == null) { - init(); + if (entity.isReadyForRender()) + init(); return; } @@ -47,18 +57,23 @@ public class CarriageContraptionInstance extends EntityInstance { if (instance != null) @@ -76,7 +92,8 @@ public class CarriageContraptionInstance extends EntityInstance { if (instance != null) diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageCouplingRenderer.java b/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageCouplingRenderer.java index e6ff622f6..f67a86798 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageCouplingRenderer.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageCouplingRenderer.java @@ -18,6 +18,7 @@ import net.minecraft.client.renderer.RenderType; import net.minecraft.core.BlockPos; import net.minecraft.util.Mth; import net.minecraft.world.entity.Entity; +import net.minecraft.world.level.Level; import net.minecraft.world.level.LightLayer; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; @@ -35,14 +36,15 @@ public class CarriageCouplingRenderer { return; Vec3 camera = cameraEntity.getPosition(partialTicks); + Level level = cameraEntity.level; for (Train train : trains) { List carriages = train.carriages; for (int i = 0; i < carriages.size() - 1; i++) { Carriage carriage = carriages.get(i); - CarriageContraptionEntity entity = carriage.entity.get(); + CarriageContraptionEntity entity = carriage.getDimensional(level).entity.get(); Carriage carriage2 = carriages.get(i + 1); - CarriageContraptionEntity entity2 = carriage.entity.get(); + CarriageContraptionEntity entity2 = carriage.getDimensional(level).entity.get(); if (entity == null || entity2 == null) continue; @@ -51,7 +53,7 @@ public class CarriageCouplingRenderer { CarriageBogey bogey2 = carriage2.leadingBogey(); Vec3 anchor = bogey1.couplingAnchors.getSecond(); Vec3 anchor2 = bogey2.couplingAnchors.getFirst(); - + if (anchor == null || anchor2 == null) continue; if (!anchor.closerThan(camera, 64)) diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageSyncData.java b/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageSyncData.java index dc25ceb1e..1cf7226cb 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageSyncData.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageSyncData.java @@ -16,6 +16,8 @@ import org.apache.commons.lang3.mutable.MutableBoolean; import com.simibubi.create.content.logistics.trains.TrackEdge; import com.simibubi.create.content.logistics.trains.TrackGraph; import com.simibubi.create.content.logistics.trains.TrackNode; +import com.simibubi.create.content.logistics.trains.entity.Carriage.DimensionalCarriageEntity; +import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.ITrackSelector; import com.simibubi.create.foundation.utility.Couple; import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.Pair; @@ -107,10 +109,12 @@ public class CarriageSyncData { } public void update(CarriageContraptionEntity entity, Carriage carriage) { + DimensionalCarriageEntity dce = carriage.getDimensional(entity.level); + TrackGraph graph = carriage.train.graph; if (graph == null) { - fallbackLocations = Pair.of(carriage.positionAnchor, carriage.rotationAnchors); - carriage.pointsInitialised = true; + fallbackLocations = Pair.of(dce.positionAnchor, dce.rotationAnchors); + dce.pointsInitialised = true; setDirty(true); return; } @@ -136,10 +140,12 @@ public class CarriageSyncData { } public void apply(CarriageContraptionEntity entity, Carriage carriage) { + DimensionalCarriageEntity dce = carriage.getDimensional(entity.level); + fallbackPointSnapshot = null; if (fallbackLocations != null) { - fallbackPointSnapshot = Pair.of(carriage.positionAnchor, carriage.rotationAnchors); - carriage.pointsInitialised = true; + fallbackPointSnapshot = Pair.of(dce.positionAnchor, dce.rotationAnchors); + dce.pointsInitialised = true; return; } @@ -154,7 +160,7 @@ public class CarriageSyncData { CarriageBogey bogey = carriage.bogeys.get(i / 2 == 0); TravellingPoint bogeyPoint = bogey.points.get(i % 2 == 0); - TravellingPoint point = carriage.pointsInitialised ? pointsToApproach[i] : bogeyPoint; + TravellingPoint point = dce.pointsInitialised ? pointsToApproach[i] : bogeyPoint; Couple nodes = pair.getFirst() .map(graph::getNode); @@ -170,7 +176,7 @@ public class CarriageSyncData { point.edge = edge; point.position = pair.getSecond(); - if (carriage.pointsInitialised) { + if (dce.pointsInitialised) { float foundDistance = -1; boolean direction = false; for (boolean forward : Iterate.trueAndFalse) { @@ -194,9 +200,9 @@ public class CarriageSyncData { } } - if (!carriage.pointsInitialised) { + if (!dce.pointsInitialised) { carriage.train.navigation.distanceToDestination = distanceToDestination; - carriage.pointsInitialised = true; + dce.pointsInitialised = true; return; } @@ -207,10 +213,12 @@ public class CarriageSyncData { } public void approach(CarriageContraptionEntity entity, Carriage carriage, float partial) { + DimensionalCarriageEntity dce = carriage.getDimensional(entity.level); + if (fallbackLocations != null && fallbackPointSnapshot != null) { - carriage.positionAnchor = approachVector(partial, carriage.positionAnchor, fallbackLocations.getFirst(), + dce.positionAnchor = approachVector(partial, dce.positionAnchor, fallbackLocations.getFirst(), fallbackPointSnapshot.getFirst()); - carriage.rotationAnchors.replaceWithContext((current, first) -> approachVector(partial, current, + dce.rotationAnchors.replaceWithContext((current, first) -> approachVector(partial, current, fallbackLocations.getSecond() .get(first), fallbackPointSnapshot.getSecond() @@ -238,9 +246,9 @@ public class CarriageSyncData { MutableBoolean success = new MutableBoolean(true); TravellingPoint toApproach = pointsToApproach[index]; - point.travel(graph, partial * f, - point.follow(toApproach, b -> success.setValue(success.booleanValue() && b)), - point.ignoreEdgePoints(), point.ignoreTurns()); + ITrackSelector trackSelector = + point.follow(toApproach, b -> success.setValue(success.booleanValue() && b)); + point.travel(graph, partial * f, trackSelector); // could not pathfind to server location if (!success.booleanValue()) { diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/entity/Navigation.java b/src/main/java/com/simibubi/create/content/logistics/trains/entity/Navigation.java index f5a9ae325..b07cf1a56 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/entity/Navigation.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/entity/Navigation.java @@ -19,6 +19,7 @@ import org.apache.commons.lang3.mutable.MutableObject; import com.jozufozu.flywheel.repack.joml.Math; import com.simibubi.create.Create; +import com.simibubi.create.content.logistics.trains.DimensionPalette; import com.simibubi.create.content.logistics.trains.TrackEdge; import com.simibubi.create.content.logistics.trains.TrackGraph; import com.simibubi.create.content.logistics.trains.TrackNode; @@ -37,9 +38,7 @@ import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.NBTHelper; import com.simibubi.create.foundation.utility.Pair; -import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtUtils; import net.minecraft.nbt.Tag; import net.minecraft.util.Mth; import net.minecraft.world.level.Level; @@ -368,7 +367,7 @@ public class Navigation { cancelNavigation(); return -1; } - + if (Math.abs(distanceToDestination) > 100) announceArrival = true; @@ -460,7 +459,7 @@ public class Navigation { return true; }); - if (!train.doubleEnded || !train.hasBackwardConductor()) + if (!train.doubleEnded || !train.manualTick && !train.hasBackwardConductor()) break; } @@ -621,14 +620,9 @@ public class Navigation { List> validTargets = new ArrayList<>(); Map connectionsFrom = graph.getConnectionsFrom(node2); - for (Entry connection : connectionsFrom.entrySet()) { - TrackEdge newEdge = connection.getValue(); - Vec3 currentDirection = edge.getDirection(false); - Vec3 newDirection = newEdge.getDirection(true); - if (currentDirection.dot(newDirection) < 7 / 8f) - continue; - validTargets.add(connection); - } + for (Entry connection : connectionsFrom.entrySet()) + if (edge.canTravelTo(connection.getValue())) + validTargets.add(connection); if (validTargets.isEmpty()) continue; @@ -673,7 +667,7 @@ public class Navigation { Pair, TrackEdge> current, GlobalStation station); } - public CompoundTag write() { + public CompoundTag write(DimensionPalette dimensions) { CompoundTag tag = new CompoundTag(); if (destination == null) return tag; @@ -685,8 +679,7 @@ public class Navigation { tag.put("Path", NBTHelper.writeCompoundList(currentPath, c -> { CompoundTag nbt = new CompoundTag(); nbt.put("Nodes", c.map(TrackNode::getLocation) - .map(BlockPos::new) - .serializeEach(NbtUtils::writeBlockPos)); + .serializeEach(loc -> loc.write(dimensions))); return nbt; })); if (waitingForSignal == null) @@ -698,7 +691,7 @@ public class Navigation { return tag; } - public void read(CompoundTag tag, TrackGraph graph) { + public void read(CompoundTag tag, TrackGraph graph, DimensionPalette dimensions) { destination = graph != null && tag.contains("Destination") ? graph.getPoint(EdgePointType.STATION, tag.getUUID("Destination")) : null; @@ -713,8 +706,7 @@ public class Navigation { currentPath.clear(); NBTHelper.iterateCompoundList(tag.getList("Path", Tag.TAG_COMPOUND), c -> currentPath.add(Couple - .deserializeEach(c.getList("Nodes", Tag.TAG_COMPOUND), - c2 -> TrackNodeLocation.fromPackedPos(NbtUtils.readBlockPos(c2))) + .deserializeEach(c.getList("Nodes", Tag.TAG_COMPOUND), c2 -> TrackNodeLocation.read(c2, dimensions)) .map(graph::locateNode))); waitingForSignal = tag.contains("BlockingSignal") ? Pair.of(tag.getUUID("BlockingSignal"), tag.getBoolean("BlockingSignalSide")) diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/entity/Train.java b/src/main/java/com/simibubi/create/content/logistics/trains/entity/Train.java index 2d487785c..43c0d9b5f 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/entity/Train.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/entity/Train.java @@ -20,10 +20,12 @@ import org.apache.commons.lang3.mutable.MutableBoolean; import org.apache.commons.lang3.mutable.MutableObject; import com.simibubi.create.Create; +import com.simibubi.create.content.logistics.trains.DimensionPalette; import com.simibubi.create.content.logistics.trains.GraphLocation; import com.simibubi.create.content.logistics.trains.TrackEdge; import com.simibubi.create.content.logistics.trains.TrackGraph; import com.simibubi.create.content.logistics.trains.TrackNode; +import com.simibubi.create.content.logistics.trains.entity.Carriage.DimensionalCarriageEntity; import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.IEdgePointListener; import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.ITrackSelector; import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.SteerDirection; @@ -50,6 +52,7 @@ import net.minecraft.core.Direction.Axis; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.Tag; import net.minecraft.network.chat.Component; +import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.util.Mth; import net.minecraft.world.entity.LivingEntity; @@ -72,6 +75,8 @@ public class Train { public Component name; public TrainStatus status; + public boolean invalid; + public SteerDirection manualSteer; public boolean manualTick; @@ -154,7 +159,7 @@ public class Train { Create.RAILWAYS.markTracksDirty(); if (graph == null) { - carriages.forEach(c -> c.manageEntity(level)); + carriages.forEach(c -> c.manageEntities(level)); updateConductors(); return; } @@ -175,9 +180,40 @@ public class Train { Carriage carriage = carriages.get(i); if (previousCarriage != null) { int target = carriageSpacing.get(i - 1); - Vec3 leadingAnchor = carriage.leadingAnchor(); - Vec3 trailingAnchor = previousCarriage.trailingAnchor(); - double actual = leadingAnchor.distanceTo(trailingAnchor); + double actual = target; + + TravellingPoint leadingPoint = carriage.getLeadingPoint(); + TravellingPoint trailingPoint = previousCarriage.getTrailingPoint(); + + int entries = 0; + double total = 0; + + if (leadingPoint.node1 != null && trailingPoint.node1 != null) { + ResourceKey d1 = leadingPoint.node1.getLocation().dimension; + ResourceKey d2 = trailingPoint.node1.getLocation().dimension; + for (boolean b : Iterate.trueAndFalse) { + ResourceKey d = b ? d1 : d2; + if (!b && d1.equals(d2)) + continue; + + DimensionalCarriageEntity dimensional = carriage.getDimensionalIfPresent(d); + DimensionalCarriageEntity dimensional2 = previousCarriage.getDimensionalIfPresent(d); + if (dimensional == null || dimensional2 == null) + continue; + + Vec3 leadingAnchor = dimensional.leadingAnchor(); + Vec3 trailingAnchor = dimensional2.trailingAnchor(); + if (leadingAnchor == null || trailingAnchor == null) + continue; + + total += leadingAnchor.distanceTo(trailingAnchor); + entries++; + } + } + + if (entries > 0) + actual = total / entries; + stress[i - 1] = target - actual; } previousCarriage = carriage; @@ -383,10 +419,19 @@ public class Train { if (derailed) return; - Vec3 start = (speed < 0 ? carriage.getTrailingPoint() : carriage.getLeadingPoint()).getPosition(); - Vec3 end = (speed < 0 ? carriage.getLeadingPoint() : carriage.getTrailingPoint()).getPosition(); + TravellingPoint trailingPoint = carriage.getTrailingPoint(); + TravellingPoint leadingPoint = carriage.getLeadingPoint(); - Pair collision = findCollidingTrain(level, start, end, this); + if (leadingPoint.node1 == null || trailingPoint.node1 == null) + return; + ResourceKey dimension = leadingPoint.node1.getLocation().dimension; + if (!dimension.equals(trailingPoint.node1.getLocation().dimension)) + return; + + Vec3 start = (speed < 0 ? trailingPoint : leadingPoint).getPosition(); + Vec3 end = (speed < 0 ? leadingPoint : trailingPoint).getPosition(); + + Pair collision = findCollidingTrain(level, start, end, this, dimension); if (collision == null) return; @@ -402,7 +447,8 @@ public class Train { train.crash(); } - public static Pair findCollidingTrain(Level level, Vec3 start, Vec3 end, Train ignore) { + public static Pair findCollidingTrain(Level level, Vec3 start, Vec3 end, Train ignore, + ResourceKey dimension) { for (Train train : Create.RAILWAYS.sided(level).trains.values()) { if (train == ignore) continue; @@ -419,6 +465,11 @@ public class Train { TravellingPoint otherTrailing = otherCarriage.getTrailingPoint(); if (otherLeading.edge == null || otherTrailing.edge == null) continue; + ResourceKey otherDimension = otherLeading.node1.getLocation().dimension; + if (!otherDimension.equals(otherTrailing.node1.getLocation().dimension)) + continue; + if (!otherDimension.equals(dimension)) + continue; Vec3 start2 = otherLeading.getPosition(); Vec3 end2 = otherTrailing.getPosition(); @@ -486,7 +537,7 @@ public class Train { for (int i = 0; i < carriages.size(); i++) { Carriage carriage = carriages.get(backwards ? carriages.size() - i - 1 : i); - CarriageContraptionEntity entity = carriage.entity.get(); + CarriageContraptionEntity entity = carriage.anyAvailableEntity(); if (entity == null) return false; @@ -511,7 +562,9 @@ public class Train { public boolean canDisassemble() { for (Carriage carriage : carriages) { - CarriageContraptionEntity entity = carriage.entity.get(); + if (carriage.presentInMultipleDimensions()) + return false; + CarriageContraptionEntity entity = carriage.anyAvailableEntity(); if (entity == null) return false; if (!Mth.equal(entity.pitch, 0)) @@ -629,11 +682,8 @@ public class Train { } public void syncTrackGraphChanges() { - for (Carriage carriage : carriages) { - CarriageContraptionEntity entity = carriage.entity.get(); - if (entity != null) - entity.setGraph(graph == null ? null : graph.id); - } + for (Carriage carriage : carriages) + carriage.forEachPresentEntity(e -> e.setGraph(graph == null ? null : graph.id)); } public int getTotalLength() { @@ -681,7 +731,10 @@ public class Train { public LivingEntity getOwner(Level level) { try { UUID uuid = owner; - return uuid == null ? null : level.getPlayerByUUID(uuid); + return uuid == null ? null + : level.getServer() + .getPlayerList() + .getPlayer(uuid); } catch (IllegalArgumentException illegalargumentexception) { return null; } @@ -799,13 +852,13 @@ public class Train { return Penalties.ANY_TRAIN; } - public CompoundTag write() { + public CompoundTag write(DimensionPalette dimensions) { CompoundTag tag = new CompoundTag(); tag.putUUID("Id", id); tag.putUUID("Owner", owner); if (graph != null) tag.putUUID("Graph", graph.id); - tag.put("Carriages", NBTHelper.writeCompoundList(carriages, Carriage::write)); + tag.put("Carriages", NBTHelper.writeCompoundList(carriages, c -> c.write(dimensions))); tag.putIntArray("CarriageSpacing", carriageSpacing); tag.putBoolean("DoubleEnded", doubleEnded); tag.putDouble("Speed", speed); @@ -830,22 +883,22 @@ public class Train { compoundTag.putUUID("Id", uid); return compoundTag; })); - tag.put("MigratingPoints", NBTHelper.writeCompoundList(migratingPoints, TrainMigration::write)); + tag.put("MigratingPoints", NBTHelper.writeCompoundList(migratingPoints, tm -> tm.write(dimensions))); tag.put("Runtime", runtime.write()); - tag.put("Navigation", navigation.write()); + tag.put("Navigation", navigation.write(dimensions)); return tag; } - public static Train read(CompoundTag tag, Map trackNetworks) { + public static Train read(CompoundTag tag, Map trackNetworks, DimensionPalette dimensions) { UUID id = tag.getUUID("Id"); UUID owner = tag.getUUID("Owner"); UUID graphId = tag.contains("Graph") ? tag.getUUID("Graph") : null; TrackGraph graph = graphId == null ? null : trackNetworks.get(graphId); List carriages = new ArrayList<>(); NBTHelper.iterateCompoundList(tag.getList("Carriages", Tag.TAG_COMPOUND), - c -> carriages.add(Carriage.read(c, graph))); + c -> carriages.add(Carriage.read(c, graph, dimensions))); List carriageSpacing = new ArrayList<>(); for (int i : tag.getIntArray("CarriageSpacing")) carriageSpacing.add(i); @@ -868,10 +921,10 @@ public class Train { NBTHelper.iterateCompoundList(tag.getList("ReservedSignalBlocks", Tag.TAG_COMPOUND), c -> train.reservedSignalBlocks.add(c.getUUID("Id"))); NBTHelper.iterateCompoundList(tag.getList("MigratingPoints", Tag.TAG_COMPOUND), - c -> train.migratingPoints.add(TrainMigration.read(c))); + c -> train.migratingPoints.add(TrainMigration.read(c, dimensions))); train.runtime.read(tag.getCompound("Runtime")); - train.navigation.read(tag.getCompound("Navigation"), graph); + train.navigation.read(tag.getCompound("Navigation"), graph, dimensions); if (train.getCurrentStation() != null) train.getCurrentStation() diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/entity/TrainMigration.java b/src/main/java/com/simibubi/create/content/logistics/trains/entity/TrainMigration.java index 4def20c9a..31185549e 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/entity/TrainMigration.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/entity/TrainMigration.java @@ -2,6 +2,7 @@ package com.simibubi.create.content.logistics.trains.entity; import java.util.Map.Entry; +import com.simibubi.create.content.logistics.trains.DimensionPalette; import com.simibubi.create.content.logistics.trains.GraphLocation; import com.simibubi.create.content.logistics.trains.TrackEdge; import com.simibubi.create.content.logistics.trains.TrackGraph; @@ -10,9 +11,7 @@ import com.simibubi.create.content.logistics.trains.TrackNodeLocation; import com.simibubi.create.foundation.utility.Couple; import com.simibubi.create.foundation.utility.VecHelper; -import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtUtils; import net.minecraft.nbt.Tag; import net.minecraft.util.Mth; import net.minecraft.world.phys.Vec3; @@ -100,24 +99,22 @@ public class TrainMigration { return null; } - public CompoundTag write() { + public CompoundTag write(DimensionPalette dimensions) { CompoundTag tag = new CompoundTag(); tag.putBoolean("Curve", curve); tag.put("Fallback", VecHelper.writeNBT(fallback)); tag.putDouble("Position", positionOnOldEdge); - tag.put("Nodes", locations.map(BlockPos::new) - .serializeEach(NbtUtils::writeBlockPos)); + tag.put("Nodes", locations.serializeEach(l -> l.write(dimensions))); return tag; } - public static TrainMigration read(CompoundTag tag) { + public static TrainMigration read(CompoundTag tag, DimensionPalette dimensions) { TrainMigration trainMigration = new TrainMigration(); trainMigration.curve = tag.getBoolean("Curve"); trainMigration.fallback = VecHelper.readNBT(tag.getList("Fallback", Tag.TAG_DOUBLE)); trainMigration.positionOnOldEdge = tag.getDouble("Position"); trainMigration.locations = - Couple.deserializeEach(tag.getList("Nodes", Tag.TAG_COMPOUND), NbtUtils::readBlockPos) - .map(TrackNodeLocation::fromPackedPos); + Couple.deserializeEach(tag.getList("Nodes", Tag.TAG_COMPOUND), c -> TrackNodeLocation.read(c, dimensions)); return trainMigration; } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/entity/TrainRelocator.java b/src/main/java/com/simibubi/create/content/logistics/trains/entity/TrainRelocator.java index 6601b6eba..2859fc676 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/entity/TrainRelocator.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/entity/TrainRelocator.java @@ -189,14 +189,18 @@ public class TrainRelocator { }; ITrackSelector steer = probe.steer(SteerDirection.NONE, track.getUpNormal(level, pos, blockState)); MutableBoolean blocked = new MutableBoolean(false); + MutableBoolean portal = new MutableBoolean(false); MutableInt blockingIndex = new MutableInt(0); train.forEachTravellingPointBackwards((tp, d) -> { if (blocked.booleanValue()) return; - probe.travel(graph, d, steer, ignoreSignals, ignoreTurns); + probe.travel(graph, d, steer, ignoreSignals, ignoreTurns, $ -> { + portal.setTrue(); + return true; + }); recorder.accept(probe); - if (probe.blocked) { + if (probe.blocked || portal.booleanValue()) { blocked.setTrue(); return; } @@ -212,7 +216,8 @@ public class TrainRelocator { Vec3 vec1 = recordedVecs.get(i); Vec3 vec2 = recordedVecs.get(i + 1); boolean blocking = i >= blockingIndex.intValue() - 1; - boolean collided = !blocked.booleanValue() && Train.findCollidingTrain(level, vec1, vec2, train) != null; + boolean collided = !blocked.booleanValue() + && Train.findCollidingTrain(level, vec1, vec2, train, level.dimension()) != null; if (level.isClientSide && simulate) toVisualise.add(vec2); if (collided || blocking) diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/entity/TrainStatus.java b/src/main/java/com/simibubi/create/content/logistics/trains/entity/TrainStatus.java index 00ec20eaf..116c8ed0b 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/entity/TrainStatus.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/entity/TrainStatus.java @@ -70,6 +70,13 @@ public class TrainStatus { track = true; } + public void doublePortal() { + if (track) + return; + displayInformation("A Carriage cannot enter a portal whilst leaving another.", false); + track = true; + } + public void endOfTrack() { if (track) return; diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/entity/TravellingPoint.java b/src/main/java/com/simibubi/create/content/logistics/trains/entity/TravellingPoint.java index ace20dd28..92145314b 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/entity/TravellingPoint.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/entity/TravellingPoint.java @@ -11,10 +11,12 @@ import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.function.BiPredicate; import java.util.function.Consumer; +import java.util.function.Predicate; import javax.annotation.Nullable; import com.simibubi.create.Create; +import com.simibubi.create.content.logistics.trains.DimensionPalette; import com.simibubi.create.content.logistics.trains.GraphLocation; import com.simibubi.create.content.logistics.trains.TrackEdge; import com.simibubi.create.content.logistics.trains.TrackGraph; @@ -25,9 +27,7 @@ import com.simibubi.create.content.logistics.trains.management.edgePoint.signal. import com.simibubi.create.foundation.utility.Couple; import com.simibubi.create.foundation.utility.Pair; -import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtUtils; import net.minecraft.nbt.Tag; import net.minecraft.util.Mth; import net.minecraft.world.phys.Vec3; @@ -59,6 +59,9 @@ public class TravellingPoint { public static interface ITurnListener extends BiConsumer { }; + public static interface IPortalListener extends Predicate> { + }; + public TravellingPoint() {} public TravellingPoint(TrackNode node1, TrackNode node2, TrackEdge edge, double position) { @@ -77,6 +80,10 @@ public class TravellingPoint { }; } + public IPortalListener ignorePortals() { + return $ -> false; + } + public ITrackSelector random() { return (graph, pair) -> pair.getSecond() .get(Create.RANDOM.nextInt(pair.getSecond() @@ -176,8 +183,22 @@ public class TravellingPoint { }; } + public double travel(TrackGraph graph, double distance, ITrackSelector trackSelector) { + return travel(graph, distance, trackSelector, ignoreEdgePoints()); + } + + public double travel(TrackGraph graph, double distance, ITrackSelector trackSelector, + IEdgePointListener signalListener) { + return travel(graph, distance, trackSelector, signalListener, ignoreTurns()); + } + public double travel(TrackGraph graph, double distance, ITrackSelector trackSelector, IEdgePointListener signalListener, ITurnListener turnListener) { + return travel(graph, distance, trackSelector, signalListener, turnListener, ignorePortals()); + } + + public double travel(TrackGraph graph, double distance, ITrackSelector trackSelector, + IEdgePointListener signalListener, ITurnListener turnListener, IPortalListener portalListener) { blocked = false; double edgeLength = edge.getLength(); if (Mth.equal(distance, 0)) @@ -185,7 +206,7 @@ public class TravellingPoint { double prevPos = position; double traveled = distance; - double currentT = position / edgeLength; + double currentT = edgeLength == 0 ? 0 : position / edgeLength; double incrementT = edge.incrementT(currentT, distance); position = incrementT * edgeLength; @@ -222,9 +243,7 @@ public class TravellingPoint { continue; TrackEdge newEdge = entry.getValue(); - Vec3 currentDirection = edge.getDirection(false); - Vec3 newDirection = newEdge.getDirection(true); - if (currentDirection.dot(newDirection) < 7 / 8f) + if (!edge.canTravelTo(newEdge)) continue; validTargets.add(entry); @@ -240,6 +259,16 @@ public class TravellingPoint { Entry entry = validTargets.size() == 1 ? validTargets.get(0) : trackSelector.apply(graph, Pair.of(true, validTargets)); + if (entry.getValue() + .getLength() == 0 && portalListener.test( + Couple.create(node2.getLocation(), entry.getKey() + .getLocation()))) { + traveled -= position - edgeLength; + position = edgeLength; + blocked = true; + break; + } + node1 = node2; node2 = entry.getKey(); edge = entry.getValue(); @@ -272,12 +301,9 @@ public class TravellingPoint { TrackNode newNode = entry.getKey(); if (newNode == node2) continue; - - TrackEdge newEdge = graph.getConnectionsFrom(newNode) - .get(node1); - Vec3 currentDirection = edge.getDirection(true); - Vec3 newDirection = newEdge.getDirection(false); - if (currentDirection.dot(newDirection) < 7 / 8f) + if (!graph.getConnectionsFrom(newNode) + .get(node1) + .canTravelTo(edge)) continue; validTargets.add(entry); @@ -293,6 +319,16 @@ public class TravellingPoint { Entry entry = validTargets.size() == 1 ? validTargets.get(0) : trackSelector.apply(graph, Pair.of(false, validTargets)); + if (entry.getValue() + .getLength() == 0 && portalListener.test( + Couple.create(entry.getKey() + .getLocation(), node1.getLocation()))) { + traveled -= position; + position = 0; + blocked = true; + break; + } + node2 = node1; node1 = entry.getKey(); edge = graph.getConnectionsFrom(node1) @@ -322,9 +358,10 @@ public class TravellingPoint { if (edge.isTurn()) turnListener.accept(Math.max(0, totalDistance), edge); - EdgeData edgeData = edge.getEdgeData(); double from = forward ? prevPos : position; double to = forward ? position : prevPos; + + EdgeData edgeData = edge.getEdgeData(); List edgePoints = edgeData.getPoints(); double length = edge.getLength(); @@ -353,7 +390,11 @@ public class TravellingPoint { } public Vec3 getPosition() { - double t = position / edge.getLength(); + return getPositionWithOffset(0); + } + + public Vec3 getPositionWithOffset(double offset) { + double t = (position + offset) / edge.getLength(); return edge.getPosition(t) .add(edge.getNormal(node1, node2, t) .scale(1)); @@ -369,28 +410,25 @@ public class TravellingPoint { .get(node2); } - public CompoundTag write() { + public CompoundTag write(DimensionPalette dimensions) { CompoundTag tag = new CompoundTag(); Couple nodes = Couple.create(node1, node2); if (nodes.either(Objects::isNull)) return tag; tag.put("Nodes", nodes.map(TrackNode::getLocation) - .map(BlockPos::new) - .serializeEach(NbtUtils::writeBlockPos)); + .serializeEach(loc -> loc.write(dimensions))); tag.putDouble("Position", position); return tag; } - public static TravellingPoint read(CompoundTag tag, TrackGraph graph) { + public static TravellingPoint read(CompoundTag tag, TrackGraph graph, DimensionPalette dimensions) { if (graph == null) return new TravellingPoint(null, null, null, 0); - Couple locs = - tag.contains("Nodes") - ? Couple.deserializeEach(tag.getList("Nodes", Tag.TAG_COMPOUND), NbtUtils::readBlockPos) - .map(TrackNodeLocation::fromPackedPos) - .map(graph::locateNode) - : Couple.create(null, null); + Couple locs = tag.contains("Nodes") + ? Couple.deserializeEach(tag.getList("Nodes", Tag.TAG_COMPOUND), c -> TrackNodeLocation.read(c, dimensions)) + .map(graph::locateNode) + : Couple.create(null, null); if (locs.either(Objects::isNull)) return new TravellingPoint(null, null, null, 0); diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/EdgeData.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/EdgeData.java index 9c45d8c7d..942ccc02c 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/EdgeData.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/EdgeData.java @@ -10,6 +10,7 @@ import javax.annotation.Nullable; import com.google.common.base.Objects; import com.simibubi.create.Create; +import com.simibubi.create.content.logistics.trains.DimensionPalette; import com.simibubi.create.content.logistics.trains.TrackEdge; import com.simibubi.create.content.logistics.trains.TrackGraph; import com.simibubi.create.content.logistics.trains.TrackNode; @@ -177,7 +178,7 @@ public class EdgeData { return null; } - public CompoundTag write() { + public CompoundTag write(DimensionPalette dimensions) { CompoundTag nbt = new CompoundTag(); if (singleSignalGroup == passiveGroup) NBTHelper.putMarker(nbt, "PassiveGroup"); @@ -194,11 +195,11 @@ public class EdgeData { return tag; })); if (hasIntersections()) - nbt.put("Intersections", NBTHelper.writeCompoundList(intersections, TrackEdgeIntersection::write)); + nbt.put("Intersections", NBTHelper.writeCompoundList(intersections, tei -> tei.write(dimensions))); return nbt; } - public static EdgeData read(CompoundTag nbt, TrackEdge edge, TrackGraph graph) { + public static EdgeData read(CompoundTag nbt, TrackEdge edge, TrackGraph graph, DimensionPalette dimensions) { EdgeData data = new EdgeData(edge); if (nbt.contains("SignalGroup")) data.singleSignalGroup = nbt.getUUID("SignalGroup"); @@ -216,8 +217,8 @@ public class EdgeData { data.points.add(point); }); if (nbt.contains("Intersections")) - data.intersections = - NBTHelper.readCompoundList(nbt.getList("Intersections", Tag.TAG_COMPOUND), TrackEdgeIntersection::read); + data.intersections = NBTHelper.readCompoundList(nbt.getList("Intersections", Tag.TAG_COMPOUND), + c -> TrackEdgeIntersection.read(c, dimensions)); return data; } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/EdgePointStorage.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/EdgePointStorage.java index 22de62ca4..d12c50d9c 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/EdgePointStorage.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/EdgePointStorage.java @@ -7,6 +7,7 @@ import java.util.Map.Entry; import java.util.UUID; import com.simibubi.create.Create; +import com.simibubi.create.content.logistics.trains.DimensionPalette; import com.simibubi.create.content.logistics.trains.TrackGraph; import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.TrackEdgePoint; import com.simibubi.create.foundation.utility.NBTHelper; @@ -65,14 +66,14 @@ public class EdgePointStorage { pointsByType.clear(); } - public CompoundTag write() { + public CompoundTag write(DimensionPalette dimensions) { CompoundTag nbt = new CompoundTag(); for (Entry, Map> entry : pointsByType.entrySet()) { EdgePointType type = entry.getKey(); ListTag list = NBTHelper.writeCompoundList(entry.getValue() .values(), edgePoint -> { CompoundTag tag = new CompoundTag(); - edgePoint.write(tag); + edgePoint.write(tag, dimensions); return tag; }); nbt.put(type.getId() @@ -81,14 +82,14 @@ public class EdgePointStorage { return nbt; } - public void read(CompoundTag nbt) { + public void read(CompoundTag nbt, DimensionPalette dimensions) { for (EdgePointType type : EdgePointType.TYPES.values()) { ListTag list = nbt.getList(type.getId() .toString(), Tag.TAG_COMPOUND); Map map = getMap(type); NBTHelper.iterateCompoundList(list, tag -> { TrackEdgePoint edgePoint = type.create(); - edgePoint.read(tag, false); + edgePoint.read(tag, false, dimensions); map.put(edgePoint.getId(), edgePoint); }); } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/EdgePointType.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/EdgePointType.java index f40cff223..69e3a98db 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/EdgePointType.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/EdgePointType.java @@ -5,6 +5,7 @@ import java.util.Map; import java.util.function.Supplier; import com.simibubi.create.Create; +import com.simibubi.create.content.logistics.trains.DimensionPalette; import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalBoundary; import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.TrackEdgePoint; import com.simibubi.create.content.logistics.trains.management.edgePoint.station.GlobalStation; @@ -44,11 +45,11 @@ public class EdgePointType { return id; } - public static TrackEdgePoint read(FriendlyByteBuf buffer) { + public static TrackEdgePoint read(FriendlyByteBuf buffer, DimensionPalette dimensions) { ResourceLocation type = buffer.readResourceLocation(); EdgePointType edgePointType = TYPES.get(type); TrackEdgePoint point = edgePointType.create(); - point.read(buffer); + point.read(buffer, dimensions); return point; } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/TrackEdgeIntersection.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/TrackEdgeIntersection.java index 16a523323..eeea62a1e 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/TrackEdgeIntersection.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/TrackEdgeIntersection.java @@ -2,12 +2,11 @@ package com.simibubi.create.content.logistics.trains.management.edgePoint; import java.util.UUID; +import com.simibubi.create.content.logistics.trains.DimensionPalette; import com.simibubi.create.content.logistics.trains.TrackNodeLocation; import com.simibubi.create.foundation.utility.Couple; -import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtUtils; import net.minecraft.nbt.Tag; public class TrackEdgeIntersection { @@ -31,18 +30,18 @@ public class TrackEdgeIntersection { || target1.equals(target.getSecond()) && target2.equals(target.getFirst()); } - public CompoundTag write() { + public CompoundTag write(DimensionPalette dimensions) { CompoundTag nbt = new CompoundTag(); nbt.putUUID("Id", id); if (groupId != null) nbt.putUUID("GroupId", groupId); nbt.putDouble("Location", location); nbt.putDouble("TargetLocation", targetLocation); - nbt.put("TargetEdge", target.serializeEach(loc -> NbtUtils.writeBlockPos(new BlockPos(loc)))); + nbt.put("TargetEdge", target.serializeEach(loc -> loc.write(dimensions))); return nbt; } - public static TrackEdgeIntersection read(CompoundTag nbt) { + public static TrackEdgeIntersection read(CompoundTag nbt, DimensionPalette dimensions) { TrackEdgeIntersection intersection = new TrackEdgeIntersection(); intersection.id = nbt.getUUID("Id"); if (nbt.contains("GroupId")) @@ -50,7 +49,7 @@ public class TrackEdgeIntersection { intersection.location = nbt.getDouble("Location"); intersection.targetLocation = nbt.getDouble("TargetLocation"); intersection.target = Couple.deserializeEach(nbt.getList("TargetEdge", Tag.TAG_COMPOUND), - tag -> TrackNodeLocation.fromPackedPos(NbtUtils.readBlockPos(tag))); + tag -> TrackNodeLocation.read(tag, dimensions)); return intersection; } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/TrackTargetingBehaviour.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/TrackTargetingBehaviour.java index 55c8d62a3..82c5ec27d 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/TrackTargetingBehaviour.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/TrackTargetingBehaviour.java @@ -8,6 +8,7 @@ import javax.annotation.Nullable; import com.jozufozu.flywheel.core.PartialModel; import com.mojang.blaze3d.vertex.PoseStack; import com.simibubi.create.Create; +import com.simibubi.create.content.logistics.trains.DimensionPalette; import com.simibubi.create.content.logistics.trains.GraphLocation; import com.simibubi.create.content.logistics.trains.ITrackBlock; import com.simibubi.create.content.logistics.trains.TrackEdge; @@ -186,7 +187,7 @@ public class TrackTargetingBehaviour extends TileEntit boolean reverseEdge = front || point instanceof SingleTileEdgePoint; if (data != null) - point.read(data, true); + point.read(data, true, DimensionPalette.read(data)); point.setId(id); point.setLocation(reverseEdge ? loc.edge : loc.edge.swap(), reverseEdge ? loc.position : length - loc.position); diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/TrackTargetingBlockItem.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/TrackTargetingBlockItem.java index 97641261e..404c7aaf6 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/TrackTargetingBlockItem.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/TrackTargetingBlockItem.java @@ -198,6 +198,9 @@ public class TrackTargetingBlockItem extends BlockItem { Couple nodes = location.edge.map(location.graph::locateNode); TrackEdge edge = location.graph.getConnection(nodes); + if (edge == null) + return; + EdgeData edgeData = edge.getEdgeData(); double edgePosition = location.position; diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/SignalBoundary.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/SignalBoundary.java index f1aaa1e85..5de7ffaff 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/SignalBoundary.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/SignalBoundary.java @@ -8,6 +8,7 @@ import java.util.UUID; import com.google.common.base.Objects; import com.simibubi.create.Create; +import com.simibubi.create.content.logistics.trains.DimensionPalette; import com.simibubi.create.content.logistics.trains.TrackGraph; import com.simibubi.create.content.logistics.trains.TrackNode; import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointType; @@ -237,8 +238,8 @@ public class SignalBoundary extends TrackEdgePoint { } @Override - public void read(CompoundTag nbt, boolean migration) { - super.read(nbt, migration); + public void read(CompoundTag nbt, boolean migration, DimensionPalette dimensions) { + super.read(nbt, migration, dimensions); if (migration) return; @@ -265,8 +266,8 @@ public class SignalBoundary extends TrackEdgePoint { } @Override - public void read(FriendlyByteBuf buffer) { - super.read(buffer); + public void read(FriendlyByteBuf buffer, DimensionPalette dimensions) { + super.read(buffer, dimensions); for (int i = 1; i <= 2; i++) { if (buffer.readBoolean()) groups.set(i == 1, buffer.readUUID()); @@ -274,8 +275,8 @@ public class SignalBoundary extends TrackEdgePoint { } @Override - public void write(CompoundTag nbt) { - super.write(nbt); + public void write(CompoundTag nbt, DimensionPalette dimensions) { + super.write(nbt, dimensions); for (int i = 1; i <= 2; i++) if (!blockEntities.get(i == 1) .isEmpty()) @@ -293,8 +294,8 @@ public class SignalBoundary extends TrackEdgePoint { } @Override - public void write(FriendlyByteBuf buffer) { - super.write(buffer); + public void write(FriendlyByteBuf buffer, DimensionPalette dimensions) { + super.write(buffer, dimensions); for (int i = 1; i <= 2; i++) { boolean hasGroup = groups.get(i == 1) != null; buffer.writeBoolean(hasGroup); diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/SignalPropagator.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/SignalPropagator.java index cc17d7fbd..6eaf4319f 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/SignalPropagator.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/SignalPropagator.java @@ -24,8 +24,6 @@ import com.simibubi.create.foundation.utility.Couple; import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.Pair; -import net.minecraft.world.phys.Vec3; - public class SignalPropagator { public static void onSignalRemoved(TrackGraph graph, SignalBoundary signal) { @@ -103,7 +101,7 @@ public class SignalPropagator { return true; }, false); - + group.resolveColor(); sync.edgeGroupCreated(groupId, group.color); } @@ -180,14 +178,10 @@ public class SignalPropagator { continue; // chain signal: check if reachable - if (forCollection) { - Vec3 currentDirection = graph.getConnectionsFrom(prevNode) - .get(currentNode) - .getDirection(false); - Vec3 newDirection = edge.getDirection(true); - if (currentDirection.dot(newDirection) < 3 / 4f) - continue; - } + if (forCollection && !graph.getConnectionsFrom(prevNode) + .get(currentNode) + .canTravelTo(edge)) + continue; TrackEdge oppositeEdge = graph.getConnectionsFrom(nextNode) .get(currentNode); diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/SingleTileEdgePoint.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/SingleTileEdgePoint.java index 10ad13829..732625e04 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/SingleTileEdgePoint.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/SingleTileEdgePoint.java @@ -1,5 +1,7 @@ package com.simibubi.create.content.logistics.trains.management.edgePoint.signal; +import com.simibubi.create.content.logistics.trains.DimensionPalette; + import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtUtils; @@ -35,16 +37,16 @@ public abstract class SingleTileEdgePoint extends TrackEdgePoint { } @Override - public void read(CompoundTag nbt, boolean migration) { - super.read(nbt, migration); + public void read(CompoundTag nbt, boolean migration, DimensionPalette dimensions) { + super.read(nbt, migration, dimensions); if (migration) return; tilePos = NbtUtils.readBlockPos(nbt.getCompound("TilePos")); } @Override - public void write(CompoundTag nbt) { - super.write(nbt); + public void write(CompoundTag nbt, DimensionPalette dimensions) { + super.write(nbt, dimensions); nbt.put("TilePos", NbtUtils.writeBlockPos(tilePos)); } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/TrackEdgePoint.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/TrackEdgePoint.java index 3e513dd7a..acfaa54eb 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/TrackEdgePoint.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/signal/TrackEdgePoint.java @@ -3,6 +3,7 @@ package com.simibubi.create.content.logistics.trains.management.edgePoint.signal import java.util.UUID; import com.simibubi.create.Create; +import com.simibubi.create.content.logistics.trains.DimensionPalette; import com.simibubi.create.content.logistics.trains.TrackEdge; import com.simibubi.create.content.logistics.trains.TrackGraph; import com.simibubi.create.content.logistics.trains.TrackNode; @@ -14,7 +15,6 @@ import com.simibubi.create.foundation.utility.Couple; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; -import net.minecraft.nbt.NbtUtils; import net.minecraft.nbt.Tag; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.world.level.LevelAccessor; @@ -56,7 +56,9 @@ public abstract class TrackEdgePoint { if (behaviour == null) return; CompoundTag migrationData = new CompoundTag(); - write(migrationData); + DimensionPalette dimensions = new DimensionPalette(); + write(migrationData, dimensions); + dimensions.write(migrationData); behaviour.invalidateEdgePoint(migrationData); } @@ -84,32 +86,32 @@ public abstract class TrackEdgePoint { .equals(node1.getLocation()); } - public void read(CompoundTag nbt, boolean migration) { + public void read(CompoundTag nbt, boolean migration, DimensionPalette dimensions) { if (migration) return; id = nbt.getUUID("Id"); position = nbt.getDouble("Position"); edgeLocation = Couple.deserializeEach(nbt.getList("Edge", Tag.TAG_COMPOUND), - tag -> TrackNodeLocation.fromPackedPos(NbtUtils.readBlockPos(tag))); + tag -> TrackNodeLocation.read(tag, dimensions)); } - public void read(FriendlyByteBuf buffer) { + public void read(FriendlyByteBuf buffer, DimensionPalette dimensions) { id = buffer.readUUID(); - edgeLocation = Couple.create(() -> TrackNodeLocation.fromPackedPos(buffer.readBlockPos())); + edgeLocation = Couple.create(() -> TrackNodeLocation.receive(buffer, dimensions)); position = buffer.readDouble(); } - public void write(CompoundTag nbt) { + public void write(CompoundTag nbt, DimensionPalette dimensions) { nbt.putUUID("Id", id); nbt.putDouble("Position", position); - nbt.put("Edge", edgeLocation.serializeEach(loc -> NbtUtils.writeBlockPos(new BlockPos(loc)))); + nbt.put("Edge", edgeLocation.serializeEach(loc -> loc.write(dimensions))); } - public void write(FriendlyByteBuf buffer) { + public void write(FriendlyByteBuf buffer, DimensionPalette dimensions) { buffer.writeResourceLocation(type.getId()); buffer.writeUUID(id); - edgeLocation.forEach(loc -> buffer.writeBlockPos(new BlockPos(loc))); + edgeLocation.forEach(loc -> loc.send(buffer, dimensions)); buffer.writeDouble(position); } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/station/GlobalStation.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/station/GlobalStation.java index 39fbf7b73..d7db61a8f 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/station/GlobalStation.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/station/GlobalStation.java @@ -4,6 +4,7 @@ import java.lang.ref.WeakReference; import javax.annotation.Nullable; +import com.simibubi.create.content.logistics.trains.DimensionPalette; import com.simibubi.create.content.logistics.trains.TrackNode; import com.simibubi.create.content.logistics.trains.entity.Train; import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SingleTileEdgePoint; @@ -33,16 +34,16 @@ public class GlobalStation extends SingleTileEdgePoint { } @Override - public void read(CompoundTag nbt, boolean migration) { - super.read(nbt, migration); + public void read(CompoundTag nbt, boolean migration, DimensionPalette dimensions) { + super.read(nbt, migration, dimensions); name = nbt.getString("Name"); assembling = nbt.getBoolean("Assembling"); nearestTrain = new WeakReference(null); } @Override - public void read(FriendlyByteBuf buffer) { - super.read(buffer); + public void read(FriendlyByteBuf buffer, DimensionPalette dimensions) { + super.read(buffer, dimensions); name = buffer.readUtf(); assembling = buffer.readBoolean(); if (buffer.readBoolean()) @@ -50,15 +51,15 @@ public class GlobalStation extends SingleTileEdgePoint { } @Override - public void write(CompoundTag nbt) { - super.write(nbt); + public void write(CompoundTag nbt, DimensionPalette dimensions) { + super.write(nbt, dimensions); nbt.putString("Name", name); nbt.putBoolean("Assembling", assembling); } @Override - public void write(FriendlyByteBuf buffer) { - super.write(buffer); + public void write(FriendlyByteBuf buffer, DimensionPalette dimensions) { + super.write(buffer, dimensions); buffer.writeUtf(name); buffer.writeBoolean(assembling); buffer.writeBoolean(tilePos != null); diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/station/StationTileEntity.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/station/StationTileEntity.java index 9a43e7613..714512be2 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/station/StationTileEntity.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/edgePoint/station/StationTileEntity.java @@ -22,6 +22,7 @@ import com.simibubi.create.content.logistics.trains.ITrackBlock; import com.simibubi.create.content.logistics.trains.TrackEdge; import com.simibubi.create.content.logistics.trains.TrackGraph; import com.simibubi.create.content.logistics.trains.TrackNode; +import com.simibubi.create.content.logistics.trains.TrackNodeLocation; import com.simibubi.create.content.logistics.trains.TrackNodeLocation.DiscoveredLocation; import com.simibubi.create.content.logistics.trains.entity.Carriage; import com.simibubi.create.content.logistics.trains.entity.CarriageBogey; @@ -409,7 +410,7 @@ public class StationTileEntity extends SmartTileEntity { ITrackBlock track = edgePoint.getTrack(); BlockPos bogeyOffset = new BlockPos(track.getUpNormal(level, trackPosition, trackState)); - DiscoveredLocation location = null; + TrackNodeLocation location = null; Vec3 centre = Vec3.atBottomCenterOf(trackPosition) .add(0, track.getElevationAtCenter(level, trackPosition, trackState), 0); Collection ends = track.getConnected(level, trackPosition, trackState, true, null); @@ -442,9 +443,9 @@ public class StationTileEntity extends SmartTileEntity { if (points.size() == pointOffsets.size()) break; - DiscoveredLocation currentLocation = location; - location = new DiscoveredLocation(location.getLocation() - .add(directionVec.scale(.5))); + TrackNodeLocation currentLocation = location; + location = new TrackNodeLocation(location.getLocation() + .add(directionVec.scale(.5))).in(location.dimension); if (graph == null) graph = Create.RAILWAYS.getGraph(level, currentLocation); diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/track/StandardBogeyBlock.java b/src/main/java/com/simibubi/create/content/logistics/trains/track/StandardBogeyBlock.java index 5540d0a2c..3832a6ed5 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/track/StandardBogeyBlock.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/track/StandardBogeyBlock.java @@ -77,7 +77,7 @@ public class StandardBogeyBlock extends Block implements IBogeyBlock, ITE SHAPE = EnumProperty.create("shape", TrackShape.class); - public static final BooleanProperty HAS_TURN = BooleanProperty.create("turn"); + public static final BooleanProperty HAS_TE = BooleanProperty.create("turn"); public TrackBlock(Properties p_49795_) { super(p_49795_); registerDefaultState(defaultBlockState().setValue(SHAPE, TrackShape.ZO) - .setValue(HAS_TURN, false)); + .setValue(HAS_TE, false)); } @Override protected void createBlockStateDefinition(Builder p_49915_) { - super.createBlockStateDefinition(p_49915_.add(SHAPE, HAS_TURN)); + super.createBlockStateDefinition(p_49915_.add(SHAPE, HAS_TE)); } @OnlyIn(Dist.CLIENT) @@ -113,7 +128,7 @@ public class TrackBlock extends Block implements EntityBlock, IWrenchable, ITrac TrackShape best = TrackShape.ZO; double bestValue = Float.MAX_VALUE; for (TrackShape shape : TrackShape.values()) { - if (shape.isJunction()) + if (shape.isJunction() || shape.isPortal()) continue; Vec3 axis = shape.getAxes() .get(0); @@ -153,7 +168,7 @@ public class TrackBlock extends Block implements EntityBlock, IWrenchable, ITrac @Override public void onPlace(BlockState pState, Level pLevel, BlockPos pPos, BlockState pOldState, boolean pIsMoving) { - if (pOldState.getBlock() == this && pState.setValue(HAS_TURN, true) == pOldState.setValue(HAS_TURN, true)) + if (pOldState.getBlock() == this && pState.setValue(HAS_TE, true) == pOldState.setValue(HAS_TE, true)) return; if (pLevel.isClientSide) return; @@ -164,14 +179,119 @@ public class TrackBlock extends Block implements EntityBlock, IWrenchable, ITrac } @Override - public void tick(BlockState p_60462_, ServerLevel p_60463_, BlockPos p_60464_, Random p_60465_) { - TrackPropagator.onRailAdded(p_60463_, p_60464_, p_60462_); + public void tick(BlockState state, ServerLevel level, BlockPos pos, Random p_60465_) { + TrackPropagator.onRailAdded(level, pos, state); + if (!state.getValue(SHAPE) + .isPortal()) + connectToNether(level, pos, state); + } + + protected void connectToNether(ServerLevel level, BlockPos pos, BlockState state) { + TrackShape shape = state.getValue(TrackBlock.SHAPE); + Axis portalTest = shape == TrackShape.XO ? Axis.X : shape == TrackShape.ZO ? Axis.Z : null; + if (portalTest == null) + return; + + boolean pop = false; + + for (Direction d : Iterate.directionsInAxis(portalTest)) { + BlockPos portalPos = pos.relative(d); + BlockState portalState = level.getBlockState(portalPos); + if (!(portalState.getBlock() instanceof NetherPortalBlock)) + continue; + + pop = true; + Pair otherSide = getOtherSide(level, new BlockFace(pos, d)); + if (otherSide == null) + continue; + + ServerLevel otherLevel = otherSide.getFirst(); + BlockFace otherTrack = otherSide.getSecond(); + BlockPos otherTrackPos = otherTrack.getPos(); + BlockState existing = otherLevel.getBlockState(otherTrackPos); + if (!existing.getMaterial() + .isReplaceable()) + continue; + + level.setBlock(pos, state.setValue(SHAPE, TrackShape.asPortal(d)) + .setValue(HAS_TE, true), 3); + BlockEntity te = level.getBlockEntity(pos); + if (te instanceof TrackTileEntity tte) + tte.bind(otherLevel.dimension(), otherTrackPos); + + otherLevel.setBlock(otherTrackPos, state.setValue(SHAPE, TrackShape.asPortal(otherTrack.getFace())) + .setValue(HAS_TE, true), 3); + BlockEntity otherTe = otherLevel.getBlockEntity(otherTrackPos); + if (otherTe instanceof TrackTileEntity tte) + tte.bind(level.dimension(), pos); + + pop = false; + } + + if (pop) + level.destroyBlock(pos, true); + } + + protected Pair getOtherSide(ServerLevel level, BlockFace inboundTrack) { + BlockPos portalPos = inboundTrack.getConnectedPos(); + BlockState portalState = level.getBlockState(portalPos); + if (!(portalState.getBlock() instanceof NetherPortalBlock)) + return null; + + MinecraftServer minecraftserver = level.getServer(); + ResourceKey resourcekey = level.dimension() == Level.NETHER ? Level.OVERWORLD : Level.NETHER; + ServerLevel otherLevel = minecraftserver.getLevel(resourcekey); + if (otherLevel == null || !minecraftserver.isNetherEnabled()) + return null; + + PortalForcer teleporter = otherLevel.getPortalForcer(); + SuperGlueEntity probe = new SuperGlueEntity(level, new AABB(portalPos)); + probe.setYRot(inboundTrack.getFace() + .toYRot()); + PortalInfo portalinfo = teleporter.getPortalInfo(probe, otherLevel, probe::findDimensionEntryPoint); + if (portalinfo == null) + return null; + + BlockPos otherPortalPos = new BlockPos(portalinfo.pos); + BlockState otherPortalState = otherLevel.getBlockState(otherPortalPos); + if (!(otherPortalState.getBlock() instanceof NetherPortalBlock)) + return null; + + Direction targetDirection = inboundTrack.getFace(); + if (targetDirection.getAxis() == otherPortalState.getValue(NetherPortalBlock.AXIS)) + targetDirection = targetDirection.getClockWise(); + BlockPos otherPos = otherPortalPos.relative(targetDirection); + return Pair.of(otherLevel, new BlockFace(otherPos, targetDirection.getOpposite())); } @Override - public Collection getConnected(BlockGetter world, BlockPos pos, BlockState state, + public BlockState updateShape(BlockState state, Direction pDirection, BlockState pNeighborState, + LevelAccessor level, BlockPos pCurrentPos, BlockPos pNeighborPos) { + TrackShape shape = state.getValue(SHAPE); + if (!shape.isPortal()) + return state; + + for (Direction d : Iterate.horizontalDirections) { + if (TrackShape.asPortal(d) != state.getValue(SHAPE)) + continue; + if (pDirection != d) + continue; + + BlockPos portalPos = pCurrentPos.relative(d); + BlockState portalState = level.getBlockState(portalPos); + if (!(portalState.getBlock() instanceof NetherPortalBlock)) + return Blocks.AIR.defaultBlockState(); + } + + return state; + } + + @Override + public Collection getConnected(BlockGetter worldIn, BlockPos pos, BlockState state, boolean linear, TrackNodeLocation connectedTo) { Collection list; + BlockGetter world = connectedTo != null && worldIn instanceof ServerLevel sl ? sl.getServer() + .getLevel(connectedTo.dimension) : worldIn; if (getTrackAxes(world, pos, state).size() > 1) { Vec3 center = Vec3.atBottomCenterOf(pos) @@ -183,11 +303,12 @@ public class TrackBlock extends Block implements EntityBlock, IWrenchable, ITrac ITrackBlock.addToListIfConnected(connectedTo, list, (d, b) -> axis.scale(b ? 0 : fromCenter ? -d : d) .add(center), - b -> shape.getNormal(), axis, null); + b -> shape.getNormal(), b -> world instanceof Level l ? l.dimension() : Level.OVERWORLD, axis, + null); } else list = ITrackBlock.super.getConnected(world, pos, state, linear, connectedTo); - if (!state.getValue(HAS_TURN)) + if (!state.getValue(HAS_TE)) return list; if (linear) return list; @@ -198,22 +319,62 @@ public class TrackBlock extends Block implements EntityBlock, IWrenchable, ITrac Map connections = trackTE.getConnections(); connections.forEach((connectedPos, bc) -> ITrackBlock.addToListIfConnected(connectedTo, list, - (d, b) -> d == 1 ? Vec3.atLowerCornerOf(bc.tePositions.get(b)) : bc.starts.get(b), bc.normals::get, null, - bc)); + (d, b) -> d == 1 ? Vec3.atLowerCornerOf(bc.tePositions.get(b)) : bc.starts.get(b), bc.normals::get, + b -> world instanceof Level l ? l.dimension() : Level.OVERWORLD, null, bc)); + + if (trackTE.boundLocation == null || !(world instanceof ServerLevel level)) + return list; + + ResourceKey otherDim = trackTE.boundLocation.getFirst(); + ServerLevel otherLevel = level.getServer() + .getLevel(otherDim); + if (otherLevel == null) + return list; + BlockPos boundPos = trackTE.boundLocation.getSecond(); + BlockState boundState = otherLevel.getBlockState(boundPos); + if (!AllBlocks.TRACK.has(boundState)) + return list; + + Vec3 center = Vec3.atBottomCenterOf(pos) + .add(0, getElevationAtCenter(world, pos, state), 0); + Vec3 boundCenter = Vec3.atBottomCenterOf(boundPos) + .add(0, getElevationAtCenter(otherLevel, boundPos, boundState), 0); + TrackShape shape = state.getValue(TrackBlock.SHAPE); + TrackShape boundShape = boundState.getValue(TrackBlock.SHAPE); + Vec3 boundAxis = getTrackAxes(otherLevel, boundPos, boundState).get(0); + + getTrackAxes(world, pos, state).forEach(axis -> { + ITrackBlock.addToListIfConnected(connectedTo, list, (d, b) -> (b ? axis : boundAxis).scale(d) + .add(b ? center : boundCenter), b -> (b ? shape : boundShape).getNormal(), + b -> b ? level.dimension() : otherLevel.dimension(), axis, null); + }); + return list; } + public void animateTick(BlockState pState, Level pLevel, BlockPos pPos, Random pRand) { + if (!pState.getValue(SHAPE) + .isPortal()) + return; + Vec3 v = Vec3.atLowerCornerOf(pPos) + .subtract(.125f, 0, .125f); + CubeParticleData data = + new CubeParticleData(1, pRand.nextFloat(), 1, .0125f + .0625f * pRand.nextFloat(), 30, false); + pLevel.addParticle(data, v.x + pRand.nextFloat() * 1.5f, v.y + .25f, v.z + pRand.nextFloat() * 1.5f, 0.0D, + 0.04D, 0.0D); + } + @Override public void onRemove(BlockState pState, Level pLevel, BlockPos pPos, BlockState pNewState, boolean pIsMoving) { boolean removeTE = false; - if (pState.getValue(HAS_TURN) && (!pState.is(pNewState.getBlock()) || !pNewState.getValue(HAS_TURN))) { + if (pState.getValue(HAS_TE) && (!pState.is(pNewState.getBlock()) || !pNewState.getValue(HAS_TE))) { BlockEntity blockEntity = pLevel.getBlockEntity(pPos); if (blockEntity instanceof TrackTileEntity && !pLevel.isClientSide) ((TrackTileEntity) blockEntity).removeInboundConnections(); removeTE = true; } - if (pNewState.getBlock() != this || pState.setValue(HAS_TURN, true) != pNewState.setValue(HAS_TURN, true)) + if (pNewState.getBlock() != this || pState.setValue(HAS_TE, true) != pNewState.setValue(HAS_TE, true)) TrackPropagator.onRailRemoved(pLevel, pPos, pState); if (removeTE) pLevel.removeBlockEntity(pPos); @@ -282,13 +443,13 @@ public class TrackBlock extends Block implements EntityBlock, IWrenchable, ITrac case AS: return TRACK_ASC.get(Direction.SOUTH); case CR_D: - return AllShapes.TRACK_CROSS_DIAG; + return TRACK_CROSS_DIAG; case CR_NDX: return TRACK_CROSS_ORTHO_DIAG.get(Direction.SOUTH); case CR_NDZ: return TRACK_CROSS_DIAG_ORTHO.get(Direction.SOUTH); case CR_O: - return AllShapes.TRACK_CROSS; + return TRACK_CROSS; case CR_PDX: return TRACK_CROSS_DIAG_ORTHO.get(Direction.EAST); case CR_PDZ: @@ -301,6 +462,14 @@ public class TrackBlock extends Block implements EntityBlock, IWrenchable, ITrac return TRACK_ORTHO.get(Direction.EAST); case ZO: return TRACK_ORTHO.get(Direction.SOUTH); + case TE: + return TRACK_ORTHO_LONG.get(Direction.EAST); + case TW: + return TRACK_ORTHO_LONG.get(Direction.WEST); + case TS: + return TRACK_ORTHO_LONG.get(Direction.SOUTH); + case TN: + return TRACK_ORTHO_LONG.get(Direction.NORTH); case NONE: default: } @@ -320,7 +489,7 @@ public class TrackBlock extends Block implements EntityBlock, IWrenchable, ITrac @Override public BlockEntity newBlockEntity(BlockPos p_153215_, BlockState state) { - if (!state.getValue(HAS_TURN)) + if (!state.getValue(HAS_TE)) return null; return AllTileEntities.TRACK.create(p_153215_, state); } @@ -354,7 +523,7 @@ public class TrackBlock extends Block implements EntityBlock, IWrenchable, ITrac public InteractionResult onSneakWrenched(BlockState state, UseOnContext context) { Player player = context.getPlayer(); Level level = context.getLevel(); - if (!level.isClientSide && !player.isCreative() && state.getValue(HAS_TURN)) { + if (!level.isClientSide && !player.isCreative() && state.getValue(HAS_TE)) { BlockEntity blockEntity = level.getBlockEntity(context.getClickedPos()); if (blockEntity instanceof TrackTileEntity trackTE) { trackTE.cancelDrops = true; @@ -489,7 +658,7 @@ public class TrackBlock extends Block implements EntityBlock, IWrenchable, ITrac @Override public boolean trackEquals(BlockState state1, BlockState state2) { return state1.getBlock() == this && state2.getBlock() == this - && state1.setValue(HAS_TURN, false) == state2.setValue(HAS_TURN, false); + && state1.setValue(HAS_TE, false) == state2.setValue(HAS_TE, false); } public static class RenderProperties extends ReducedDestroyEffects implements DestroyProgressRenderingHandler { diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackBlockOutline.java b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackBlockOutline.java index 2c3bf1c6c..bd20136e9 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackBlockOutline.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackBlockOutline.java @@ -13,6 +13,7 @@ import com.simibubi.create.AllShapes; import com.simibubi.create.content.logistics.trains.BezierConnection; import com.simibubi.create.foundation.utility.AngleHelper; import com.simibubi.create.foundation.utility.AnimationTickHolder; +import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.RaycastHelper; import com.simibubi.create.foundation.utility.VecHelper; import com.simibubi.create.foundation.utility.WorldAttached; @@ -237,6 +238,7 @@ public class TrackBlockOutline { private static final VoxelShape LONG_CROSS = Shapes.or(TrackVoxelShapes.longOrthogonalZ(), TrackVoxelShapes.longOrthogonalX()); private static final VoxelShape LONG_ORTHO = TrackVoxelShapes.longOrthogonalZ(); + private static final VoxelShape LONG_ORTHO_OFFSET = TrackVoxelShapes.longOrthogonalZOffset(); private static void walkShapes(TrackShape shape, TransformStack msr, Consumer renderer) { float angle45 = Mth.PI / 4; @@ -246,6 +248,16 @@ public class TrackBlockOutline { else if (shape == TrackShape.ZO || shape == TrackShape.CR_NDZ || shape == TrackShape.CR_PDZ) renderer.accept(AllShapes.TRACK_ORTHO.get(Direction.SOUTH)); + if (shape.isPortal()) { + for (Direction d : Iterate.horizontalDirections) { + if (TrackShape.asPortal(d) != shape) + continue; + msr.rotateCentered(Direction.UP, AngleHelper.rad(AngleHelper.horizontalAngle(d))); + renderer.accept(LONG_ORTHO_OFFSET); + return; + } + } + if (shape == TrackShape.PD || shape == TrackShape.CR_PDX || shape == TrackShape.CR_PDZ) { msr.rotateCentered(Direction.UP, angle45); renderer.accept(LONG_ORTHO); diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackPlacement.java b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackPlacement.java index c4e480e3d..4dfa847f0 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackPlacement.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackPlacement.java @@ -144,7 +144,7 @@ public class TrackPlacement { if (pos1.distSqr(pos2) > 32 * 32) return info.withMessage("too_far") .tooJumbly(); - if (!state1.hasProperty(TrackBlock.HAS_TURN)) + if (!state1.hasProperty(TrackBlock.HAS_TE)) return info.withMessage("original_missing"); if (axis1.dot(end2.subtract(end1)) < 0) { @@ -469,8 +469,19 @@ public class TrackPlacement { Vec3 axis = first ? info.axis1 : info.axis2; BlockPos pos = first ? info.pos1 : info.pos2; BlockState state = first ? state1 : state2; - if (state.hasProperty(TrackBlock.HAS_TURN) && !simulate) - state = state.setValue(TrackBlock.HAS_TURN, false); + if (state.hasProperty(TrackBlock.HAS_TE) && !simulate) + state = state.setValue(TrackBlock.HAS_TE, false); + + switch (state.getValue(TrackBlock.SHAPE)) { + case TE, TW: + state = state.setValue(TrackBlock.SHAPE, TrackShape.XO); + break; + case TN, TS: + state = state.setValue(TrackBlock.SHAPE, TrackShape.ZO); + break; + default: + break; + } for (int i = 0; i < (info.curve != null ? extent + 1 : extent); i++) { Vec3 offset = axis.scale(i); @@ -501,12 +512,12 @@ public class TrackPlacement { if (!simulate) { BlockState stateAtPos = level.getBlockState(targetPos1); level.setBlock(targetPos1, - (stateAtPos.getBlock() == state1.getBlock() ? stateAtPos : state1).setValue(TrackBlock.HAS_TURN, true), + (stateAtPos.getBlock() == state1.getBlock() ? stateAtPos : state1).setValue(TrackBlock.HAS_TE, true), 3); stateAtPos = level.getBlockState(targetPos2); level.setBlock(targetPos2, - (stateAtPos.getBlock() == state2.getBlock() ? stateAtPos : state2).setValue(TrackBlock.HAS_TURN, true), + (stateAtPos.getBlock() == state2.getBlock() ? stateAtPos : state2).setValue(TrackBlock.HAS_TE, true), 3); } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackShape.java b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackShape.java index f9a22a148..0c3e2332b 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackShape.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackShape.java @@ -7,6 +7,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.simibubi.create.foundation.utility.Lang; +import net.minecraft.core.Direction; import net.minecraft.util.StringRepresentable; import net.minecraft.world.level.block.Mirror; import net.minecraft.world.level.block.Rotation; @@ -23,6 +24,11 @@ public enum TrackShape implements StringRepresentable { AE("ascending", 270, new Vec3(1, 1, 0), new Vec3(-1, 1, 0)), AW("ascending", 90, new Vec3(-1, 1, 0), new Vec3(1, 1, 0)), + TN("teleport", 180, new Vec3(0, 0, -1), new Vec3(0, 1, 0)), + TS("teleport", 0, new Vec3(0, 0, 1), new Vec3(0, 1, 0)), + TE("teleport", 270, new Vec3(1, 0, 0), new Vec3(0, 1, 0)), + TW("teleport", 90, new Vec3(-1, 0, 0), new Vec3(0, 1, 0)), + CR_O("cross_ortho", new Vec3(0, 0, 1), new Vec3(1, 0, 0)), CR_D("cross_diag", new Vec3(1, 0, 1), new Vec3(-1, 0, 1)), CR_PDX("cross_d1_xo", new Vec3(1, 0, 0), new Vec3(1, 0, 1)), @@ -60,7 +66,7 @@ public enum TrackShape implements StringRepresentable { .put(CR_PDZ, CR_NDZ) .put(CR_NDZ, CR_PDZ) .build()); - + clockwise.putAll(ImmutableMap.builder() .put(PD, ND) .put(ND, PD) @@ -112,6 +118,29 @@ public enum TrackShape implements StringRepresentable { return axes.size() > 1; } + public boolean isPortal() { + switch (this) { + case TE, TN, TS, TW: + return true; + default: + return false; + } + } + + public static TrackShape asPortal(Direction horizontalFacing) { + switch (horizontalFacing) { + case EAST: + return TE; + case NORTH: + return TN; + case SOUTH: + return TS; + case WEST: + default: + return TW; + } + } + public Vec3 getNormal() { return normal; } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackTileEntity.java b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackTileEntity.java index 284359632..dad78d407 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackTileEntity.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackTileEntity.java @@ -8,6 +8,7 @@ import java.util.Map.Entry; import java.util.Set; import com.jozufozu.flywheel.backend.instancing.InstancedRenderDispatcher; +import com.simibubi.create.AllBlocks; import com.simibubi.create.content.contraptions.components.structureMovement.ITransformableTE; import com.simibubi.create.content.contraptions.components.structureMovement.StructureTransform; import com.simibubi.create.content.logistics.trains.BezierConnection; @@ -17,12 +18,19 @@ import com.simibubi.create.foundation.tileEntity.IMergeableTE; import com.simibubi.create.foundation.tileEntity.RemoveTileEntityPacket; import com.simibubi.create.foundation.tileEntity.SmartTileEntity; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; +import com.simibubi.create.foundation.utility.Pair; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction.Axis; +import net.minecraft.core.Registry; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.NbtUtils; import net.minecraft.nbt.Tag; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; @@ -38,6 +46,8 @@ public class TrackTileEntity extends SmartTileEntity implements ITransformableTE boolean connectionsValidated; boolean cancelDrops; + public Pair, BlockPos> boundLocation; + public TrackTileEntity(BlockEntityType type, BlockPos pos, BlockState state) { super(type, pos, state); connections = new HashMap<>(); @@ -84,12 +94,14 @@ public class TrackTileEntity extends SmartTileEntity implements ITransformableTE public void removeConnection(BlockPos target) { connections.remove(target); notifyUpdate(); - if (!connections.isEmpty()) + if (!connections.isEmpty() || getBlockState().getOptionalValue(TrackBlock.SHAPE) + .orElse(TrackShape.NONE) + .isPortal()) return; BlockState blockState = level.getBlockState(worldPosition); - if (blockState.hasProperty(TrackBlock.HAS_TURN)) - level.setBlockAndUpdate(worldPosition, blockState.setValue(TrackBlock.HAS_TURN, false)); + if (blockState.hasProperty(TrackBlock.HAS_TE)) + level.setBlockAndUpdate(worldPosition, blockState.setValue(TrackBlock.HAS_TE, false)); AllPackets.channel.send(packetTarget(), new RemoveTileEntityPacket(worldPosition)); } @@ -108,6 +120,11 @@ public class TrackTileEntity extends SmartTileEntity implements ITransformableTE AllPackets.channel.send(packetTarget(), new RemoveTileEntityPacket(worldPosition)); } + public void bind(ResourceKey boundDimension, BlockPos boundLocation) { + this.boundLocation = Pair.of(boundDimension, boundLocation); + setChanged(); + } + @Override protected void write(CompoundTag tag, boolean clientPacket) { super.write(tag, clientPacket); @@ -115,6 +132,13 @@ public class TrackTileEntity extends SmartTileEntity implements ITransformableTE for (BezierConnection bezierConnection : connections.values()) listTag.add(bezierConnection.write(worldPosition)); tag.put("Connections", listTag); + + if (boundLocation != null) { + tag.put("BoundLocation", NbtUtils.writeBlockPos(boundLocation.getSecond())); + tag.putString("BoundDimension", boundLocation.getFirst() + .location() + .toString()); + } } @Override @@ -134,6 +158,11 @@ public class TrackTileEntity extends SmartTileEntity implements ITransformableTE registerToCurveInteraction(); else removeFromCurveInteraction(); + + if (tag.contains("BoundLocation")) + boundLocation = Pair.of( + ResourceKey.create(Registry.DIMENSION_REGISTRY, new ResourceLocation(tag.getString("BoundDimension"))), + NbtUtils.readBlockPos(tag.getCompound("BoundLocation"))); } @Override @@ -204,6 +233,20 @@ public class TrackTileEntity extends SmartTileEntity implements ITransformableTE removeFromCurveInteraction(); } + @Override + protected void setRemovedNotDueToChunkUnload() { + super.setRemovedNotDueToChunkUnload(); + + if (boundLocation != null && level instanceof ServerLevel) { + ServerLevel otherLevel = level.getServer() + .getLevel(boundLocation.getFirst()); + if (otherLevel == null) + return; + if (AllBlocks.TRACK.has(otherLevel.getBlockState(boundLocation.getSecond()))) + otherLevel.destroyBlock(boundLocation.getSecond(), false); + } + } + private void registerToCurveInteraction() { DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> this::registerToCurveInteractionUnsafe); } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackVoxelShapes.java b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackVoxelShapes.java index 0f796a2a8..653de6e45 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackVoxelShapes.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/track/TrackVoxelShapes.java @@ -19,6 +19,10 @@ public class TrackVoxelShapes { return Block.box(-14, 0, -3.3, 16 + 14, 4, 19.3); } + public static VoxelShape longOrthogonalZOffset() { + return Block.box(-14, 0, 0, 16 + 14, 4, 24); + } + public static VoxelShape ascending() { VoxelShape shape = Block.box(-14, 0, 0, 16 + 14, 4, 4); VoxelShape[] shapes = new VoxelShape[6]; diff --git a/src/main/resources/assets/create/models/block/track/teleport.json b/src/main/resources/assets/create/models/block/track/teleport.json new file mode 100644 index 000000000..c2d50802b --- /dev/null +++ b/src/main/resources/assets/create/models/block/track/teleport.json @@ -0,0 +1,171 @@ +{ + "credit": "Made with Blockbench", + "ambientocclusion": false, + "texture_size": [32, 32], + "textures": { + "1": "create:block/portal_track", + "2": "create:block/portal_track_mip", + "3": "create:block/standard_track", + "particle": "create:block/palettes/stone_types/polished/andesite_cut_polished" + }, + "elements": [ + { + "name": "cube1", + "from": [8, -1.95625, 10], + "to": [29.95, 2.14375, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]}, + "faces": { + "north": {"uv": [11, 2, 0, 4], "texture": "#1"}, + "south": {"uv": [0, 2, 11, 4], "texture": "#1"}, + "down": {"uv": [0, 0, 11, 2], "texture": "#1"} + } + }, + { + "name": "cube2", + "from": [8, -1.95625, 2], + "to": [29.95, 2.14375, 6], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]}, + "faces": { + "north": {"uv": [11, 2, 0, 4], "texture": "#3"}, + "south": {"uv": [0, 2, 11, 4], "texture": "#3"}, + "down": {"uv": [0, 4, 11, 6], "texture": "#3"} + } + }, + { + "name": "cube3", + "from": [-13.95, -1.95625, 10], + "to": [8, 2.14375, 14], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]}, + "faces": { + "north": {"uv": [0, 2, 11, 4], "texture": "#1"}, + "south": {"uv": [11, 2, 0, 4], "texture": "#1"}, + "down": {"uv": [0, 0, 11, 2], "rotation": 180, "texture": "#1"} + } + }, + { + "name": "cube2", + "from": [8, -1.95625, 18], + "to": [29.95, 2.14375, 22], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]}, + "faces": { + "north": {"uv": [11, 14, 0, 16], "texture": "#1"}, + "south": {"uv": [0, 14, 11, 16], "texture": "#1"}, + "down": {"uv": [0, 4, 11, 6], "texture": "#1"} + } + }, + { + "name": "cube4", + "from": [-13.95, -1.95625, 18], + "to": [8, 2.14375, 22], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]}, + "faces": { + "north": {"uv": [0, 14, 11, 16], "texture": "#1"}, + "south": {"uv": [11, 14, 0, 16], "texture": "#1"}, + "down": {"uv": [0, 4, 11, 6], "rotation": 180, "texture": "#1"} + } + }, + { + "name": "cube4", + "from": [-13.95, -1.95625, 2], + "to": [8, 2.14375, 6], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]}, + "faces": { + "north": {"uv": [0, 2, 11, 4], "texture": "#3"}, + "south": {"uv": [11, 2, 0, 4], "texture": "#3"}, + "down": {"uv": [0, 4, 11, 6], "rotation": 180, "texture": "#3"} + } + }, + { + "name": "cube5", + "from": [-13.95, -1.95625, 0], + "to": [8, 2.14375, 24], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]}, + "faces": { + "west": {"uv": [11, 12.5, 13.05, 0.5], "rotation": 90, "texture": "#2"}, + "up": {"uv": [0, 12.5, 10.975, 0.5], "rotation": 180, "texture": "#2"} + } + }, + { + "name": "cube6", + "from": [8, -1.95625, 0], + "to": [29.95, 2.14375, 24], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]}, + "faces": { + "east": {"uv": [11, 0.5, 13.05, 12.5], "rotation": 90, "texture": "#2"}, + "up": {"uv": [0, 0.5, 10.975, 12.5], "texture": "#2"} + } + }, + { + "name": "tie1", + "from": [21.35, 1.09375, 0], + "to": [25.55, 1.14375, 24], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]}, + "faces": { + "up": {"uv": [12, 8.5, 0, 10.5], "rotation": 270, "texture": "#1"}, + "down": {"uv": [0, 8.5, 12, 10.5], "rotation": 270, "texture": "#1"} + } + }, + { + "name": "tie2", + "from": [21.45, 5.49375, 0], + "to": [25.45, 5.54375, 24], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]}, + "faces": { + "up": {"uv": [12, 11.5, 0, 13.5], "rotation": 270, "texture": "#1"}, + "down": {"uv": [0, 8.5, 12, 10.5], "rotation": 270, "texture": "#1"} + } + }, + { + "name": "tie3", + "from": [21.9, 1.14375, 0], + "to": [25, 5.54375, 24], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]}, + "faces": { + "north": {"uv": [11, 2, 12.5, 4], "texture": "#3"}, + "east": {"uv": [12, 6, 0, 8], "texture": "#1"}, + "south": {"uv": [12.5, 2, 11, 4], "texture": "#1"}, + "west": {"uv": [0, 6, 12, 8], "texture": "#1"} + } + }, + { + "name": "tie4", + "from": [-9.45, 5.49375, 0], + "to": [-5.45, 5.54375, 24], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]}, + "faces": { + "up": {"uv": [12, 13.5, 0, 11.5], "rotation": 270, "texture": "#1"}, + "down": {"uv": [0, 10.5, 12, 8.5], "rotation": 270, "texture": "#1"} + } + }, + { + "name": "tie5", + "from": [-9, 1.14375, 0], + "to": [-5.9, 5.54375, 24], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]}, + "faces": { + "north": {"uv": [12.5, 2, 11, 4], "texture": "#3"}, + "east": {"uv": [12, 6, 0, 8], "texture": "#1"}, + "south": {"uv": [12.5, 2, 11, 4], "texture": "#1"}, + "west": {"uv": [0, 6, 12, 8], "texture": "#1"} + } + }, + { + "name": "tie6", + "from": [-9.55, 1.09375, 0], + "to": [-5.35, 1.14375, 24], + "rotation": {"angle": 0, "axis": "y", "origin": [8, 0, 8]}, + "faces": { + "up": {"uv": [12, 10.5, 0, 8.5], "rotation": 270, "texture": "#1"}, + "down": {"uv": [0, 10.5, 12, 8.5], "rotation": 270, "texture": "#1"} + } + } + ], + "groups": [ + { + "name": "group", + "origin": [0, 0, 0], + "color": 0, + "children": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13] + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/create/textures/block/portal_track.png b/src/main/resources/assets/create/textures/block/portal_track.png new file mode 100644 index 0000000000000000000000000000000000000000..8728fb8cf2ccb6fce6e009c2b8b31a709e69a4c9 GIT binary patch literal 1534 zcmVPx#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1(QicK~z{r%~wr` z990mm*WJ_8-7~YjNj8j0Br8Oag&@J;MKFr^i$CDcB@#S%kQ}^t5+xVGi;9qo5hCJA zyeJ+d9s$RdH&Sqz4Z>EOFtDo0h_3E!{)EA$A zUr-6jC@nFfx>u9k@R+X3tI!HP**Bw+oa^@n^v(xolrZOj`uf79!u4!whA#D^F^xyL z2==#q+UUf@GJaDltP6jfWE6E%@;pm;n^8ie(L`tl=KZ<4L1*84bx#IZ2k&W8xaiZc zmr<60p=@HFjotHWbo9Rcv;etMmaNTa0(pa4;0^H1I8pKp@4N_SEajB1pAYKGkAIL5 zR9$Y|-7H<(himTyg>w$hWkNA-px=w>?75fs7J^@Xx>7VkTZjxd=ygZpRu091b|5$d z(}$NA=w@di^gnBT*$o_8Zqib_A^6{GJ!)Tjm<}wA>BdG(k9~M}rZMWqcdS`?jFtK%IPm-Ma$bs$8RL}jj8qvKso~6MsCaadvOP}qVX@JFwc2ds3 zFXdYsguJa zaPabas3?gF%1R}Twnf8EL5_|FxsWsDa7NPAd@rm_S@b&V6IiPO- znv2G9gqN4q=C}3Eyyi0Y?(|*54pC5%v+}AYel}VqI`*2iHv_JHSBL?2c>^|rao*if z^H|YNV9pubK-wd^|Mcv*S3$k`!V^W7*C_OwuEY2u(zpgZ%W6VvUPE}AW|=l@ z%JX5sv}_F=dV7We&pmPy2H*u)D(9Ezo49s^8lnsyumZBSWX$s+(;j2I4xWSOtqcY@ zjO+5P!c*sWHo!{J*#^Cd)T{oWSmi1xO>}b^ZWg7Cmp;d`L9f6f*{9F*e&@PtK|Oi$ zgyaCbn%&DW4Ez?=9d@q?M}T_s;OZ$a@NpJr7+0xwz008l?fV`YnpW`mN4r;pRDq-F zloy=+fn+}$G%469WOuy@ye_ylKqnekfX2a7d|u-V9w%X)$_`B@&h2i16{i^v=O`No z3vD#bK~I3&vXm->&rslJAWk(Nk0+vx%Z^u!sjW)S19j^7LxqDIv-Q=*w`&I2r8?jh z;qxtZm>Ry1F2rtUzUGnvo~bq> z^DOhK9+=gD45-u3oPq(9@+UNcm}X6Ov6iu zWFF{7>!5kMwP`WjFFamxJdkq%JTo4TM9$}2w=VJm-!+yq&j9$l8*u*Og(4Z|P{fH= zqbx2c3{hfMjVAcg)es&Y%b8}mYrEynDGy^XH8HMYR1wBB##x?YX0@A8urkL0T?IM> zjer%Qu^{UuySW)jmg|bpF6Um)XP7Q4LaIgc_#A&1(y>-BuL^EULY+jc1Pi4Z;%mrw zmN{Ge5FaRw>txH3gwwpy&5L&fbCa;M+AI8hI%X-{AKyYF!kCp~G;6eN!5Mf|TU>Ui z-{HS*1$Ec-UpNDc%lLbT^9;AvGFo|hX;;Mt)E}3Jg)K87W<8k+wV=^fG{8(~F5{OY zUW%{esGJG6=13UM?_lu2+rUhC{B&z?2CV<4N3vNnVGA>1($zG>@L#m%Z8KpNcwUTF k3(SP}-VFGEXF{U?0KEl6FCVHT`v3p{07*qoM6N<$f-kA%2mk;8 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/create/textures/block/portal_track_mip.png b/src/main/resources/assets/create/textures/block/portal_track_mip.png new file mode 100644 index 0000000000000000000000000000000000000000..09bf58da1a059dc8611f6a4086ce546c648b3c95 GIT binary patch literal 944 zcmV;h15f;kP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D14T(hK~z{r?N&`| zBt;m0sy}CXCfhwp$huJ&mmtYu&r8m^`zr!o^y1MUAl^NBSa16S6a*1eOb*Kmx-4vj z$bz9KhD@88^vBGkzoxrFvXY8xPq z6I=!^c$zgXmOXm(1uq6KU|ELfT%#DInEW8uit{@F|5jkVc`bY(;uF4<4)~^;VQ^r z+>xmUBndF_Vg!DKB+Bqk-@;$c1m}ZE+58#|oBb8sf6v4$$nmG`Va3!jaw4ShoMKn_ z)&8kD2@k)0LifO22+L~Y%=SgSK*!WD^T~y5h zQapILj-fphP0j~1{P_IqMgx*_8X}zJP$dP!(;2cv60~{-f{Qt1ip!Erx-A%7CfHc- zL1GF~7W6M;idks4R6&VJV2sfp%yH?&Jm&3C&=oUlha7tuK z%7EkZ!%v!%(E06(&m%65N{c%AgKF?Xn+&mE*s3O?Vt9~4qGa!G)#Fj0;~cWa@b+G5 zJobRiT?zgP^`$=30mr9becWh33eQyVnFdwSP}HSVhfM6QYDmW#G({(B=&tGLtm??T zHo6;a=t`@i4(_+HvO#r&-UCEcrg3DtP3H=di{c)+c_X|EHa+EBzxBR|T{_d1o`>}N zXf+eN)HRZu%kmm>3((B6F1BmC{1XM-BjB^|KWg5B<$Iti7lx&QrGYoy0Pr{E4{-&i SoVdgQ0000