From 9e0f35e4d77a043c93332a6524d71217e0ecdfdf Mon Sep 17 00:00:00 2001 From: simibubi <31564874+simibubi@users.noreply.github.com> Date: Wed, 9 Mar 2022 21:33:28 +0100 Subject: [PATCH] Reincartnated - Trains now get saved to disk - Trains now sync raw position data to clients while in the derailed state - Trains can no longer be relocated into another train - Train relocation now shows indicators - Fixed single-bogey carriages not sending enough position context when derailed --- src/generated/resources/.cache/cache | 30 ++-- .../resources/assets/create/lang/en_us.json | 2 +- .../assets/create/lang/unfinished/de_de.json | 2 +- .../assets/create/lang/unfinished/es_cl.json | 2 +- .../assets/create/lang/unfinished/es_es.json | 2 +- .../assets/create/lang/unfinished/fr_fr.json | 2 +- .../assets/create/lang/unfinished/it_it.json | 2 +- .../assets/create/lang/unfinished/ja_jp.json | 2 +- .../assets/create/lang/unfinished/ko_kr.json | 2 +- .../assets/create/lang/unfinished/nl_nl.json | 2 +- .../assets/create/lang/unfinished/pl_pl.json | 2 +- .../assets/create/lang/unfinished/pt_br.json | 2 +- .../assets/create/lang/unfinished/pt_pt.json | 2 +- .../assets/create/lang/unfinished/ru_ru.json | 2 +- .../assets/create/lang/unfinished/zh_cn.json | 2 +- .../assets/create/lang/unfinished/zh_tw.json | 2 +- .../controls/ControlsInputPacket.java | 4 +- .../trains/GlobalRailwayManager.java | 3 +- .../logistics/trains/RailwaySavedData.java | 21 ++- .../content/logistics/trains/TrackGraph.java | 5 + .../logistics/trains/entity/Carriage.java | 118 ++++++++++---- .../trains/entity/CarriageBogey.java | 72 ++++++--- .../entity/CarriageContraptionEntity.java | 13 +- .../CarriageContraptionEntityRenderer.java | 2 +- .../entity/CarriageCouplingRenderer.java | 4 +- .../trains/entity/CarriageSyncData.java | 65 +++++++- .../logistics/trains/entity/Navigation.java | 132 ++++++++++------ .../logistics/trains/entity/Train.java | 146 +++++++++++++++--- .../trains/entity/TrainMigration.java | 27 ++++ .../logistics/trains/entity/TrainPacket.java | 9 +- .../trains/entity/TrainRelocator.java | 55 ++++++- .../trains/entity/TravellingPoint.java | 39 ++++- .../management/schedule/ScheduleRuntime.java | 9 ++ .../assets/create/lang/default/interface.json | 2 +- 34 files changed, 611 insertions(+), 175 deletions(-) diff --git a/src/generated/resources/.cache/cache b/src/generated/resources/.cache/cache index 1df7bdd36..8c91c9cee 100644 --- a/src/generated/resources/.cache/cache +++ b/src/generated/resources/.cache/cache @@ -541,21 +541,21 @@ bf2b0310500213ff853c748c236eb5d01f61658e assets/create/blockstates/yellow_toolbo 7f39521b211441f5c3e06d60c5978cebe16cacfb assets/create/blockstates/zinc_block.json b7181bcd8182b2f17088e5aa881f374c9c65470c assets/create/blockstates/zinc_ore.json 255e47ab7894ba35851a2f34c82be3dc9c9e8784 assets/create/lang/en_ud.json -3610238d5807c0d4f04a7632a871487ba8cc2c1f assets/create/lang/en_us.json -6a67c98238de2e57b5f2b36b3e988e70616c2ab0 assets/create/lang/unfinished/de_de.json -f9cb3ca070daff2503223de083c95e7e176b10fd assets/create/lang/unfinished/es_cl.json -35bb65eb644b10adbf55bc03913db056bc88f32c assets/create/lang/unfinished/es_es.json -9e7c2ea229f44ff57fc7ba2bc6f94638d2a68af0 assets/create/lang/unfinished/fr_fr.json -0e4bc37cd97dd28107593eb11d936101a2a3ebb6 assets/create/lang/unfinished/it_it.json -11034cb1b7e049c403f4a1e4774c2e1611af5b10 assets/create/lang/unfinished/ja_jp.json -626ef47b3ee504f922b6fa7cde416ae284f22236 assets/create/lang/unfinished/ko_kr.json -1025d8531ada9f4a521aba15a153de59a3bfd596 assets/create/lang/unfinished/nl_nl.json -4113a74ef44f7c30285b578eea239fee70646702 assets/create/lang/unfinished/pl_pl.json -ecb8680bae4a7ce8c6e7f1ae45f74992a6f152a6 assets/create/lang/unfinished/pt_br.json -db520fa1663bbecea9e39a1acfa6514740e74445 assets/create/lang/unfinished/pt_pt.json -718e9841f70866b4fbbe397f8fdd1600e2c044a8 assets/create/lang/unfinished/ru_ru.json -c8094e8ebf65b51fe1156955e2968709543c0592 assets/create/lang/unfinished/zh_cn.json -949a8d2f792ff994dbe2339dda5d08543d62fd55 assets/create/lang/unfinished/zh_tw.json +b908dd7c47b286d05a57b8a58c60051363ffff86 assets/create/lang/en_us.json +29ad91d27ee183b9d4de8d152d0fb8ed2a388a0c assets/create/lang/unfinished/de_de.json +8d33ba82d3cca307c5ae0dda41a49ec16f1c7b74 assets/create/lang/unfinished/es_cl.json +ae674553b564a9e09a7d10c9c2d8c1be1307f40a assets/create/lang/unfinished/es_es.json +318149855f383fb90439f2f0818e8e83fee3924e assets/create/lang/unfinished/fr_fr.json +e7321dbe8f98c96e778b5c46823c98500b3001bc assets/create/lang/unfinished/it_it.json +5a2da425fd0dd276c3d7cf66f6bb43f229574306 assets/create/lang/unfinished/ja_jp.json +98fc1956e8b25aec27e889ada3a160800b958dfd assets/create/lang/unfinished/ko_kr.json +67cefb6690f5cccce93d776a760ebdadc7f2631e assets/create/lang/unfinished/nl_nl.json +9a7f4d369d2a54aaf5c422fae740244025a13af4 assets/create/lang/unfinished/pl_pl.json +3656897ffc5c89bf16a32ad11efe33cd5b4bfb23 assets/create/lang/unfinished/pt_br.json +c84995ec524b6469fc42f39574be21b9dbee24e3 assets/create/lang/unfinished/pt_pt.json +bcf41d9fd8454296dc56326b6e26ee41e2ad52e5 assets/create/lang/unfinished/ru_ru.json +d3dfdab017748fa82f9e952a35bd6e82fe063771 assets/create/lang/unfinished/zh_cn.json +dda3aee2ece5503edd53dbd4aa9db7363e4beafc assets/create/lang/unfinished/zh_tw.json 487a511a01b2a4531fb672f917922312db78f958 assets/create/models/block/acacia_window.json b48060cba1a382f373a05bf0039054053eccf076 assets/create/models/block/acacia_window_pane_noside.json 3066db1bf03cffa1a9c7fbacf47ae586632f4eb3 assets/create/models/block/acacia_window_pane_noside_alt.json diff --git a/src/generated/resources/assets/create/lang/en_us.json b/src/generated/resources/assets/create/lang/en_us.json index a4644f44d..bec91fbc7 100644 --- a/src/generated/resources/assets/create/lang/en_us.json +++ b/src/generated/resources/assets/create/lang/en_us.json @@ -1421,7 +1421,7 @@ "create.train.relocate.abort": "Relocation aborted", "create.train.relocate.success": "Relocation successful", "create.train.relocate.valid": "Can relocate to here, Click to Confirm", - "create.train.relocate.invalid": "Cannot relocate Train to this Track", + "create.train.relocate.invalid": "Cannot relocate Train to here", "create.train.relocate.too_far": "Cannot relocate Train this far away", "create.contraption.controls.start_controlling": "Now controlling: %1$s", diff --git a/src/generated/resources/assets/create/lang/unfinished/de_de.json b/src/generated/resources/assets/create/lang/unfinished/de_de.json index bac784a48..267cc1628 100644 --- a/src/generated/resources/assets/create/lang/unfinished/de_de.json +++ b/src/generated/resources/assets/create/lang/unfinished/de_de.json @@ -1422,7 +1422,7 @@ "create.train.relocate.abort": "UNLOCALIZED: Relocation aborted", "create.train.relocate.success": "UNLOCALIZED: Relocation successful", "create.train.relocate.valid": "UNLOCALIZED: Can relocate to here, Click to Confirm", - "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to this Track", + "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here", "create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away", "create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s", diff --git a/src/generated/resources/assets/create/lang/unfinished/es_cl.json b/src/generated/resources/assets/create/lang/unfinished/es_cl.json index 5083ad46d..a2cbb0a0b 100644 --- a/src/generated/resources/assets/create/lang/unfinished/es_cl.json +++ b/src/generated/resources/assets/create/lang/unfinished/es_cl.json @@ -1422,7 +1422,7 @@ "create.train.relocate.abort": "UNLOCALIZED: Relocation aborted", "create.train.relocate.success": "UNLOCALIZED: Relocation successful", "create.train.relocate.valid": "UNLOCALIZED: Can relocate to here, Click to Confirm", - "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to this Track", + "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here", "create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away", "create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s", diff --git a/src/generated/resources/assets/create/lang/unfinished/es_es.json b/src/generated/resources/assets/create/lang/unfinished/es_es.json index feca7b44c..f1a3c04fb 100644 --- a/src/generated/resources/assets/create/lang/unfinished/es_es.json +++ b/src/generated/resources/assets/create/lang/unfinished/es_es.json @@ -1422,7 +1422,7 @@ "create.train.relocate.abort": "UNLOCALIZED: Relocation aborted", "create.train.relocate.success": "UNLOCALIZED: Relocation successful", "create.train.relocate.valid": "UNLOCALIZED: Can relocate to here, Click to Confirm", - "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to this Track", + "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here", "create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away", "create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s", diff --git a/src/generated/resources/assets/create/lang/unfinished/fr_fr.json b/src/generated/resources/assets/create/lang/unfinished/fr_fr.json index 67f88d1fc..fe6040df8 100644 --- a/src/generated/resources/assets/create/lang/unfinished/fr_fr.json +++ b/src/generated/resources/assets/create/lang/unfinished/fr_fr.json @@ -1422,7 +1422,7 @@ "create.train.relocate.abort": "UNLOCALIZED: Relocation aborted", "create.train.relocate.success": "UNLOCALIZED: Relocation successful", "create.train.relocate.valid": "UNLOCALIZED: Can relocate to here, Click to Confirm", - "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to this Track", + "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here", "create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away", "create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s", diff --git a/src/generated/resources/assets/create/lang/unfinished/it_it.json b/src/generated/resources/assets/create/lang/unfinished/it_it.json index 6f4babfd3..4b4e8ee5a 100644 --- a/src/generated/resources/assets/create/lang/unfinished/it_it.json +++ b/src/generated/resources/assets/create/lang/unfinished/it_it.json @@ -1422,7 +1422,7 @@ "create.train.relocate.abort": "UNLOCALIZED: Relocation aborted", "create.train.relocate.success": "UNLOCALIZED: Relocation successful", "create.train.relocate.valid": "UNLOCALIZED: Can relocate to here, Click to Confirm", - "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to this Track", + "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here", "create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away", "create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s", diff --git a/src/generated/resources/assets/create/lang/unfinished/ja_jp.json b/src/generated/resources/assets/create/lang/unfinished/ja_jp.json index 8bdd1dce1..0d4062584 100644 --- a/src/generated/resources/assets/create/lang/unfinished/ja_jp.json +++ b/src/generated/resources/assets/create/lang/unfinished/ja_jp.json @@ -1422,7 +1422,7 @@ "create.train.relocate.abort": "UNLOCALIZED: Relocation aborted", "create.train.relocate.success": "UNLOCALIZED: Relocation successful", "create.train.relocate.valid": "UNLOCALIZED: Can relocate to here, Click to Confirm", - "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to this Track", + "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here", "create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away", "create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s", diff --git a/src/generated/resources/assets/create/lang/unfinished/ko_kr.json b/src/generated/resources/assets/create/lang/unfinished/ko_kr.json index 577e97648..a98bd1e5a 100644 --- a/src/generated/resources/assets/create/lang/unfinished/ko_kr.json +++ b/src/generated/resources/assets/create/lang/unfinished/ko_kr.json @@ -1422,7 +1422,7 @@ "create.train.relocate.abort": "UNLOCALIZED: Relocation aborted", "create.train.relocate.success": "UNLOCALIZED: Relocation successful", "create.train.relocate.valid": "UNLOCALIZED: Can relocate to here, Click to Confirm", - "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to this Track", + "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here", "create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away", "create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s", diff --git a/src/generated/resources/assets/create/lang/unfinished/nl_nl.json b/src/generated/resources/assets/create/lang/unfinished/nl_nl.json index 52f139bb3..fd6b4c140 100644 --- a/src/generated/resources/assets/create/lang/unfinished/nl_nl.json +++ b/src/generated/resources/assets/create/lang/unfinished/nl_nl.json @@ -1422,7 +1422,7 @@ "create.train.relocate.abort": "UNLOCALIZED: Relocation aborted", "create.train.relocate.success": "UNLOCALIZED: Relocation successful", "create.train.relocate.valid": "UNLOCALIZED: Can relocate to here, Click to Confirm", - "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to this Track", + "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here", "create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away", "create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s", diff --git a/src/generated/resources/assets/create/lang/unfinished/pl_pl.json b/src/generated/resources/assets/create/lang/unfinished/pl_pl.json index 0da1ea64a..3dd84b415 100644 --- a/src/generated/resources/assets/create/lang/unfinished/pl_pl.json +++ b/src/generated/resources/assets/create/lang/unfinished/pl_pl.json @@ -1422,7 +1422,7 @@ "create.train.relocate.abort": "UNLOCALIZED: Relocation aborted", "create.train.relocate.success": "UNLOCALIZED: Relocation successful", "create.train.relocate.valid": "UNLOCALIZED: Can relocate to here, Click to Confirm", - "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to this Track", + "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here", "create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away", "create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s", diff --git a/src/generated/resources/assets/create/lang/unfinished/pt_br.json b/src/generated/resources/assets/create/lang/unfinished/pt_br.json index 155551c1d..17086c8c8 100644 --- a/src/generated/resources/assets/create/lang/unfinished/pt_br.json +++ b/src/generated/resources/assets/create/lang/unfinished/pt_br.json @@ -1422,7 +1422,7 @@ "create.train.relocate.abort": "UNLOCALIZED: Relocation aborted", "create.train.relocate.success": "UNLOCALIZED: Relocation successful", "create.train.relocate.valid": "UNLOCALIZED: Can relocate to here, Click to Confirm", - "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to this Track", + "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here", "create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away", "create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s", diff --git a/src/generated/resources/assets/create/lang/unfinished/pt_pt.json b/src/generated/resources/assets/create/lang/unfinished/pt_pt.json index d0e4c8f6a..19a116dbc 100644 --- a/src/generated/resources/assets/create/lang/unfinished/pt_pt.json +++ b/src/generated/resources/assets/create/lang/unfinished/pt_pt.json @@ -1422,7 +1422,7 @@ "create.train.relocate.abort": "UNLOCALIZED: Relocation aborted", "create.train.relocate.success": "UNLOCALIZED: Relocation successful", "create.train.relocate.valid": "UNLOCALIZED: Can relocate to here, Click to Confirm", - "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to this Track", + "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here", "create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away", "create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s", diff --git a/src/generated/resources/assets/create/lang/unfinished/ru_ru.json b/src/generated/resources/assets/create/lang/unfinished/ru_ru.json index 2bf7fbe08..5f282f4ca 100644 --- a/src/generated/resources/assets/create/lang/unfinished/ru_ru.json +++ b/src/generated/resources/assets/create/lang/unfinished/ru_ru.json @@ -1422,7 +1422,7 @@ "create.train.relocate.abort": "UNLOCALIZED: Relocation aborted", "create.train.relocate.success": "UNLOCALIZED: Relocation successful", "create.train.relocate.valid": "UNLOCALIZED: Can relocate to here, Click to Confirm", - "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to this Track", + "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here", "create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away", "create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s", diff --git a/src/generated/resources/assets/create/lang/unfinished/zh_cn.json b/src/generated/resources/assets/create/lang/unfinished/zh_cn.json index 01b31a53f..e7a92edf4 100644 --- a/src/generated/resources/assets/create/lang/unfinished/zh_cn.json +++ b/src/generated/resources/assets/create/lang/unfinished/zh_cn.json @@ -1422,7 +1422,7 @@ "create.train.relocate.abort": "UNLOCALIZED: Relocation aborted", "create.train.relocate.success": "UNLOCALIZED: Relocation successful", "create.train.relocate.valid": "UNLOCALIZED: Can relocate to here, Click to Confirm", - "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to this Track", + "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here", "create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away", "create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s", diff --git a/src/generated/resources/assets/create/lang/unfinished/zh_tw.json b/src/generated/resources/assets/create/lang/unfinished/zh_tw.json index 451be04d9..d93ec320d 100644 --- a/src/generated/resources/assets/create/lang/unfinished/zh_tw.json +++ b/src/generated/resources/assets/create/lang/unfinished/zh_tw.json @@ -1422,7 +1422,7 @@ "create.train.relocate.abort": "UNLOCALIZED: Relocation aborted", "create.train.relocate.success": "UNLOCALIZED: Relocation successful", "create.train.relocate.valid": "UNLOCALIZED: Can relocate to here, Click to Confirm", - "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to this Track", + "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here", "create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away", "create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s", diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/interaction/controls/ControlsInputPacket.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/interaction/controls/ControlsInputPacket.java index f36dd55a6..6c4c95f4b 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/interaction/controls/ControlsInputPacket.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/interaction/controls/ControlsInputPacket.java @@ -64,8 +64,8 @@ public class ControlsInputPacket extends SimplePacketBase { Entity entity = world.getEntity(contraptionEntityId); if (!(entity instanceof AbstractContraptionEntity ace)) return; - if (ace.toGlobalVector(Vec3.atCenterOf(controlsPos), 0) - .distanceTo(player.position()) > 10) + if (!ace.toGlobalVector(Vec3.atCenterOf(controlsPos), 0) + .closerThan(player.position(), 16)) return; ControlsServerHandler.receivePressed(world, ace, controlsPos, uniqueID, activatedButtons, press); 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 66facb602..fadb3d402 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 @@ -53,7 +53,7 @@ public class GlobalRailwayManager { sync.sendEdgeGroups(signalEdgeGroups.keySet(), serverPlayer); for (Train train : trains.values()) AllPackets.channel.send(PacketDistributor.PLAYER.with(() -> serverPlayer), - new TrainPacket(train, false)); + new TrainPacket(train, true)); } } @@ -70,6 +70,7 @@ public class GlobalRailwayManager { if (savedData != null) return; savedData = RailwaySavedData.load(server); + trains = savedData.getTrains(); trackNetworks = savedData.getTrackNetworks(); signalEdgeGroups = savedData.getSignalBlocks(); } 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 cd6594008..2617030a5 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 @@ -5,6 +5,7 @@ import java.util.Map; import java.util.UUID; import com.simibubi.create.Create; +import com.simibubi.create.content.logistics.trains.entity.Train; import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalEdgeGroup; import com.simibubi.create.foundation.utility.NBTHelper; @@ -17,23 +18,33 @@ public class RailwaySavedData extends SavedData { private Map trackNetworks = new HashMap<>(); private Map signalEdgeGroups = new HashMap<>(); + private Map trains = new HashMap<>(); @Override public CompoundTag save(CompoundTag nbt) { - nbt.put("RailGraphs", NBTHelper.writeCompoundList(Create.RAILWAYS.trackNetworks.values(), TrackGraph::write)); + 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(Create.RAILWAYS.signalEdgeGroups.values(), SignalEdgeGroup::write)); + NBTHelper.writeCompoundList(railways.signalEdgeGroups.values(), SignalEdgeGroup::write)); + nbt.put("Trains", NBTHelper.writeCompoundList(railways.trains.values(), Train::write)); return nbt; } private static RailwaySavedData load(CompoundTag nbt) { - RailwaySavedData sd = new RailwaySavedData();//TODO load trains before everything else + RailwaySavedData sd = new RailwaySavedData(); sd.trackNetworks = new HashMap<>(); sd.signalEdgeGroups = new HashMap<>(); + sd.trains = new HashMap<>(); + Create.LOGGER.info("Loading Railway Information..."); NBTHelper.iterateCompoundList(nbt.getList("RailGraphs", Tag.TAG_COMPOUND), c -> { TrackGraph graph = TrackGraph.read(c); 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); @@ -45,6 +56,10 @@ public class RailwaySavedData extends SavedData { return trackNetworks; } + public Map getTrains() { + return trains; + } + public Map getSignalBlocks() { return signalEdgeGroups; } 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 384c2c15b..25f04eb2d 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 @@ -32,6 +32,7 @@ import com.simibubi.create.content.logistics.trains.management.edgePoint.signal. import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.TrackEdgePoint; import com.simibubi.create.content.logistics.trains.management.edgePoint.station.GlobalStation; import com.simibubi.create.foundation.utility.Color; +import com.simibubi.create.foundation.utility.Couple; import com.simibubi.create.foundation.utility.NBTHelper; import com.simibubi.create.foundation.utility.Pair; import com.simibubi.create.foundation.utility.VecHelper; @@ -307,6 +308,10 @@ public class TrackGraph { return connectionsByNode.getOrDefault(node, new HashMap<>()); } + public TrackEdge getConnection(Couple nodes) { + return getConnectionsFrom(nodes.getFirst()).get(nodes.getSecond()); + } + public void connectNodes(TrackNodeLocation location, TrackNodeLocation location2, TrackEdge edge) { TrackNode node1 = nodes.get(location); TrackNode node2 = nodes.get(location2); 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 af58017e7..beacfc5de 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 @@ -15,9 +15,11 @@ import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.ISign 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.VecHelper; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.Tag; import net.minecraft.util.Mth; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity.RemovalReason; @@ -37,7 +39,10 @@ public class Carriage { public Couple presentConductors; public int bogeySpacing; - Couple bogeys; + public Couple bogeys; + + public Vec3 positionAnchor; + public Couple rotationAnchors; CompoundTag serialisedEntity; WeakReference entity; @@ -54,6 +59,9 @@ public class Carriage { this.id = netIdGenerator.incrementAndGet(); this.serialisedEntity = new CompoundTag(); this.pointsInitialised = false; + this.rotationAnchors = Couple.create(null, null); + + updateContraptionAnchors(); bogey1.carriage = this; if (bogey2 != null) @@ -69,9 +77,7 @@ public class Carriage { entity.setCarriage(this); contraption.startMoving(level); contraption.onEntityInitialize(level, entity); - for (CarriageBogey carriageBogey : bogeys) - if (carriageBogey != null) - carriageBogey.updateAnchorPosition(); + updateContraptionAnchors(); alignEntity(entity); List players = new ArrayList<>(); @@ -89,10 +95,8 @@ public class Carriage { public double travel(Level level, TrackGraph graph, double distance, Function forwardControl, Function backwardControl, int type) { - Vec3 leadingAnchor = leadingBogey().anchorPosition; - Vec3 trailingAnchor = trailingBogey().anchorPosition; boolean onTwoBogeys = isOnTwoBogeys(); - double stress = onTwoBogeys ? bogeySpacing - leadingAnchor.distanceTo(trailingAnchor) : 0; + double stress = onTwoBogeys ? bogeySpacing - leadingAnchor().distanceTo(trailingAnchor()) : 0; blocked = false; MutableDouble distanceMoved = new MutableDouble(distance); @@ -139,13 +143,11 @@ public class Carriage { distanceMoved.setValue(moved); } - - bogey.updateAnchorPosition(); } - double actualMovement = distanceMoved.getValue(); - manageEntity(level, actualMovement); - return actualMovement; + updateContraptionAnchors(); + manageEntity(level); + return distanceMoved.getValue(); } public void updateConductors() { @@ -162,7 +164,7 @@ public class Carriage { if (!(entity instanceof CarriageContraptionEntity cce)) return; - Vec3 pos = leadingBogey().anchorPosition; + Vec3 pos = positionAnchor; cce.setPos(pos); cce.setCarriage(this); cce.setGraph(train.graph == null ? null : train.graph.id); @@ -171,10 +173,10 @@ public class Carriage { } public ChunkPos getChunk() { - return new ChunkPos(new BlockPos(leadingBogey().anchorPosition)); + return new ChunkPos(new BlockPos(positionAnchor)); } - protected void manageEntity(Level level, double actualMovement) { + public void manageEntity(Level level) { CarriageContraptionEntity entity = this.entity.get(); if (entity == null) { if (CarriageEntityHandler.isActiveChunk(level, getChunk())) @@ -182,12 +184,7 @@ public class Carriage { } else { CarriageEntityHandler.validateCarriageEntity(entity); if (!entity.isAlive() || entity.leftTickingChunks) { - for (Entity passenger : entity.getPassengers()) - if (!(passenger instanceof Player)) - passenger.remove(RemovalReason.UNLOADED_WITH_PLAYER); - serialisedEntity = entity.serializeNBT(); - entity.discard(); - this.entity.clear(); + removeAndSaveEntity(entity); return; } } @@ -200,19 +197,34 @@ public class Carriage { entity.syncCarriage(); } + private void removeAndSaveEntity(CarriageContraptionEntity entity) { + for (Entity passenger : entity.getPassengers()) + if (!(passenger instanceof Player)) + passenger.remove(RemovalReason.UNLOADED_WITH_PLAYER); + serialisedEntity = entity.serializeNBT(); + 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()); + } + public void alignEntity(CarriageContraptionEntity entity) { - Vec3 positionVec = isOnTwoBogeys() ? leadingBogey().anchorPosition - : leadingBogey().leading() - .getPosition(); - Vec3 coupledVec = isOnTwoBogeys() ? trailingBogey().anchorPosition - : leadingBogey().trailing() - .getPosition(); + 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(leadingBogey().anchorPosition); + entity.setPos(positionAnchor); entity.prevYaw = entity.yaw; entity.prevPitch = entity.pitch; entity.yaw = (float) (Mth.atan2(diffZ, diffX) * 180 / Math.PI) + 180; @@ -239,4 +251,54 @@ public class Carriage { return bogeys.getSecond() != null; } + public Vec3 leadingAnchor() { + return isOnTwoBogeys() ? rotationAnchors.getFirst() : positionAnchor; + } + + public Vec3 trailingAnchor() { + return isOnTwoBogeys() ? rotationAnchors.getSecond() : positionAnchor; + } + + public CompoundTag write() { + CompoundTag tag = new CompoundTag(); + tag.put("FirstBogey", bogeys.getFirst() + .write()); + if (isOnTwoBogeys()) + tag.put("SecondBogey", bogeys.getSecond() + .write()); + tag.putInt("Spacing", bogeySpacing); + tag.putBoolean("FrontConductor", presentConductors.getFirst()); + tag.putBoolean("BackConductor", presentConductors.getSecond()); + + CarriageContraptionEntity entity = this.entity.get(); + if (entity != null) + serialisedEntity = entity.serializeNBT(); + + tag.put("Entity", serialisedEntity.copy()); + tag.put("PositionAnchor", VecHelper.writeNBT(positionAnchor)); + tag.put("RotationAnchors", rotationAnchors.serializeEach(VecHelper::writeNBTCompound)); + + return tag; + } + + public static Carriage read(CompoundTag tag, TrackGraph graph) { + CarriageBogey bogey1 = CarriageBogey.read(tag.getCompound("FirstBogey"), graph); + CarriageBogey bogey2 = + tag.contains("SecondBogey") ? CarriageBogey.read(tag.getCompound("SecondBogey"), graph) : null; + + Carriage carriage = new Carriage(bogey1, bogey2, tag.getInt("Spacing")); + + carriage.presentConductors = Couple.create(tag.getBoolean("FrontConductor"), tag.getBoolean("BackConductor")); + 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); + } + + return carriage; + } + } 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 a0b61f8e8..b87d53720 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 @@ -1,15 +1,23 @@ package com.simibubi.create.content.logistics.trains.entity; +import javax.annotation.Nullable; + import com.simibubi.create.Create; import com.simibubi.create.content.logistics.trains.IBogeyBlock; +import com.simibubi.create.content.logistics.trains.TrackGraph; import com.simibubi.create.foundation.utility.AngleHelper; import com.simibubi.create.foundation.utility.Couple; import com.simibubi.create.foundation.utility.VecHelper; 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.ResourceLocation; import net.minecraft.util.Mth; +import net.minecraft.world.level.block.Block; import net.minecraft.world.phys.Vec3; +import net.minecraftforge.registries.ForgeRegistries; public class CarriageBogey { @@ -17,14 +25,12 @@ public class CarriageBogey { IBogeyBlock type; Couple points; - Vec3 anchorPosition; LerpedFloat wheelAngle; LerpedFloat yaw; LerpedFloat pitch; - public Vec3 leadingCouplingAnchor; - public Vec3 trailingCouplingAnchor; + public Couple couplingAnchors; int derailAngle; @@ -34,22 +40,27 @@ public class CarriageBogey { wheelAngle = LerpedFloat.angular(); yaw = LerpedFloat.angular(); pitch = LerpedFloat.angular(); - updateAnchorPosition(); - derailAngle = Create.RANDOM.nextInt(90) - 45; + derailAngle = Create.RANDOM.nextInt(60) - 30; + couplingAnchors = Couple.create(null, null); } - public void updateAngles(double distanceMoved) { + public void updateAngles(CarriageContraptionEntity entity, double distanceMoved) { double angleDiff = 360 * distanceMoved / (Math.PI * 2 * type.getWheelRadius()); - Vec3 positionVec = leading().getPosition(); - Vec3 coupledVec = trailing().getPosition(); - double diffX = positionVec.x - coupledVec.x; - double diffY = positionVec.y - coupledVec.y; - double diffZ = positionVec.z - coupledVec.z; - float yRot = AngleHelper.deg(Mth.atan2(diffZ, diffX)) + 90; - float xRot = AngleHelper.deg(Math.atan2(diffY, Math.sqrt(diffX * diffX + diffZ * diffZ))); - if (carriage.train.derailed) - yRot += derailAngle; + float xRot = 0; + float yRot = 0; + + if (leading().edge == null || carriage.train.derailed) { + yRot = -90 + entity.yaw - derailAngle; + } else { + Vec3 positionVec = leading().getPosition(); + Vec3 coupledVec = trailing().getPosition(); + double diffX = positionVec.x - coupledVec.x; + double diffY = positionVec.y - coupledVec.y; + double diffZ = positionVec.z - coupledVec.z; + yRot = AngleHelper.deg(Mth.atan2(diffZ, diffX)) + 90; + xRot = AngleHelper.deg(Math.atan2(diffY, Math.sqrt(diffX * diffX + diffZ * diffZ))); + } wheelAngle.setValue((wheelAngle.getValue() - angleDiff) % 360); pitch.setValue(xRot); @@ -69,10 +80,11 @@ public class CarriageBogey { .distanceTo(trailing().getPosition()); } - public void updateAnchorPosition() { - if (points.getFirst().node1 == null) - return; - anchorPosition = points.getFirst() + @Nullable + public Vec3 getAnchorPosition() { + if (leading().edge == null) + return null; + return points.getFirst() .getPosition() .add(points.getSecond() .getPosition()) @@ -94,10 +106,24 @@ public class CarriageBogey { thisOffset = VecHelper.rotate(thisOffset, -entityXRot, Axis.X); thisOffset = VecHelper.rotate(thisOffset, entityYRot + 90, Axis.Y); - if (leading) - leadingCouplingAnchor = entityPos.add(thisOffset); - else - trailingCouplingAnchor = entityPos.add(thisOffset); + couplingAnchors.set(leading, entityPos.add(thisOffset)); + } + + public CompoundTag write() { + CompoundTag tag = new CompoundTag(); + tag.putString("Type", ((Block) type).getRegistryName() + .toString()); + tag.put("Points", points.serializeEach(TravellingPoint::write)); + return tag; + } + + public static CarriageBogey read(CompoundTag tag, TrackGraph graph) { + 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)); + CarriageBogey carriageBogey = new CarriageBogey(type, points.getFirst(), points.getSecond()); + return carriageBogey; } } \ No newline at end of file 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 c94b2a8d7..0e39fcd91 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 @@ -163,11 +163,8 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity { carriageData.approach(this, carriage, 1f / getType().updateInterval()); - carriage.bogeys.getFirst() - .updateAnchorPosition(); - if (carriage.isOnTwoBogeys()) - carriage.bogeys.getSecond() - .updateAnchorPosition(); + if (!carriage.train.derailed) + carriage.updateContraptionAnchors(); xo = getX(); yo = getY(); @@ -182,10 +179,10 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity { movingBackwards = signum < 0; carriage.bogeys.getFirst() - .updateAngles(distanceTo); + .updateAngles(this, distanceTo); if (carriage.isOnTwoBogeys()) carriage.bogeys.getSecond() - .updateAngles(distanceTo); + .updateAngles(this, distanceTo); if (carriage.train.derailed) spawnDerailParticles(carriage); @@ -278,7 +275,7 @@ public class CarriageContraptionEntity extends OrientedContraptionEntity { return true; if (player.isSpectator()) return false; - if (!toGlobalVector(VecHelper.getCenterOf(controlsLocalPos), 1).closerThan(player.position(), 10)) + if (!toGlobalVector(VecHelper.getCenterOf(controlsLocalPos), 1).closerThan(player.position(), 8)) return false; if (heldControls.contains(5)) return false; 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 4ccb4d538..08898efe7 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 @@ -22,7 +22,7 @@ public class CarriageContraptionEntityRenderer extends ContraptionEntityRenderer if (carriage != null) for (CarriageBogey bogey : carriage.bogeys) if (bogey != null) - bogey.leadingCouplingAnchor = bogey.trailingCouplingAnchor = null; + bogey.couplingAnchors.replace(v -> null); if (!super.shouldRender(entity, clippingHelper, cameraX, cameraY, cameraZ)) return false; return entity.validForRender; 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 50a6ddce0..e6ff622f6 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 @@ -49,8 +49,8 @@ public class CarriageCouplingRenderer { CarriageBogey bogey1 = carriage.trailingBogey(); CarriageBogey bogey2 = carriage2.leadingBogey(); - Vec3 anchor = bogey1.trailingCouplingAnchor; - Vec3 anchor2 = bogey2.leadingCouplingAnchor; + Vec3 anchor = bogey1.couplingAnchors.getSecond(); + Vec3 anchor2 = bogey2.couplingAnchors.getFirst(); if (anchor == null || anchor2 == null) continue; 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 e9cd00dc8..6775165c3 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 @@ -19,6 +19,7 @@ import com.simibubi.create.content.logistics.trains.TrackNode; import com.simibubi.create.foundation.utility.Couple; import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.Pair; +import com.simibubi.create.foundation.utility.VecHelper; import net.minecraft.network.FriendlyByteBuf; import net.minecraft.util.Mth; @@ -27,18 +28,22 @@ import net.minecraft.world.phys.Vec3; public class CarriageSyncData { public Vector, Float>> wheelLocations; + public Pair> fallbackLocations; public float distanceToDestination; public boolean leadingCarriage; // For Client interpolation + private Pair> fallbackPointSnapshot; private TravellingPoint[] pointsToApproach; private float[] pointDistanceSnapshot; private float destinationDistanceSnapshot; public CarriageSyncData() { wheelLocations = new Vector<>(4); + fallbackLocations = null; pointDistanceSnapshot = new float[4]; pointsToApproach = new TravellingPoint[4]; + fallbackPointSnapshot = null; destinationDistanceSnapshot = 0; leadingCarriage = false; for (int i = 0; i < 4; i++) { @@ -51,12 +56,26 @@ public class CarriageSyncData { CarriageSyncData data = new CarriageSyncData(); for (int i = 0; i < 4; i++) data.wheelLocations.set(i, wheelLocations.get(i)); + if (fallbackLocations != null) + data.fallbackLocations = fallbackLocations.copy(); data.distanceToDestination = distanceToDestination; data.leadingCarriage = leadingCarriage; return data; } public void write(FriendlyByteBuf buffer) { + buffer.writeBoolean(leadingCarriage); + buffer.writeBoolean(fallbackLocations != null); + + if (fallbackLocations != null) { + Vec3 contraptionAnchor = fallbackLocations.getFirst(); + Couple rotationAnchors = fallbackLocations.getSecond(); + VecHelper.write(contraptionAnchor, buffer); + VecHelper.write(rotationAnchors.getFirst(), buffer); + VecHelper.write(rotationAnchors.getSecond(), buffer); + return; + } + for (Pair, Float> pair : wheelLocations) { buffer.writeBoolean(pair == null); if (pair == null) @@ -66,24 +85,37 @@ public class CarriageSyncData { buffer.writeFloat(pair.getSecond()); } buffer.writeFloat(distanceToDestination); - buffer.writeBoolean(leadingCarriage); } public void read(FriendlyByteBuf buffer) { + leadingCarriage = buffer.readBoolean(); + boolean fallback = buffer.readBoolean(); + + if (fallback) { + fallbackLocations = + Pair.of(VecHelper.read(buffer), Couple.create(VecHelper.read(buffer), VecHelper.read(buffer))); + return; + } + + fallbackLocations = null; for (int i = 0; i < 4; i++) { if (buffer.readBoolean()) break; wheelLocations.set(i, Pair.of(Couple.create(buffer::readInt), buffer.readFloat())); } distanceToDestination = buffer.readFloat(); - leadingCarriage = buffer.readBoolean(); } public void update(CarriageContraptionEntity entity, Carriage carriage) { TrackGraph graph = carriage.train.graph; - if (graph == null) + if (graph == null) { + fallbackLocations = Pair.of(carriage.positionAnchor, carriage.rotationAnchors); + carriage.pointsInitialised = true; + setDirty(true); return; + } + fallbackLocations = null; leadingCarriage = entity.carriageIndex == (carriage.train.speed >= 0 ? 0 : carriage.train.carriages.size() - 1); for (boolean first : Iterate.trueAndFalse) { @@ -104,6 +136,13 @@ public class CarriageSyncData { } public void apply(CarriageContraptionEntity entity, Carriage carriage) { + fallbackPointSnapshot = null; + if (fallbackLocations != null) { + fallbackPointSnapshot = Pair.of(carriage.positionAnchor, carriage.rotationAnchors); + carriage.pointsInitialised = true; + return; + } + TrackGraph graph = carriage.train.graph; if (graph == null) return; @@ -163,11 +202,22 @@ public class CarriageSyncData { if (!leadingCarriage) return; - + destinationDistanceSnapshot = (float) (distanceToDestination - carriage.train.navigation.distanceToDestination); } public void approach(CarriageContraptionEntity entity, Carriage carriage, float partial) { + if (fallbackLocations != null && fallbackPointSnapshot != null) { + carriage.positionAnchor = approachVector(partial, carriage.positionAnchor, fallbackLocations.getFirst(), + fallbackPointSnapshot.getFirst()); + carriage.rotationAnchors.replaceWithContext((current, first) -> approachVector(partial, current, + fallbackLocations.getSecond() + .get(first), + fallbackPointSnapshot.getSecond() + .get(first))); + return; + } + TrackGraph graph = carriage.train.graph; if (graph == null) return; @@ -204,6 +254,13 @@ public class CarriageSyncData { } } + private Vec3 approachVector(float partial, Vec3 current, Vec3 target, Vec3 snapshot) { + if (current == null || snapshot == null) + return target; + return current.add(target.subtract(snapshot) + .scale(partial)); + } + public float getDistanceTo(TrackGraph graph, TravellingPoint current, TravellingPoint target, float maxDistance, boolean forward) { if (maxDistance == -1) 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 963890af9..630456188 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 @@ -16,6 +16,7 @@ import com.simibubi.create.Create; 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.entity.TravellingPoint.ITrackSelector; import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgeData; import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointType; @@ -26,18 +27,24 @@ import com.simibubi.create.content.logistics.trains.management.edgePoint.station import com.simibubi.create.foundation.config.AllConfigs; 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.Pair; +import net.minecraft.core.BlockPos; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.NbtUtils; +import net.minecraft.nbt.Tag; import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; public class Navigation { - Train train; + public Train train; + public GlobalStation destination; public double distanceToDestination; public boolean destinationBehindTrain; - List currentPath; + List> currentPath; private TravellingPoint signalScout; public Pair waitingForSignal; @@ -186,39 +193,29 @@ public class Navigation { public ITrackSelector control(TravellingPoint mp) { if (destination == null) return mp.steer(train.manualSteer, new Vec3(0, 1, 0)); - return (graph, pair) -> { - List> options = pair.getSecond(); - if (currentPath.isEmpty()) - return options.get(0); - TrackEdge target = currentPath.get(0); - for (Entry entry : options) { - if (entry.getValue() != target) - continue; - currentPath.remove(0); - return entry; - } - return options.get(0); - }; + return (graph, pair) -> navigateOptions(currentPath, graph, pair.getSecond()); } public ITrackSelector controlSignalScout() { if (destination == null) return signalScout.steer(train.manualSteer, new Vec3(0, 1, 0)); - List pathCopy = new ArrayList<>(currentPath); - return (graph, pair) -> { - List> options = pair.getSecond(); - if (pathCopy.isEmpty()) - return options.get(0); - TrackEdge target = pathCopy.get(0); - for (Entry entry : options) { - if (entry.getValue() != target) - continue; - pathCopy.remove(0); - return entry; - } - return options.get(0); - }; + List> pathCopy = new ArrayList<>(currentPath); + return (graph, pair) -> navigateOptions(pathCopy, graph, pair.getSecond()); + } + private Entry navigateOptions(List> path, TrackGraph graph, + List> options) { + if (path.isEmpty()) + return options.get(0); + Couple nodes = path.get(0); + TrackEdge targetEdge = graph.getConnection(nodes); + for (Entry entry : options) { + if (entry.getValue() != targetEdge) + continue; + path.remove(0); + return entry; + } + return options.get(0); } public void cancelNavigation() { @@ -232,7 +229,7 @@ public class Navigation { } public double startNavigation(GlobalStation destination, boolean simulate) { - Pair> pathTo = findPathTo(destination); + Pair>> pathTo = findPathTo(destination); boolean noneFound = pathTo.getFirst() == null; double distance = noneFound ? -1 : Math.abs(pathTo.getFirst()); @@ -280,21 +277,21 @@ public class Navigation { return distanceToDestination; } - private Pair> findPathTo(GlobalStation destination) { + private Pair>> findPathTo(GlobalStation destination) { TrackGraph graph = train.graph; - List path = new ArrayList<>(); + List> path = new ArrayList<>(); if (graph == null) return Pair.of(null, path); - MutableObject>> frontResult = new MutableObject<>(Pair.of(null, path)); - MutableObject>> backResult = new MutableObject<>(Pair.of(null, path)); + MutableObject>>> frontResult = new MutableObject<>(Pair.of(null, path)); + MutableObject>>> backResult = new MutableObject<>(Pair.of(null, path)); for (boolean forward : Iterate.trueAndFalse) { if (this.destination == destination && destinationBehindTrain == forward) continue; - List currentPath = new ArrayList<>(); + List> currentPath = new ArrayList<>(); TravellingPoint initialPoint = forward ? train.carriages.get(0) .getLeadingPoint() : train.carriages.get(train.carriages.size() - 1) @@ -313,13 +310,17 @@ public class Navigation { TrackNode node2 = currentEntry.getFirst() .getSecond(); - Pair backTrack = reachedVia.get(edge); - TrackEdge toReach = edge; - while (backTrack != null && toReach != initialEdge) { + Pair> backTrack = reachedVia.get(edge); + Couple toReach = Couple.create(node1, node2); + TrackEdge edgeReached = edge; + while (backTrack != null) { + if (edgeReached == initialEdge) + break; if (backTrack.getFirst()) currentPath.add(0, toReach); toReach = backTrack.getSecond(); - backTrack = reachedVia.get(backTrack.getSecond()); + edgeReached = graph.getConnection(toReach); + backTrack = reachedVia.get(edgeReached); } double position = edge.getLength(node1, node2) - destination.getLocationOn(node1, node2, edge); @@ -336,8 +337,8 @@ public class Navigation { break; } - Pair> front = frontResult.getValue(); - Pair> back = backResult.getValue(); + Pair>> front = frontResult.getValue(); + Pair>> back = backResult.getValue(); boolean frontEmpty = front.getFirst() == null; boolean backEmpty = back.getFirst() == null; @@ -398,7 +399,7 @@ public class Navigation { .getTrailingPoint(); Set visited = new HashSet<>(); - Map> reachedVia = new IdentityHashMap<>(); + Map>> reachedVia = new IdentityHashMap<>(); PriorityQueue, TrackEdge>>> frontier = new PriorityQueue<>((p1, p2) -> Double.compare(p1.getFirst(), p2.getFirst())); @@ -458,7 +459,7 @@ public class Navigation { for (Entry entry : validTargets) { TrackNode newNode = entry.getKey(); TrackEdge newEdge = entry.getValue(); - reachedVia.put(newEdge, Pair.of(validTargets.size() > 1, edge)); + reachedVia.put(newEdge, Pair.of(validTargets.size() > 1, Couple.create(node1, node2))); frontier.add(Pair.of(newEdge.getLength(node2, newNode) + distance, Pair.of(Couple.create(node2, newNode), newEdge))); } @@ -467,8 +468,51 @@ public class Navigation { @FunctionalInterface public interface StationTest { - boolean test(double distance, Map> reachedVia, + boolean test(double distance, Map>> reachedVia, Pair, TrackEdge> current, GlobalStation station); } + public CompoundTag write() { + CompoundTag tag = new CompoundTag(); + if (destination == null) + return tag; + tag.putUUID("Destination", destination.id); + tag.putDouble("DistanceToDestination", distanceToDestination); + tag.putBoolean("BehindTrain", destinationBehindTrain); + tag.put("Path", NBTHelper.writeCompoundList(currentPath, c -> { + CompoundTag nbt = new CompoundTag(); + nbt.put("Nodes", c.map(TrackNode::getLocation) + .map(BlockPos::new) + .serializeEach(NbtUtils::writeBlockPos)); + return nbt; + })); + if (waitingForSignal == null) + return tag; + tag.putUUID("BlockingSignal", waitingForSignal.getFirst()); + tag.putBoolean("BlockingSignalSide", waitingForSignal.getSecond()); + tag.putDouble("DistanceToSignal", distanceToSignal); + return tag; + } + + public void read(CompoundTag tag, TrackGraph graph) { + destination = + tag.contains("Destination") ? graph.getPoint(EdgePointType.STATION, tag.getUUID("Destination")) : null; + if (destination == null) + return; + distanceToDestination = tag.getDouble("DistanceToDestination"); + destinationBehindTrain = tag.getBoolean("BehindTrain"); + 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))) + .map(graph::locateNode))); + waitingForSignal = tag.contains("BlockingSignal") + ? Pair.of(tag.getUUID("BlockingSignal"), tag.getBoolean("BlockingSignalSide")) + : null; + if (waitingForSignal == null) + return; + distanceToSignal = tag.getDouble("DistanceToSignal"); + } + } 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 45abfc08f..c46de93c9 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 @@ -37,12 +37,17 @@ import com.simibubi.create.foundation.config.AllConfigs; import com.simibubi.create.foundation.networking.AllPackets; 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.ResourceLocation; import net.minecraft.util.Mth; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.level.Explosion.BlockInteraction; @@ -132,8 +137,13 @@ public class Train { } public void tick(Level level) { - if (graph == null) + Create.RAILWAYS.markTracksDirty(); + + if (graph == null) { + carriages.forEach(c -> c.manageEntity(level)); + updateConductors(); return; + } updateConductors(); runtime.tick(level); @@ -151,8 +161,8 @@ public class Train { Carriage carriage = carriages.get(i); if (previousCarriage != null) { int target = carriageSpacing.get(i - 1); - Vec3 leadingAnchor = carriage.leadingBogey().anchorPosition; - Vec3 trailingAnchor = previousCarriage.trailingBogey().anchorPosition; + Vec3 leadingAnchor = carriage.leadingAnchor(); + Vec3 trailingAnchor = previousCarriage.trailingAnchor(); double actual = leadingAnchor.distanceTo(trailingAnchor); stress[i - 1] = target - actual; } @@ -296,11 +306,31 @@ public class Train { private void collideWithOtherTrains(Level level, Carriage carriage) { if (derailed) return; - Collision: for (Train train : Create.RAILWAYS.trains.values()) { - if (train == this) + + Vec3 start = (speed < 0 ? carriage.getTrailingPoint() : carriage.getLeadingPoint()).getPosition(); + Vec3 end = (speed < 0 ? carriage.getLeadingPoint() : carriage.getTrailingPoint()).getPosition(); + + Pair collision = findCollidingTrain(level, start, end, this); + 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 findCollidingTrain(Level level, Vec3 start, Vec3 end, Train ignore) { + for (Train train : Create.RAILWAYS.sided(level).trains.values()) { + if (train == ignore) continue; - Vec3 start = (speed < 0 ? carriage.getTrailingPoint() : carriage.getLeadingPoint()).getPosition(); - Vec3 end = (speed < 0 ? carriage.getLeadingPoint() : carriage.getTrailingPoint()).getPosition(); + Vec3 diff = end.subtract(start); Vec3 lastPoint = null; @@ -309,10 +339,13 @@ public class Train { if (betweenBits && lastPoint == null) continue; - Vec3 start2 = otherCarriage.getLeadingPoint() - .getPosition(); - Vec3 end2 = otherCarriage.getTrailingPoint() - .getPosition(); + TravellingPoint otherLeading = otherCarriage.getLeadingPoint(); + TravellingPoint otherTrailing = otherCarriage.getTrailingPoint(); + if (otherLeading.edge == null || otherTrailing.edge == null) + continue; + + Vec3 start2 = otherLeading.getPosition(); + Vec3 end2 = otherTrailing.getPosition(); if (betweenBits) { end2 = start2; start2 = lastPoint; @@ -328,6 +361,7 @@ public class Train { 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) @@ -339,6 +373,7 @@ public class Train { intersect[0] = intersectSphere.distanceTo(start) - .125; intersect[1] = intersectSphere.distanceTo(start2) - .125; } + if (intersect[0] > diff.length()) continue; if (intersect[1] > diff2.length()) @@ -348,18 +383,11 @@ public class Train { if (intersect[1] < 0) continue; - double combinedSpeed = Math.abs(speed) + Math.abs(train.speed); - if (combinedSpeed > .2f) { - Vec3 v = start.add(normedDiff.scale(intersect[0])); - level.explode(null, v.x, v.y, v.z, (float) Math.min(3 * combinedSpeed, 5), - BlockInteraction.NONE); - } - crash(); - train.crash(); - break Collision; + return Pair.of(train, start.add(normedDiff.scale(intersect[0]))); } } } + return null; } public void crash() { @@ -552,9 +580,8 @@ public class Train { public void leaveStation() { GlobalStation currentStation = getCurrentStation(); - if (currentStation == null) - return; - currentStation.trainDeparted(this); + if (currentStation != null) + currentStation.trainDeparted(this); this.currentStation = null; } @@ -658,4 +685,77 @@ public class Train { } + public CompoundTag write() { + 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.putIntArray("CarriageSpacing", carriageSpacing); + tag.putBoolean("DoubleEnded", doubleEnded); + tag.putDouble("Speed", speed); + 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("StillAssembling", heldForAssembly); + tag.putBoolean("Derailed", derailed); + tag.putBoolean("UpdateSignals", updateSignalBlocks); + tag.put("SignalBlocks", NBTHelper.writeCompoundList(occupiedSignalBlocks, uid -> { + CompoundTag compoundTag = new CompoundTag(); + compoundTag.putUUID("Id", uid); + return compoundTag; + })); + tag.put("MigratingPoints", NBTHelper.writeCompoundList(migratingPoints, TrainMigration::write)); + + tag.put("Runtime", runtime.write()); + tag.put("Navigation", navigation.write()); + + return tag; + } + + public static Train read(CompoundTag tag, Map trackNetworks) { + 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))); + List 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.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.heldForAssembly = tag.getBoolean("StillAssembling"); + train.derailed = tag.getBoolean("Derailed"); + train.updateSignalBlocks = tag.getBoolean("UpdateSignals"); + + NBTHelper.iterateCompoundList(tag.getList("SignalBlocks", Tag.TAG_COMPOUND), + c -> train.occupiedSignalBlocks.add(c.getUUID("Id"))); + + NBTHelper.iterateCompoundList(tag.getList("MigratingPoints", Tag.TAG_COMPOUND), + c -> train.migratingPoints.add(TrainMigration.read(c))); + + train.runtime.read(tag.getCompound("Runtime")); + train.navigation.read(tag.getCompound("Navigation"), graph); + + if (train.getCurrentStation() != null) + train.getCurrentStation() + .reserveFor(train); + + return train; + } + } 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 deb9d591e..56a9bf31a 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 @@ -10,6 +10,10 @@ 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; @@ -20,6 +24,8 @@ public class TrainMigration { boolean curve; Vec3 fallback; + public TrainMigration() {} + public TrainMigration(TravellingPoint point) { double t = point.position / point.edge.getLength(point.node1, point.node2); fallback = point.edge.getPosition(point.node1, point.node2, t); @@ -94,4 +100,25 @@ public class TrainMigration { return null; } + public CompoundTag write() { + 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)); + return tag; + } + + public static TrainMigration read(CompoundTag tag) { + 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); + return trainMigration; + } + } \ No newline at end of file diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/entity/TrainPacket.java b/src/main/java/com/simibubi/create/content/logistics/trains/entity/TrainPacket.java index 1a66c0017..bc3d40c5c 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/entity/TrainPacket.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/entity/TrainPacket.java @@ -13,6 +13,7 @@ import com.simibubi.create.foundation.utility.Couple; import com.simibubi.create.foundation.utility.Iterate; import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.chat.Component; import net.minecraft.world.level.block.Block; import net.minecraftforge.network.NetworkEvent.Context; import net.minecraftforge.registries.ForgeRegistries; @@ -57,8 +58,11 @@ public class TrainPacket extends SimplePacketBase { carriageSpacing.add(buffer.readVarInt()); boolean doubleEnded = buffer.readBoolean(); - train = new Train(trainId, owner, null, carriages, carriageSpacing, doubleEnded); + + train.heldForAssembly = buffer.readBoolean(); + train.name = Component.Serializer.fromJson(buffer.readUtf()); + train.icon = TrainIconType.byId(buffer.readResourceLocation()); } @Override @@ -90,6 +94,9 @@ public class TrainPacket extends SimplePacketBase { train.carriageSpacing.forEach(buffer::writeVarInt); buffer.writeBoolean(train.doubleEnded); + buffer.writeBoolean(train.heldForAssembly); + buffer.writeUtf(Component.Serializer.toJson(train.name)); + buffer.writeResourceLocation(train.icon.id); } @Override 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 a6af96f21..b1775efaa 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 @@ -9,9 +9,11 @@ import java.util.function.Consumer; import javax.annotation.Nullable; import org.apache.commons.lang3.mutable.MutableBoolean; +import org.apache.commons.lang3.mutable.MutableInt; import com.simibubi.create.AllItems; import com.simibubi.create.Create; +import com.simibubi.create.CreateClient; import com.simibubi.create.content.contraptions.components.structureMovement.AbstractContraptionEntity; import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionHandlerClient; import com.simibubi.create.content.logistics.trains.GraphLocation; @@ -56,6 +58,7 @@ public class TrainRelocator { static BlockPos lastHoveredPos; static Boolean lastHoveredResult; + static List toVisualise; @OnlyIn(Dist.CLIENT) public static void onClicked(ClickInputEvent event) { @@ -98,10 +101,22 @@ public class TrainRelocator { return null; BlockPos blockPos = blockhit.getBlockPos(); + if (simulate && toVisualise != null) { + for (int i = 0; i < toVisualise.size() - 1; i++) { + Vec3 vec1 = toVisualise.get(i); + Vec3 vec2 = toVisualise.get(i + 1); + CreateClient.OUTLINER.showLine(Pair.of(relocating, i), vec1.add(0, -.925f, 0), vec2.add(0, -.925f, 0)) + .colored(lastHoveredResult || i != toVisualise.size() - 2 ? 0x95CD41 : 0xEA5C2B) + .disableNormals() + .lineWidth(i % 2 == 1 ? 1 / 6f : 1 / 4f); + } + } + if (simulate) { if (lastHoveredPos != null && lastHoveredPos.equals(blockPos)) return lastHoveredResult; lastHoveredPos = blockPos; + toVisualise = null; } BlockState blockState = mc.level.getBlockState(blockPos); @@ -113,6 +128,7 @@ public class TrainRelocator { if (!simulate && result) AllPackets.channel .sendToServer(new TrainRelocationPacket(relocatingTrain, blockPos, lookAngle, relocatingEntityId)); + return lastHoveredResult = result; } @@ -139,22 +155,43 @@ public class TrainRelocator { TravellingPoint probe = new TravellingPoint(node1, node2, edge, graphLocation.position); ISignalBoundaryListener ignoreSignals = probe.ignoreSignals(); List, Double>> recordedLocations = new ArrayList<>(); - Consumer recorder = - tp -> recordedLocations.add(Pair.of(Couple.create(tp.node1, tp.node2), tp.position)); + List recordedVecs = new ArrayList<>(); + Consumer recorder = tp -> { + recordedLocations.add(Pair.of(Couple.create(tp.node1, tp.node2), tp.position)); + recordedVecs.add(tp.getPosition()); + }; ITrackSelector steer = probe.steer(SteerDirection.NONE, track.getUpNormal(level, pos, blockState)); MutableBoolean blocked = new MutableBoolean(false); + MutableInt blockingIndex = new MutableInt(0); train.forEachTravellingPointBackwards((tp, d) -> { if (blocked.booleanValue()) return; probe.travel(graph, d, steer, ignoreSignals); + recorder.accept(probe); if (probe.blocked) { blocked.setTrue(); return; } - recorder.accept(probe); + blockingIndex.increment(); }); + if (level.isClientSide && simulate && !recordedVecs.isEmpty()) { + toVisualise = new ArrayList<>(); + toVisualise.add(recordedVecs.get(0)); + } + + for (int i = 0; i < recordedVecs.size() - 1; i++) { + 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; + if (level.isClientSide && simulate) + toVisualise.add(vec2); + if (collided || blocking) + return false; + } + if (blocked.booleanValue()) return false; @@ -167,6 +204,7 @@ public class TrainRelocator { train.occupiedSignalBlocks.clear(); train.graph = graph; train.speed = 0; + train.migratingPoints.clear(); if (train.navigation.destination != null) train.navigation.cancelNavigation(); @@ -182,11 +220,22 @@ public class TrainRelocator { .get(tp.node2); }); + for (Carriage carriage : train.carriages) + carriage.updateContraptionAnchors(); + train.status.successfulMigration(); train.collectInitiallyOccupiedSignalBlocks(); return true; } + @OnlyIn(Dist.CLIENT) + public static void visualise(Train train, int i, Vec3 v1, Vec3 v2, boolean valid) { + CreateClient.OUTLINER.showLine(Pair.of(train, i), v1.add(0, -.825f, 0), v2.add(0, -.825f, 0)) + .colored(valid ? 0x95CD41 : 0xEA5C2B) + .disableNormals() + .lineWidth(i % 2 == 1 ? 1 / 6f : 1 / 4f); + } + @OnlyIn(Dist.CLIENT) public static void clientTick() { Minecraft mc = Minecraft.getInstance(); 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 d9b20984c..d72de14dc 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 @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map.Entry; +import java.util.Objects; import java.util.Set; import java.util.UUID; import java.util.Vector; @@ -18,12 +19,17 @@ 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.TrackNodeLocation; 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.signal.SignalBoundary; 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.world.phys.Vec3; public class TravellingPoint { @@ -73,7 +79,7 @@ public class TravellingPoint { public ITrackSelector follow(TravellingPoint other) { return follow(other, null); } - + public ITrackSelector follow(TravellingPoint other, @Nullable Consumer success) { return (graph, pair) -> { List> validTargets = pair.getSecond(); @@ -331,4 +337,35 @@ public class TravellingPoint { .get(node2); } + public CompoundTag write() { + 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)); + tag.putDouble("Position", position); + return tag; + } + + public static TravellingPoint read(CompoundTag tag, TrackGraph graph) { + 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); + + if (locs.either(Objects::isNull)) + return new TravellingPoint(null, null, null, 0); + + double position = tag.getDouble("Position"); + return new TravellingPoint(locs.getFirst(), locs.getSecond(), graph.getConnectionsFrom(locs.getFirst()) + .get(locs.getSecond()), position); + } + } diff --git a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/ScheduleRuntime.java b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/ScheduleRuntime.java index bae3f1e32..9462fad0d 100644 --- a/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/ScheduleRuntime.java +++ b/src/main/java/com/simibubi/create/content/logistics/trains/management/schedule/ScheduleRuntime.java @@ -9,8 +9,10 @@ import com.simibubi.create.content.logistics.trains.management.edgePoint.station import com.simibubi.create.content.logistics.trains.management.schedule.condition.ScheduleWaitCondition; import com.simibubi.create.content.logistics.trains.management.schedule.destination.FilteredDestination; import com.simibubi.create.content.logistics.trains.management.schedule.destination.ScheduleDestination; +import com.simibubi.create.foundation.utility.NBTHelper; import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.Tag; import net.minecraft.world.level.Level; public class ScheduleRuntime { @@ -180,6 +182,9 @@ public class ScheduleRuntime { tag.putBoolean("Paused", paused); if (schedule != null) tag.put("Schedule", schedule.write()); + NBTHelper.writeEnum(tag, "State", state); + tag.putIntArray("ConditionProgress", conditionProgress); + tag.put("ConditionContext", NBTHelper.writeCompoundList(conditionContext, CompoundTag::copy)); return tag; } @@ -190,6 +195,10 @@ public class ScheduleRuntime { currentEntry = tag.getInt("CurrentEntry"); if (tag.contains("Schedule")) schedule = Schedule.fromTag(tag.getCompound("Schedule")); + state = NBTHelper.readEnum(tag, "State", State.class); + for (int i : tag.getIntArray("ConditionProgress")) + conditionProgress.add(i); + NBTHelper.iterateCompoundList(tag.getList("ConditionContext", Tag.TAG_COMPOUND), conditionContext::add); } } diff --git a/src/main/resources/assets/create/lang/default/interface.json b/src/main/resources/assets/create/lang/default/interface.json index 43f020106..7598aa187 100644 --- a/src/main/resources/assets/create/lang/default/interface.json +++ b/src/main/resources/assets/create/lang/default/interface.json @@ -645,7 +645,7 @@ "create.train.relocate.abort": "Relocation aborted", "create.train.relocate.success": "Relocation successful", "create.train.relocate.valid": "Can relocate to here, Click to Confirm", - "create.train.relocate.invalid": "Cannot relocate Train to this Track", + "create.train.relocate.invalid": "Cannot relocate Train to here", "create.train.relocate.too_far": "Cannot relocate Train this far away", "create.contraption.controls.start_controlling": "Now controlling: %1$s",