Create/src/main/java/com/simibubi/create/content/logistics/trains/entity/Train.java
simibubi e5c6ca157c Squashed commit of the following:
commit 053dd09df6c426ab5e570f42a1edb5df3d0fbd01
Merge: 6d1e1c71d ecc645eba
Author: simibubi <31564874+simibubi@users.noreply.github.com>
Date:   Tue May 9 18:22:42 2023 +0200

    Merge branch '1.18/api' of https://github.com/Layers-of-Railways/Create into pr/4692

commit ecc645eba7
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Tue May 9 11:24:11 2023 +0100

    Implemented support for creating and removing individual blockstate models

commit 6d1e1c71de7ce20f6fd9fc8ed4ed9bdd1072829a
Author: simibubi <31564874+simibubi@users.noreply.github.com>
Date:   Tue May 9 12:16:54 2023 +0200

    Less error logging when migrating old worlds

commit 205e47352e
Author: techno-sam <linux.techno.sam@gmail.com>
Date:   Mon May 8 21:02:19 2023 -0700

    Fix up ItemOutline

commit 6cf204f6af
Merge: fe049bc77 2e3c906ce
Author: techno-sam <linux.techno.sam@gmail.com>
Date:   Mon May 8 20:28:56 2023 -0700

    Merge remote-tracking branch 'upstream/mc1.18/dev' into 1.18/api

    # Conflicts:
    #	src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageCouplingRenderer.java

commit fe049bc771
Author: techno-sam <linux.techno.sam@gmail.com>
Date:   Mon May 8 20:26:16 2023 -0700

    Revert "Revert "Rewrite outline buffering""

    This reverts commit 726bfaf0

commit 435b4c1c16
Author: techno-sam <linux.techno.sam@gmail.com>
Date:   Mon May 8 20:20:23 2023 -0700

    Clean up last bits of upside down rendering

commit 662da6bab1
Merge: 122fe77af d83285e8a
Author: techno-sam <linux.techno.sam@gmail.com>
Date:   Mon May 8 20:16:32 2023 -0700

    Merge remote-tracking branch 'origin/1.18/api' into 1.18/api

    # Conflicts:
    #	src/main/java/com/simibubi/create/content/logistics/trains/StandardBogeyRenderer.java

commit 122fe77afa
Author: techno-sam <linux.techno.sam@gmail.com>
Date:   Mon May 8 20:15:46 2023 -0700

    Fix up upside down rendering

commit d83285e8a4
Merge: 00e953a58 cdb0ad210
Author: techno-sam <77073745+techno-sam@users.noreply.github.com>
Date:   Sun May 7 07:02:18 2023 -0700

    Merge pull request #3 from Layers-of-Railways/1.18/bogey-api

    Cleanup cycle groups and unused imports

commit cdb0ad210b
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Sun May 7 10:15:47 2023 +0100

    Fixed merge artifact

commit 457d5f33ed
Merge: 4e4e227a3 00e953a58
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Sun May 7 10:14:07 2023 +0100

    Merge remote-tracking branch 'origin/1.18/api' into 1.18/api

commit 00e953a585
Merge: 1e4d5504e a7a25896c
Author: Rabbitminers <79579164+Rabbitminers@users.noreply.github.com>
Date:   Sun May 7 10:13:49 2023 +0100

    Merge pull request #2 from Rabbitminers/mc1.18/dev

    Added Return Values and Small Cleanup

commit a7a25896c1
Merge: 7622128be 1e4d5504e
Author: Rabbitminers <79579164+Rabbitminers@users.noreply.github.com>
Date:   Sun May 7 10:13:40 2023 +0100

    Merge branch '1.18/api' into mc1.18/dev

commit 4e4e227a35
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Sun May 7 10:10:30 2023 +0100

    Cleanup to cycle groups

commit aa94fc97d1
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Sun May 7 09:50:50 2023 +0100

    Removed unused import of Railways

commit 7622128bec
Merge: 81eeadb85 d52065808
Author: Rabbitminers <79579164+Rabbitminers@users.noreply.github.com>
Date:   Sun May 7 09:11:59 2023 +0100

    Merge branch 'Layers-of-Railways:mc1.18/dev' into mc1.18/dev

commit 1e4d5504ee
Author: techno-sam <linux.techno.sam@gmail.com>
Date:   Sat May 6 18:03:39 2023 -0700

    Don't revert non-buggy changes

commit b306cf2124
Author: techno-sam <linux.techno.sam@gmail.com>
Date:   Sat May 6 18:00:59 2023 -0700

    Take materials into consideration when trains pathfind

commit fca02ae4bf
Author: techno-sam <linux.techno.sam@gmail.com>
Date:   Sat May 6 10:25:51 2023 -0700

    Add materials to track graph

commit 726bfaf0b5
Author: techno-sam <linux.techno.sam@gmail.com>
Date:   Fri May 5 21:16:49 2023 -0700

    Revert "Rewrite outline buffering"

    This reverts commit d4106d545b.

commit 171897bed2
Author: techno-sam <linux.techno.sam@gmail.com>
Date:   Fri May 5 20:55:25 2023 -0700

    Fix up style cycling

commit cbd0cf20da
Author: techno-sam <linux.techno.sam@gmail.com>
Date:   Fri May 5 07:32:06 2023 -0700

    clean up nether portal carriage handling

commit d556f08876
Author: techno-sam <linux.techno.sam@gmail.com>
Date:   Fri May 5 07:06:02 2023 -0700

    upside down bogeys work in nether portals
    fixed coupling anchor offsets

commit da26c0ccbf
Author: techno-sam <linux.techno.sam@gmail.com>
Date:   Thu May 4 09:32:53 2023 -0700

    working on upside down bogeys in nether portals

commit 81eeadb853
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Mon May 1 16:15:28 2023 +0100

    Small cleanup

commit c7e9df973c
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Mon May 1 16:13:51 2023 +0100

    Fixed issue raised in #1

commit 2f285b6eb7
Author: techno-sam <linux.techno.sam@gmail.com>
Date:   Mon May 1 08:13:27 2023 -0700

    add data gen

commit 206de01311
Merge: e91753a33 6564f4fa7
Author: techno-sam <77073745+techno-sam@users.noreply.github.com>
Date:   Mon May 1 06:49:21 2023 -0700

    Merge pull request #1 from Rabbitminers/mc1.18/dev

    Bogey API

commit 6564f4fa73
Merge: e5d759582 e91753a33
Author: Rabbitminers <79579164+Rabbitminers@users.noreply.github.com>
Date:   Mon May 1 10:40:32 2023 +0100

    Merge branch '1.18/api' into mc1.18/dev

commit e5d7595822
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Mon May 1 10:09:03 2023 +0100

    Connected Custom Bogey Particle Types To CarriageParticles

commit e91753a33c
Author: techno-sam <linux.techno.sam@gmail.com>
Date:   Sun Apr 30 19:51:26 2023 -0700

    Fix up some problems

commit 9815f1490f
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Sun Apr 30 21:12:43 2023 +0100

    Implemented default data when shifting styles

commit da30e78815
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Sun Apr 30 21:12:14 2023 +0100

    Added Particles To Bogey Style (And Respective Builder)

commit 08c000b8ba
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Sun Apr 30 21:01:19 2023 +0100

    Added Backup Rendering If A Size Is Not Present

commit 2b76e8d7b3
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Sun Apr 30 21:00:40 2023 +0100

    Added Common Renderer To Remove Function

commit 411ec36f57
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Sun Apr 30 20:59:50 2023 +0100

    Added Display Name To Standard Bogey Style

commit 112306d5d4
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Sun Apr 30 20:59:30 2023 +0100

    Displayed new style name when changing betweeen them

commit 5634670b27
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Sun Apr 30 20:06:00 2023 +0100

    General Cleanup

commit 0f7a8b7b24
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Sun Apr 30 20:05:50 2023 +0100

    Implemented Changes To Remaining Classes

commit 8aedc00f96
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Sun Apr 30 20:02:06 2023 +0100

    Removed Bogey Style Handling From Registrate

commit edf8079abf
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Sun Apr 30 20:01:40 2023 +0100

    Removed Unused Registry Handling

commit 6a185c4e72
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Sun Apr 30 20:01:16 2023 +0100

    Refactored Bogey Sizes

commit e10d07ddc3
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Sun Apr 30 20:01:00 2023 +0100

    Overhauled Bogey Style

commit 74d98a2ad5
Merge: e629d02f5 4ebcf8201
Author: techno-sam <77073745+techno-sam@users.noreply.github.com>
Date:   Sun Apr 23 07:16:33 2023 -0700

    Merge branch 'Creators-of-Create:mc1.18/dev' into 1.18/api

commit e629d02f50
Author: techno-sam <linux.techno.sam@gmail.com>
Date:   Sun Apr 9 07:18:22 2023 -0700

    Track API

    Clean up code a bit

commit d9ce6ce995
Author: techno-sam <linux.techno.sam@gmail.com>
Date:   Sun Apr 9 07:14:46 2023 -0700

    Track API?

    Fix placement

commit 7fbf08ba54
Author: techno-sam <linux.techno.sam@gmail.com>
Date:   Sat Apr 8 11:11:24 2023 -0700

    Track API?

    Fix up some placement issues

commit 35644f1434
Author: techno-sam <linux.techno.sam@gmail.com>
Date:   Sat Apr 8 08:11:13 2023 -0700

    Track API maybe?

    Datagen
    Seems to be working

commit f7c56b867a
Author: techno-sam <linux.techno.sam@gmail.com>
Date:   Thu Apr 6 21:24:31 2023 -0700

    Track API maybe?

    Fix build - broken generic
    Not yet tested, but it is progress

commit 2a59fd7e8a
Author: techno-sam <linux.techno.sam@gmail.com>
Date:   Thu Apr 6 21:13:54 2023 -0700

    Track API maybe?

    Not yet tested, but it is progress

commit 5ba30d6a85
Merge: e4e5ac1c4 c2977bbff
Author: techno-sam <77073745+techno-sam@users.noreply.github.com>
Date:   Thu Apr 6 17:10:39 2023 -0700

    Merge branch 'Creators-of-Create:mc1.18/dev' into 1.18/api

commit d52065808c
Merge: e4e5ac1c4 c2977bbff
Author: techno-sam <77073745+techno-sam@users.noreply.github.com>
Date:   Thu Apr 6 17:10:26 2023 -0700

    Merge branch 'Creators-of-Create:mc1.18/dev' into mc1.18/dev

commit 53240bd42f
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Mon Apr 3 21:42:29 2023 +0100

    Corrected Bogey InteractionResult To Pass

commit 69326e361a
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Mon Apr 3 21:30:28 2023 +0100

    Fixed Default Values When Used Styles Are Removed

commit 4f176979de
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Mon Apr 3 19:33:17 2023 +0100

    Fixed Carriage Sounds (Again)

commit 1e80af3303
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Mon Apr 3 19:27:58 2023 +0100

    Refactored Bogey Sizes To Seperate Class

commit 129be61fee
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Mon Apr 3 17:20:17 2023 +0100

    Fixed Bogey Sound Loading

commit 2543185a55
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Mon Apr 3 09:45:23 2023 +0100

    Added Bogey Sound Customisation

commit 1ad5ae9514
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Mon Apr 3 00:44:53 2023 +0100

    Added Size Transforms If Size Is Not Available For New Style

commit 96566b1614
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Sun Apr 2 23:02:02 2023 +0100

    Moved Bogey Style Inside Of Bogey Data And Implemented Bogey Data Communication

commit eedd984738
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Sun Apr 2 16:53:55 2023 +0100

    Fixed Large Bogey Size

commit 68ca0974c6
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Sun Apr 2 16:47:58 2023 +0100

    Implemented Style Cycling & Default Values

commit a55ba4267a
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Sun Apr 2 16:46:15 2023 +0100

    Implemented renderer instance creator

commit 43523302c2
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Sun Apr 2 16:45:33 2023 +0100

    Removed Unused Standard Bogey Instance

commit 773e084422
Merge: 0c0b5a1ed d1e1f7ec5
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Sat Apr 1 18:50:15 2023 +0100

    Merge remote-tracking branch 'origin/mc1.18/dev' into mc1.18/dev

    # Conflicts:
    #	src/main/java/com/simibubi/create/AllBogeyStyles.java
    #	src/main/java/com/simibubi/create/content/logistics/trains/BogeyTileEntityRenderer.java
    #	src/main/java/com/simibubi/create/content/logistics/trains/entity/BogeyStyle.java
    #	src/main/java/com/simibubi/create/content/logistics/trains/entity/CarriageContraptionEntityRenderer.java
    #	src/main/java/com/simibubi/create/content/logistics/trains/entity/StandardBogeyInstance.java
    #	src/main/java/com/simibubi/create/foundation/data/BogeyStyleBuilder.java

commit 0c0b5a1ed6
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Sat Apr 1 18:39:58 2023 +0100

    Linked Style Registry To Bogey Blocks

commit 71f839ee51
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Sat Apr 1 18:39:03 2023 +0100

    Replaced size boolean with direct use of size enum

commit 50ff081704
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Thu Mar 30 18:47:13 2023 +0100

    Added Resource Location To NBT helper methods

commit d1e1f7ec5a
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Thu Mar 30 18:47:13 2023 +0100

    Re-worked BogeyStyles

commit da593fccb1
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Thu Mar 30 18:46:02 2023 +0100

    Refactored IBogeyBlock to AbstractBogeyBlock and extracted relevant StandardBogeyBlock implementations

commit 17432c9113
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Sat Mar 25 10:20:50 2023 +0000

    Fixed Incorrect Registry Loading

commit c7d899369a
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Fri Mar 24 23:44:03 2023 +0000

    Registered Registers

commit 6d862290d7
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Fri Mar 24 23:43:23 2023 +0000

    Added BogeyStyleBuilder To Registrate

commit 3dfb9e3b3b
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Fri Mar 24 23:43:08 2023 +0000

    Implemented AllBogeyStyles

commit c9e71b462d
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Fri Mar 24 23:42:56 2023 +0000

    Created BogeyStyleBuilder

commit a90977d642
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Fri Mar 24 23:42:25 2023 +0000

    Created AllRegistries and BogeyStyle Registry

commit 154d455f3f
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Fri Mar 24 23:41:56 2023 +0000

    Added BogeyStyle Wrapper

commit dfb7640bfc
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Thu Mar 23 18:50:41 2023 +0000

    Removed left over logging statement

commit 9920536cc3
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Thu Mar 23 18:50:18 2023 +0000

    Implemented Secondary Shaft To Large Renderer

commit 6cd40cc6f9
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Thu Mar 23 18:49:56 2023 +0000

    Prevented Overwrite When Using Two BlockStates Of The Same Type With Different Properties

commit 06fb901144
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Thu Mar 23 18:39:11 2023 +0000

    Implemented Common Rendering For StandardBogeyRenderer

commit 435b0f8266
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Thu Mar 23 18:38:40 2023 +0000

    Added Common Renderer

commit 96a0623dab
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Thu Mar 23 18:38:29 2023 +0000

    Implemented BlockState Models For Rendering

commit 469d9d592b
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Thu Mar 23 17:42:28 2023 +0000

    Added Standard Bogey Instance (Might be redundant)

commit 2661d260d8
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Thu Mar 23 17:42:06 2023 +0000

    Refactored Changes To Existing Methods

commit 9ded16fbab
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Thu Mar 23 17:41:15 2023 +0000

    Integrated BogeyRenderer To BogeyInstance (Also Corrected Rendering In Contraption)

commit 4a82fcbca1
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Thu Mar 23 17:40:13 2023 +0000

    Implemented Changes To StandardBogeyBlock

commit 7238fb93f3
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Thu Mar 23 17:39:51 2023 +0000

    Added Renderer To IBogeyBlock

commit ded4c1f613
Merge: 91727cc84 3c02fe6ec
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Wed Mar 22 17:03:37 2023 +0000

    Merge remote-tracking branch 'origin/mc1.18/dev' into mc1.18/dev

commit 91727cc84a
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Wed Mar 22 17:03:28 2023 +0000

    Implemented Model Data Initializer to StandardBogeyRenderer

commit 6d98a1f469
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Wed Mar 22 17:03:00 2023 +0000

    Added Contraption Model Instance Initializer

commit 3c02fe6ecc
Author: Rabbitminers <79579164+Rabbitminers@users.noreply.github.com>
Date:   Tue Mar 21 22:45:34 2023 +0000

    Added missing render type check

commit 6672c49649
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Tue Mar 21 22:37:36 2023 +0000

    Re-created standard bogey with test api

commit a8a9491fa0
Author: Rabbitminers <Rabbitminers2.0@gmail.com>
Date:   Tue Mar 21 22:34:54 2023 +0000

    Implemented Proof Of Concept Generic Bogey Renderer

commit e4e5ac1c40
Author: SpottyTheTurtle <69260662+SpottyTheTurtle@users.noreply.github.com>
Date:   Sat Mar 11 21:34:59 2023 +0000

    init
2023-05-09 18:23:47 +02:00

1244 lines
40 KiB
Java

package com.simibubi.create.content.logistics.trains.entity;
import java.util.ArrayList;
import java.util.Collection;
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.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import javax.annotation.Nullable;
import com.simibubi.create.content.logistics.trains.track.AbstractBogeyTileEntity;
import net.minecraft.world.level.block.entity.BlockEntity;
import org.apache.commons.lang3.mutable.MutableBoolean;
import org.apache.commons.lang3.mutable.MutableObject;
import com.simibubi.create.AllMovementBehaviours;
import com.simibubi.create.Create;
import com.simibubi.create.content.contraptions.components.structureMovement.MovementBehaviour;
import com.simibubi.create.content.logistics.item.filter.FilterItem;
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.SteerDirection;
import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgeData;
import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointType;
import com.simibubi.create.content.logistics.trains.management.edgePoint.observer.TrackObserver;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalBlock.SignalType;
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.station.GlobalStation;
import com.simibubi.create.content.logistics.trains.management.edgePoint.station.StationTileEntity;
import com.simibubi.create.content.logistics.trains.management.schedule.ScheduleRuntime;
import com.simibubi.create.content.logistics.trains.management.schedule.ScheduleRuntime.State;
import com.simibubi.create.foundation.advancement.AllAdvancements;
import com.simibubi.create.foundation.config.AllConfigs;
import com.simibubi.create.foundation.networking.AllPackets;
import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.foundation.utility.NBTHelper;
import com.simibubi.create.foundation.utility.Pair;
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.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;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Explosion.BlockInteraction;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.common.ForgeHooks;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.capability.IFluidHandler;
import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction;
import net.minecraftforge.items.IItemHandlerModifiable;
import net.minecraftforge.items.ItemHandlerHelper;
import net.minecraftforge.network.PacketDistributor;
public class Train {
public double speed = 0;
public double targetSpeed = 0;
public Double speedBeforeStall = null;
public int carriageWaitingForChunks = -1;
public double throttle = 1;
public boolean honk = false;
public UUID id;
public UUID owner;
public TrackGraph graph;
public Navigation navigation;
public ScheduleRuntime runtime;
public TrainIconType icon;
public Component name;
public TrainStatus status;
public boolean invalid;
public SteerDirection manualSteer;
public boolean manualTick;
public UUID currentStation;
public boolean currentlyBackwards;
public boolean doubleEnded;
public List<Carriage> carriages;
public List<Integer> carriageSpacing;
public boolean updateSignalBlocks;
public Map<UUID, UUID> occupiedSignalBlocks;
public Set<UUID> reservedSignalBlocks;
public Set<UUID> occupiedObservers;
public Map<UUID, Pair<Integer, Boolean>> cachedObserverFiltering;
List<TrainMigration> migratingPoints;
public int migrationCooldown;
public boolean derailed;
public int fuelTicks;
public int honkTicks;
public Boolean lowHonk;
public int honkPitch;
public float accumulatedSteamRelease;
int tickOffset;
double[] stress;
// advancements
public Player backwardsDriver;
public Train(UUID id, UUID owner, TrackGraph graph, List<Carriage> carriages, List<Integer> carriageSpacing,
boolean doubleEnded) {
this.id = id;
this.owner = owner;
this.graph = graph;
this.carriages = carriages;
this.carriageSpacing = carriageSpacing;
this.icon = TrainIconType.getDefault();
this.stress = new double[carriageSpacing.size()];
this.name = Lang.translateDirect("train.unnamed");
this.status = new TrainStatus(this);
this.doubleEnded = doubleEnded;
carriages.forEach(c -> c.setTrain(this));
navigation = new Navigation(this);
runtime = new ScheduleRuntime(this);
migratingPoints = new ArrayList<>();
currentStation = null;
manualSteer = SteerDirection.NONE;
occupiedSignalBlocks = new HashMap<>();
reservedSignalBlocks = new HashSet<>();
occupiedObservers = new HashSet<>();
cachedObserverFiltering = new HashMap<>();
tickOffset = Create.RANDOM.nextInt(100);
}
public void earlyTick(Level level) {
status.tick(level);
if (graph == null && !migratingPoints.isEmpty())
reattachToTracks(level);
if (graph == null) {
addToSignalGroups(occupiedSignalBlocks.keySet());
return;
}
if (updateSignalBlocks) {
updateSignalBlocks = false;
collectInitiallyOccupiedSignalBlocks();
}
addToSignalGroups(occupiedSignalBlocks.keySet());
addToSignalGroups(reservedSignalBlocks);
if (occupiedObservers.isEmpty())
return;
tickOccupiedObservers(level);
}
private void tickOccupiedObservers(Level level) {
int storageVersion = 0;
for (Carriage carriage : carriages)
storageVersion += carriage.storage.getVersion();
for (UUID uuid : occupiedObservers) {
TrackObserver observer = graph.getPoint(EdgePointType.OBSERVER, uuid);
if (observer == null)
continue;
ItemStack filter = observer.getFilter();
if (filter.isEmpty()) {
observer.keepAlive(this);
continue;
}
Pair<Integer, Boolean> cachedMatch = cachedObserverFiltering.computeIfAbsent(uuid, $ -> Pair.of(-1, false));
boolean shouldActivate = cachedMatch.getSecond();
if (cachedMatch.getFirst() == storageVersion) {
if (shouldActivate)
observer.keepAlive(this);
continue;
}
shouldActivate = false;
for (Carriage carriage : carriages) {
if (shouldActivate)
break;
IItemHandlerModifiable inv = carriage.storage.getItems();
if (inv != null) {
for (int slot = 0; slot < inv.getSlots(); slot++) {
if (shouldActivate)
break;
ItemStack extractItem = inv.extractItem(slot, 1, true);
if (extractItem.isEmpty())
continue;
shouldActivate |= FilterItem.test(level, extractItem, filter);
}
}
IFluidHandler tank = carriage.storage.getFluids();
if (tank != null) {
for (int slot = 0; slot < tank.getTanks(); slot++) {
if (shouldActivate)
break;
FluidStack drain = tank.drain(1, FluidAction.SIMULATE);
if (drain.isEmpty())
continue;
shouldActivate |= FilterItem.test(level, drain, filter);
}
}
}
cachedObserverFiltering.put(uuid, Pair.of(storageVersion, shouldActivate));
if (shouldActivate)
observer.keepAlive(this);
}
}
private void addToSignalGroups(Collection<UUID> groups) {
Map<UUID, SignalEdgeGroup> groupMap = Create.RAILWAYS.signalEdgeGroups;
for (Iterator<UUID> iterator = groups.iterator(); iterator.hasNext();) {
SignalEdgeGroup signalEdgeGroup = groupMap.get(iterator.next());
if (signalEdgeGroup == null)
iterator.remove();
else
signalEdgeGroup.trains.add(this);
}
}
public void tick(Level level) {
Create.RAILWAYS.markTracksDirty();
if (graph == null) {
carriages.forEach(c -> c.manageEntities(level));
updateConductors();
return;
}
updateConductors();
runtime.tick(level);
navigation.tick(level);
tickPassiveSlowdown();
if (derailed)
tickDerailedSlowdown();
double distance = speed;
Carriage previousCarriage = null;
int carriageCount = carriages.size();
boolean stalled = false;
double maxStress = 0;
if (carriageWaitingForChunks != -1)
distance = 0;
for (int i = 0; i < carriageCount; i++) {
Carriage carriage = carriages.get(i);
if (previousCarriage != null) {
int target = carriageSpacing.get(i - 1);
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<Level> d1 = leadingPoint.node1.getLocation().dimension;
ResourceKey<Level> d2 = trailingPoint.node1.getLocation().dimension;
for (boolean b : Iterate.trueAndFalse) {
ResourceKey<Level> d = b ? d1 : d2;
if (!b && d1.equals(d2))
continue;
if (!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;
double distanceTo = leadingAnchor.distanceToSqr(trailingAnchor);
if (carriage.leadingBogey().isUpsideDown() != previousCarriage.trailingBogey().isUpsideDown()) {
distanceTo = Math.sqrt(distanceTo - 4);
} else {
distanceTo = Math.sqrt(distanceTo);
}
total += distanceTo;
entries++;
}
}
if (entries > 0)
actual = total / entries;
stress[i - 1] = target - actual;
maxStress = Math.max(maxStress, Math.abs(target - actual));
}
previousCarriage = carriage;
if (carriage.stalled) {
if (speedBeforeStall == null)
speedBeforeStall = speed;
distance = 0;
speed = 0;
stalled = true;
}
}
if (!stalled && speedBeforeStall != null) {
speed = Mth.clamp(speedBeforeStall, -1, 1);
speedBeforeStall = null;
}
// positive stress: carriages should move apart
// negative stress: carriages should move closer
boolean approachingStation = navigation.distanceToDestination < 5;
double leadingModifier = approachingStation ? 0.75d : 0.5d;
double trailingModifier = approachingStation ? 0d : 0.125d;
boolean blocked = false;
boolean iterateFromBack = speed < 0;
for (int index = 0; index < carriageCount; index++) {
int i = iterateFromBack ? carriageCount - 1 - index : index;
double leadingStress = i == 0 ? 0 : stress[i - 1] * -(iterateFromBack ? trailingModifier : leadingModifier);
double trailingStress =
i == stress.length ? 0 : stress[i] * (iterateFromBack ? leadingModifier : trailingModifier);
Carriage carriage = carriages.get(i);
TravellingPoint toFollowForward = i == 0 ? null
: carriages.get(i - 1)
.getTrailingPoint();
TravellingPoint toFollowBackward = i == carriageCount - 1 ? null
: carriages.get(i + 1)
.getLeadingPoint();
double totalStress = derailed ? 0 : leadingStress + trailingStress;
boolean first = i == 0;
boolean last = i == carriageCount - 1;
int carriageType = first ? last ? Carriage.BOTH : Carriage.FIRST : last ? Carriage.LAST : Carriage.MIDDLE;
double actualDistance =
carriage.travel(level, graph, distance + totalStress, toFollowForward, toFollowBackward, carriageType);
blocked |= carriage.blocked || carriage.isOnIncompatibleTrack();
boolean onTwoBogeys = carriage.isOnTwoBogeys();
maxStress = Math.max(maxStress, onTwoBogeys ? carriage.bogeySpacing - carriage.getAnchorDiff() : 0);
maxStress = Math.max(maxStress, carriage.leadingBogey()
.getStress());
if (onTwoBogeys)
maxStress = Math.max(maxStress, carriage.trailingBogey()
.getStress());
if (index == 0) {
distance = actualDistance;
collideWithOtherTrains(level, carriage);
backwardsDriver = null;
if (graph == null)
return;
}
}
if (blocked) {
speed = 0;
navigation.cancelNavigation();
runtime.tick(level);
status.endOfTrack();
} else if (maxStress > 4) {
speed = 0;
navigation.cancelNavigation();
runtime.tick(level);
derailed = true;
status.highStress();
} else if (speed != 0)
status.trackOK();
updateNavigationTarget(distance);
}
public IEdgePointListener frontSignalListener() {
return (distance, couple) -> {
if (couple.getFirst()instanceof GlobalStation station) {
if (!station.canApproachFrom(couple.getSecond()
.getSecond()) || navigation.destination != station)
return false;
speed = 0;
navigation.distanceToDestination = 0;
navigation.currentPath.clear();
arriveAt(navigation.destination);
navigation.destination = null;
return true;
}
if (couple.getFirst()instanceof TrackObserver observer) {
occupiedObservers.add(observer.getId());
return false;
}
if (!(couple.getFirst()instanceof SignalBoundary signal))
return false;
if (navigation.waitingForSignal != null && navigation.waitingForSignal.getFirst()
.equals(signal.getId())) {
speed = 0;
navigation.distanceToSignal = 0;
return true;
}
UUID groupId = signal.getGroup(couple.getSecond()
.getSecond());
SignalEdgeGroup signalEdgeGroup = Create.RAILWAYS.signalEdgeGroups.get(groupId);
if (signalEdgeGroup == null)
return false;
if ((runtime.getSchedule() == null || runtime.paused) && signalEdgeGroup.isOccupiedUnless(this))
carriages.forEach(c -> c.forEachPresentEntity(cce -> cce.getControllingPlayer()
.ifPresent(uuid -> AllAdvancements.RED_SIGNAL.awardTo(cce.level.getPlayerByUUID(uuid)))));
signalEdgeGroup.reserved = signal;
occupy(groupId, signal.id);
return false;
};
}
public void cancelStall() {
speedBeforeStall = null;
carriages.forEach(c -> {
c.stalled = false;
c.forEachPresentEntity(cce -> cce.getContraption()
.getActors()
.forEach(pair -> {
MovementBehaviour behaviour = AllMovementBehaviours.getBehaviour(pair.getKey().state);
if (behaviour != null)
behaviour.cancelStall(pair.getValue());
}));
});
}
private boolean occupy(UUID groupId, @Nullable UUID boundaryId) {
reservedSignalBlocks.remove(groupId);
if (boundaryId != null && occupiedSignalBlocks.containsKey(groupId))
if (boundaryId.equals(occupiedSignalBlocks.get(groupId)))
return false;
return occupiedSignalBlocks.put(groupId, boundaryId) == null;
}
public IEdgePointListener backSignalListener() {
return (distance, couple) -> {
if (couple.getFirst()instanceof TrackObserver observer) {
occupiedObservers.remove(observer.getId());
cachedObserverFiltering.remove(observer.getId());
return false;
}
if (!(couple.getFirst()instanceof SignalBoundary signal))
return false;
UUID groupId = signal.getGroup(couple.getSecond()
.getFirst());
occupiedSignalBlocks.remove(groupId);
return false;
};
}
private void updateNavigationTarget(double distance) {
if (navigation.destination == null)
return;
Pair<UUID, Boolean> blockingSignal = navigation.waitingForSignal;
boolean fullRefresh = navigation.distanceToDestination > 100 && navigation.distanceToDestination % 100 > 20;
boolean signalRefresh = blockingSignal != null && navigation.distanceToSignal % 50 > 5;
boolean partialRefresh = navigation.distanceToDestination < 100 && navigation.distanceToDestination % 50 > 5;
double toSubstract = navigation.destinationBehindTrain ? -distance : distance;
boolean navigatingManually = runtime.paused;
navigation.distanceToDestination -= toSubstract;
if (blockingSignal != null) {
navigation.distanceToSignal -= toSubstract;
signalRefresh &= navigation.distanceToSignal % 50 < 5;
}
fullRefresh &= navigation.distanceToDestination % 100 <= 20;
partialRefresh &= navigation.distanceToDestination % 50 <= 5;
if (blockingSignal != null && navigation.ticksWaitingForSignal % 100 == 50) {
SignalBoundary signal = graph.getPoint(EdgePointType.SIGNAL, blockingSignal.getFirst());
fullRefresh |= signal != null && signal.types.get(blockingSignal.getSecond()) == SignalType.CROSS_SIGNAL;
}
if (signalRefresh)
navigation.waitingForSignal = null;
if (!fullRefresh && !partialRefresh)
return;
if (!reservedSignalBlocks.isEmpty())
return;
GlobalStation destination = navigation.destination;
if (!navigatingManually && fullRefresh) {
GlobalStation preferredDestination = runtime.startCurrentInstruction();
if (preferredDestination != null)
destination = preferredDestination;
}
navigation.startNavigation(destination, navigatingManually ? -1 : Double.MAX_VALUE, false);
}
private void tickDerailedSlowdown() {
speed /= 3f;
if (Mth.equal(speed, 0))
speed = 0;
}
private void tickPassiveSlowdown() {
if (!manualTick && navigation.destination == null && speed != 0) {
double acceleration = acceleration();
if (speed > 0) {
speed = Math.max(speed - acceleration, 0);
} else
speed = Math.min(speed + acceleration, 0);
}
manualTick = false;
}
private void updateConductors() {
for (Carriage carriage : carriages)
carriage.updateConductors();
}
public boolean hasForwardConductor() {
for (Carriage carriage : carriages)
if (carriage.presentConductors.getFirst())
return true;
return false;
}
public boolean hasBackwardConductor() {
for (Carriage carriage : carriages)
if (carriage.presentConductors.getSecond())
return true;
return false;
}
private void collideWithOtherTrains(Level level, Carriage carriage) {
if (derailed)
return;
TravellingPoint trailingPoint = carriage.getTrailingPoint();
TravellingPoint leadingPoint = carriage.getLeadingPoint();
if (leadingPoint.node1 == null || trailingPoint.node1 == null)
return;
ResourceKey<Level> 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<Train, Vec3> collision = findCollidingTrain(level, start, end, this, dimension);
if (collision == null)
return;
Train train = collision.getFirst();
double combinedSpeed = Math.abs(speed) + Math.abs(train.speed);
if (combinedSpeed > .2f) {
Vec3 v = collision.getSecond();
level.explode(null, v.x, v.y, v.z, (float) Math.min(3 * combinedSpeed, 5), BlockInteraction.NONE);
}
crash();
train.crash();
}
public static Pair<Train, Vec3> findCollidingTrain(Level level, Vec3 start, Vec3 end, Train ignore,
ResourceKey<Level> dimension) {
for (Train train : Create.RAILWAYS.sided(level).trains.values()) {
if (train == ignore)
continue;
Vec3 diff = end.subtract(start);
Vec3 lastPoint = null;
for (Carriage otherCarriage : train.carriages) {
for (boolean betweenBits : Iterate.trueAndFalse) {
if (betweenBits && lastPoint == null)
continue;
TravellingPoint otherLeading = otherCarriage.getLeadingPoint();
TravellingPoint otherTrailing = otherCarriage.getTrailingPoint();
if (otherLeading.edge == null || otherTrailing.edge == null)
continue;
ResourceKey<Level> 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();
if (betweenBits) {
end2 = start2;
start2 = lastPoint;
}
lastPoint = end2;
if ((end.y < end2.y - 3 || end2.y < end.y - 3)
&& (start.y < start2.y - 3 || start2.y < start.y - 3))
continue;
Vec3 diff2 = end2.subtract(start2);
Vec3 normedDiff = diff.normalize();
Vec3 normedDiff2 = diff2.normalize();
double[] intersect = VecHelper.intersect(start, start2, normedDiff, normedDiff2, Axis.Y);
if (intersect == null) {
Vec3 intersectSphere = VecHelper.intersectSphere(start2, normedDiff2, start, .125f);
if (intersectSphere == null)
continue;
if (!Mth.equal(normedDiff2.dot(intersectSphere.subtract(start2)
.normalize()), 1))
continue;
intersect = new double[2];
intersect[0] = intersectSphere.distanceTo(start) - .125;
intersect[1] = intersectSphere.distanceTo(start2) - .125;
}
if (intersect[0] > diff.length())
continue;
if (intersect[1] > diff2.length())
continue;
if (intersect[0] < 0)
continue;
if (intersect[1] < 0)
continue;
return Pair.of(train, start.add(normedDiff.scale(intersect[0])));
}
}
}
return null;
}
public void crash() {
navigation.cancelNavigation();
if (derailed)
return;
speed = -Mth.clamp(speed, -.5, .5);
derailed = true;
graph = null;
status.crash();
for (Carriage carriage : carriages)
carriage.forEachPresentEntity(e -> e.getIndirectPassengers()
.forEach(entity -> {
if (!(entity instanceof Player p))
return;
Optional<UUID> controllingPlayer = e.getControllingPlayer();
if (controllingPlayer.isPresent() && controllingPlayer.get()
.equals(p.getUUID()))
return;
AllAdvancements.TRAIN_CRASH.awardTo(p);
}));
if (backwardsDriver != null)
AllAdvancements.TRAIN_CRASH_BACKWARDS.awardTo(backwardsDriver);
}
public boolean disassemble(Direction assemblyDirection, BlockPos pos) {
if (!canDisassemble())
return false;
int offset = 1;
boolean backwards = currentlyBackwards;
Level level = null;
for (int i = 0; i < carriages.size(); i++) {
Carriage carriage = carriages.get(backwards ? carriages.size() - i - 1 : i);
CarriageContraptionEntity entity = carriage.anyAvailableEntity();
if (entity == null)
return false;
level = entity.level;
if (entity.getContraption()instanceof CarriageContraption cc)
cc.returnStorageForDisassembly(carriage.storage);
entity.setPos(Vec3
.atLowerCornerOf(pos.relative(assemblyDirection, backwards ? offset + carriage.bogeySpacing : offset).below(carriage.leadingBogey().isUpsideDown() ? 2 : 0)));
entity.disassemble();
for (CarriageBogey bogey : carriage.bogeys) {
if (bogey == null)
continue;
Vec3 bogeyPosition = bogey.getAnchorPosition();
if (bogeyPosition == null) continue;
BlockEntity be = level.getBlockEntity(new BlockPos(bogeyPosition));
if (!(be instanceof AbstractBogeyTileEntity sbte))
continue;
sbte.setBogeyData(bogey.bogeyData);
}
offset += carriage.bogeySpacing;
if (i < carriageSpacing.size())
offset += carriageSpacing.get(backwards ? carriageSpacing.size() - i - 1 : i);
}
GlobalStation currentStation = getCurrentStation();
if (currentStation != null) {
currentStation.cancelReservation(this);
BlockPos tilePos = currentStation.getTilePos();
if (level.getBlockEntity(tilePos)instanceof StationTileEntity ste)
ste.lastDisassembledTrainName = name.copy();
}
Create.RAILWAYS.removeTrain(id);
AllPackets.channel.send(PacketDistributor.ALL.noArg(), new TrainPacket(this, false));
return true;
}
public boolean canDisassemble() {
for (Carriage carriage : carriages) {
if (carriage.presentInMultipleDimensions())
return false;
CarriageContraptionEntity entity = carriage.anyAvailableEntity();
if (entity == null)
return false;
if (!Mth.equal(entity.pitch, 0))
return false;
if (!Mth.equal(((entity.yaw % 90) + 360) % 90, 0))
return false;
}
return true;
}
public boolean isTravellingOn(TrackNode node) {
MutableBoolean affected = new MutableBoolean(false);
forEachTravellingPoint(tp -> {
if (tp.node1 == node || tp.node2 == node)
affected.setTrue();
});
return affected.booleanValue();
}
public void detachFromTracks() {
migratingPoints.clear();
navigation.cancelNavigation();
forEachTravellingPoint(tp -> migratingPoints.add(new TrainMigration(tp)));
graph = null;
}
public void forEachTravellingPoint(Consumer<TravellingPoint> callback) {
for (Carriage c : carriages) {
c.leadingBogey().points.forEach(callback::accept);
if (c.isOnTwoBogeys())
c.trailingBogey().points.forEach(callback::accept);
}
}
public void forEachTravellingPointBackwards(BiConsumer<TravellingPoint, Double> callback) {
double lastWheelOffset = 0;
for (int i = 0; i < carriages.size(); i++) {
int index = carriages.size() - i - 1;
Carriage carriage = carriages.get(index);
CarriageBogey trailingBogey = carriage.trailingBogey();
double trailSpacing = trailingBogey.type.getWheelPointSpacing();
// trailing point
callback.accept(trailingBogey.trailing(),
i == 0 ? 0 : carriageSpacing.get(index) - lastWheelOffset - trailSpacing / 2);
// inside 1st bogey
callback.accept(trailingBogey.leading(), trailSpacing);
lastWheelOffset = trailSpacing / 2;
if (!carriage.isOnTwoBogeys())
continue;
CarriageBogey leadingBogey = carriage.leadingBogey();
double leadSpacing = carriage.leadingBogey().type.getWheelPointSpacing();
// between bogeys
callback.accept(leadingBogey.trailing(), carriage.bogeySpacing - lastWheelOffset - leadSpacing / 2);
// inside 2nd bogey
callback.accept(trailingBogey.leading(), leadSpacing);
lastWheelOffset = leadSpacing / 2;
}
}
public void reattachToTracks(Level level) {
if (migrationCooldown > 0) {
migrationCooldown--;
return;
}
Set<Entry<UUID, TrackGraph>> entrySet = new HashSet<>(Create.RAILWAYS.trackNetworks.entrySet());
Map<UUID, List<GraphLocation>> successfulMigrations = new HashMap<>();
for (TrainMigration md : migratingPoints) {
for (Iterator<Entry<UUID, TrackGraph>> iterator = entrySet.iterator(); iterator.hasNext();) {
Entry<UUID, TrackGraph> entry = iterator.next();
GraphLocation gl = md.tryMigratingTo(entry.getValue());
if (gl == null) {
iterator.remove();
continue;
}
successfulMigrations.computeIfAbsent(entry.getKey(), uuid -> new ArrayList<>())
.add(gl);
}
}
if (entrySet.isEmpty()) {
migrationCooldown = 40;
status.failedMigration();
derailed = true;
return;
}
for (Entry<UUID, TrackGraph> entry : entrySet) {
graph = entry.getValue();
List<GraphLocation> locations = successfulMigrations.get(entry.getKey());
forEachTravellingPoint(tp -> tp.migrateTo(locations));
migratingPoints.clear();
if (derailed)
status.successfulMigration();
derailed = false;
if (runtime.getSchedule() != null && runtime.state == State.IN_TRANSIT)
runtime.state = State.PRE_TRANSIT;
GlobalStation currentStation = getCurrentStation();
if (currentStation != null)
currentStation.reserveFor(this);
updateSignalBlocks = true;
migrationCooldown = 0;
return;
}
}
public int getTotalLength() {
int length = 0;
for (int i = 0; i < carriages.size(); i++) {
Carriage carriage = carriages.get(i);
if (i == 0)
length += carriage.leadingBogey().type.getWheelPointSpacing() / 2;
if (i == carriages.size() - 1)
length += carriage.trailingBogey().type.getWheelPointSpacing() / 2;
length += carriage.bogeySpacing;
if (i < carriageSpacing.size())
length += carriageSpacing.get(i);
}
return length;
}
public void leaveStation() {
GlobalStation currentStation = getCurrentStation();
if (currentStation != null)
currentStation.trainDeparted(this);
this.currentStation = null;
}
public void arriveAt(GlobalStation station) {
setCurrentStation(station);
reservedSignalBlocks.clear();
runtime.destinationReached();
}
public void setCurrentStation(GlobalStation station) {
currentStation = station.id;
}
public GlobalStation getCurrentStation() {
if (currentStation == null)
return null;
if (graph == null)
return null;
return graph.getPoint(EdgePointType.STATION, currentStation);
}
@Nullable
public LivingEntity getOwner(Level level) {
try {
UUID uuid = owner;
return uuid == null ? null
: level.getServer()
.getPlayerList()
.getPlayer(uuid);
} catch (IllegalArgumentException illegalargumentexception) {
return null;
}
}
public void approachTargetSpeed(float accelerationMod) {
double actualTarget = targetSpeed;
if (Mth.equal(actualTarget, speed))
return;
if (manualTick)
leaveStation();
double acceleration = acceleration();
if (speed < actualTarget)
speed = Math.min(speed + acceleration * accelerationMod, actualTarget);
else if (speed > actualTarget)
speed = Math.max(speed - acceleration * accelerationMod, actualTarget);
}
public void collectInitiallyOccupiedSignalBlocks() {
TravellingPoint trailingPoint = carriages.get(carriages.size() - 1)
.getTrailingPoint();
TrackNode node1 = trailingPoint.node1;
TrackNode node2 = trailingPoint.node2;
TrackEdge edge = trailingPoint.edge;
double position = trailingPoint.position;
EdgeData signalData = edge.getEdgeData();
occupiedSignalBlocks.clear();
reservedSignalBlocks.clear();
occupiedObservers.clear();
cachedObserverFiltering.clear();
TravellingPoint signalScout = new TravellingPoint(node1, node2, edge, position, false);
Map<UUID, SignalEdgeGroup> allGroups = Create.RAILWAYS.signalEdgeGroups;
MutableObject<UUID> prevGroup = new MutableObject<>(null);
if (signalData.hasSignalBoundaries()) {
SignalBoundary nextBoundary = signalData.next(EdgePointType.SIGNAL, position);
if (nextBoundary == null) {
double d = 0;
SignalBoundary prev = null;
SignalBoundary current = signalData.next(EdgePointType.SIGNAL, 0);
while (current != null) {
prev = current;
d = current.getLocationOn(edge);
current = signalData.next(EdgePointType.SIGNAL, d);
}
if (prev != null) {
UUID group = prev.getGroup(node2);
if (Create.RAILWAYS.signalEdgeGroups.containsKey(group)) {
occupy(group, null);
prevGroup.setValue(group);
}
}
} else {
UUID group = nextBoundary.getGroup(node1);
if (Create.RAILWAYS.signalEdgeGroups.containsKey(group)) {
occupy(group, null);
prevGroup.setValue(group);
}
}
} else {
UUID groupId = signalData.getEffectiveEdgeGroupId(graph);
if (allGroups.containsKey(groupId)) {
occupy(groupId, null);
prevGroup.setValue(groupId);
}
}
forEachTravellingPointBackwards((tp, d) -> {
signalScout.travel(graph, d, signalScout.follow(tp), (distance, couple) -> {
if (couple.getFirst()instanceof TrackObserver observer) {
occupiedObservers.add(observer.getId());
return false;
}
if (!(couple.getFirst()instanceof SignalBoundary signal))
return false;
couple.getSecond()
.map(signal::getGroup)
.forEach(id -> {
if (!Create.RAILWAYS.signalEdgeGroups.containsKey(id))
return;
if (id.equals(prevGroup.getValue()))
return;
occupy(id, null);
prevGroup.setValue(id);
});
return false;
}, signalScout.ignoreTurns());
});
}
public boolean shouldCarriageSyncThisTick(long gameTicks, int updateInterval) {
return (gameTicks + tickOffset) % updateInterval == 0;
}
public Couple<Couple<TrackNode>> getEndpointEdges() {
return Couple.create(carriages.get(0)
.getLeadingPoint(),
carriages.get(carriages.size() - 1)
.getTrailingPoint())
.map(tp -> Couple.create(tp.node1, tp.node2));
}
public static class Penalties {
static final int STATION = 200, STATION_WITH_TRAIN = 300;
static final int MANUAL_TRAIN = 200, IDLE_TRAIN = 700, ARRIVING_TRAIN = 50, WAITING_TRAIN = 50, ANY_TRAIN = 25,
RED_SIGNAL = 25, REDSTONE_RED_SIGNAL = 400;
}
public int getNavigationPenalty() {
if (manualTick)
return Penalties.MANUAL_TRAIN;
if (runtime.getSchedule() == null || runtime.paused)
return Penalties.IDLE_TRAIN;
if (navigation.waitingForSignal != null && navigation.ticksWaitingForSignal > 0)
return Penalties.WAITING_TRAIN + Math.min(navigation.ticksWaitingForSignal / 20, 1000);
if (navigation.destination != null && navigation.distanceToDestination < 50 || navigation.distanceToSignal < 20)
return Penalties.ARRIVING_TRAIN;
return Penalties.ANY_TRAIN;
}
public void burnFuel() {
if (fuelTicks > 0) {
fuelTicks--;
return;
}
boolean iterateFromBack = speed < 0;
int carriageCount = carriages.size();
for (int index = 0; index < carriageCount; index++) {
int i = iterateFromBack ? carriageCount - 1 - index : index;
Carriage carriage = carriages.get(i);
IItemHandlerModifiable fuelItems = carriage.storage.getFuelItems();
if (fuelItems == null)
continue;
for (int slot = 0; slot < fuelItems.getSlots(); slot++) {
ItemStack stack = fuelItems.extractItem(slot, 1, true);
int burnTime = ForgeHooks.getBurnTime(stack, null);
if (burnTime <= 0)
continue;
stack = fuelItems.extractItem(slot, 1, false);
fuelTicks += burnTime * stack.getCount();
ItemStack containerItem = stack.getContainerItem();
if (!containerItem.isEmpty())
ItemHandlerHelper.insertItemStacked(fuelItems, containerItem, false);
return;
}
}
}
public float maxSpeed() {
return (fuelTicks > 0 ? AllConfigs.SERVER.trains.poweredTrainTopSpeed.getF()
: AllConfigs.SERVER.trains.trainTopSpeed.getF()) / 20;
}
public float maxTurnSpeed() {
return (fuelTicks > 0 ? AllConfigs.SERVER.trains.poweredTrainTurningTopSpeed.getF()
: AllConfigs.SERVER.trains.trainTurningTopSpeed.getF()) / 20;
}
public float acceleration() {
return (fuelTicks > 0 ? AllConfigs.SERVER.trains.poweredTrainAcceleration.getF()
: AllConfigs.SERVER.trains.trainAcceleration.getF()) / 400;
}
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, c -> c.write(dimensions)));
tag.putIntArray("CarriageSpacing", carriageSpacing);
tag.putBoolean("DoubleEnded", doubleEnded);
tag.putDouble("Speed", speed);
tag.putDouble("Throttle", throttle);
if (speedBeforeStall != null)
tag.putDouble("SpeedBeforeStall", speedBeforeStall);
tag.putInt("Fuel", fuelTicks);
tag.putDouble("TargetSpeed", targetSpeed);
tag.putString("IconType", icon.id.toString());
tag.putString("Name", Component.Serializer.toJson(name));
if (currentStation != null)
tag.putUUID("Station", currentStation);
tag.putBoolean("Backwards", currentlyBackwards);
tag.putBoolean("Derailed", derailed);
tag.putBoolean("UpdateSignals", updateSignalBlocks);
tag.put("SignalBlocks", NBTHelper.writeCompoundList(occupiedSignalBlocks.entrySet(), e -> {
CompoundTag compoundTag = new CompoundTag();
compoundTag.putUUID("Id", e.getKey());
if (e.getValue() != null)
compoundTag.putUUID("Boundary", e.getValue());
return compoundTag;
}));
tag.put("ReservedSignalBlocks", NBTHelper.writeCompoundList(reservedSignalBlocks, uid -> {
CompoundTag compoundTag = new CompoundTag();
compoundTag.putUUID("Id", uid);
return compoundTag;
}));
tag.put("OccupiedObservers", NBTHelper.writeCompoundList(occupiedObservers, uid -> {
CompoundTag compoundTag = new CompoundTag();
compoundTag.putUUID("Id", uid);
return compoundTag;
}));
tag.put("MigratingPoints", NBTHelper.writeCompoundList(migratingPoints, tm -> tm.write(dimensions)));
tag.put("Runtime", runtime.write());
tag.put("Navigation", navigation.write(dimensions));
return tag;
}
public static Train read(CompoundTag tag, Map<UUID, TrackGraph> 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<Carriage> carriages = new ArrayList<>();
NBTHelper.iterateCompoundList(tag.getList("Carriages", Tag.TAG_COMPOUND),
c -> carriages.add(Carriage.read(c, graph, dimensions)));
List<Integer> carriageSpacing = new ArrayList<>();
for (int i : tag.getIntArray("CarriageSpacing"))
carriageSpacing.add(i);
boolean doubleEnded = tag.getBoolean("DoubleEnded");
Train train = new Train(id, owner, graph, carriages, carriageSpacing, doubleEnded);
train.speed = tag.getDouble("Speed");
train.throttle = tag.getDouble("Throttle");
if (tag.contains("SpeedBeforeStall"))
train.speedBeforeStall = tag.getDouble("SpeedBeforeStall");
train.targetSpeed = tag.getDouble("TargetSpeed");
train.icon = TrainIconType.byId(new ResourceLocation(tag.getString("IconType")));
train.name = Component.Serializer.fromJson(tag.getString("Name"));
train.currentStation = tag.contains("Station") ? tag.getUUID("Station") : null;
train.currentlyBackwards = tag.getBoolean("Backwards");
train.derailed = tag.getBoolean("Derailed");
train.updateSignalBlocks = tag.getBoolean("UpdateSignals");
train.fuelTicks = tag.getInt("Fuel");
NBTHelper.iterateCompoundList(tag.getList("SignalBlocks", Tag.TAG_COMPOUND), c -> train.occupiedSignalBlocks
.put(c.getUUID("Id"), c.contains("Boundary") ? c.getUUID("Boundary") : null));
NBTHelper.iterateCompoundList(tag.getList("ReservedSignalBlocks", Tag.TAG_COMPOUND),
c -> train.reservedSignalBlocks.add(c.getUUID("Id")));
NBTHelper.iterateCompoundList(tag.getList("OccupiedObservers", Tag.TAG_COMPOUND),
c -> train.occupiedObservers.add(c.getUUID("Id")));
NBTHelper.iterateCompoundList(tag.getList("MigratingPoints", Tag.TAG_COMPOUND),
c -> train.migratingPoints.add(TrainMigration.read(c, dimensions)));
train.runtime.read(tag.getCompound("Runtime"));
train.navigation.read(tag.getCompound("Navigation"), graph, dimensions);
if (train.getCurrentStation() != null)
train.getCurrentStation()
.reserveFor(train);
return train;
}
public int countPlayerPassengers() {
AtomicInteger count = new AtomicInteger();
for (Carriage carriage : carriages)
carriage.forEachPresentEntity(e -> e.getIndirectPassengers()
.forEach(p -> {
if (p instanceof Player)
count.incrementAndGet();
}));
return count.intValue();
}
public void determineHonk(Level level) {
if (lowHonk != null)
return;
for (int index = 0; index < carriages.size(); index++) {
Carriage carriage = carriages.get(index);
DimensionalCarriageEntity dimensional = carriage.getDimensionalIfPresent(level.dimension());
if (dimensional == null)
return;
CarriageContraptionEntity entity = dimensional.entity.get();
if (entity == null || !(entity.getContraption()instanceof CarriageContraption otherCC))
break;
Pair<Boolean, Integer> first = otherCC.soundQueue.getFirstWhistle(entity);
if (first != null) {
lowHonk = first.getFirst();
honkPitch = first.getSecond();
}
}
}
public float distanceToLocationSqr(Level level, Vec3 location) {
float distance = Float.MAX_VALUE;
for (Carriage carriage : carriages) {
DimensionalCarriageEntity dce = carriage.getDimensionalIfPresent(level.dimension());
if (dce == null || dce.positionAnchor == null)
continue;
distance = Math.min(distance, (float) dce.positionAnchor.distanceToSqr(location));
}
return distance;
}
}