Train crash, game crash

- Added a new signal mode for easier junction & platform management
- Fixed crash during trains' tick after a collision
- Fixed Trains trying to avoid their own carriages in navigation
- Fixed Trains trying to avoid their own station in navigation
- Fixed Trains triggering signals behind their targeted station
- Fixed Trains reversing mid-travel when path cost behind them is lower
- Wait time at signals now affects the tick order of Trains
- Trains no longer change destination when they are close to arriving at their initial choice
- Fixed crashed trains no longer keeping signal blocks occupied
- Fixed crash when a trains' path update is unable to find the original destination
- Improved precision of trains arriving at signals/stations
This commit is contained in:
simibubi 2022-03-17 15:41:09 +01:00
parent 71e18eb505
commit a9a37b313c
48 changed files with 1041 additions and 254 deletions

View file

@ -484,7 +484,7 @@ e815bfd854c2653f10828bb11950f7fb991d7efc assets/create/blockstates/stressometer.
8b0c2c7ac72529565b3339aa8df7565858100afa assets/create/blockstates/tiled_glass.json 8b0c2c7ac72529565b3339aa8df7565858100afa assets/create/blockstates/tiled_glass.json
a2454400b1cf9889f70aebdc89c52a1be25f543c assets/create/blockstates/tiled_glass_pane.json a2454400b1cf9889f70aebdc89c52a1be25f543c assets/create/blockstates/tiled_glass_pane.json
85b57776edf426c2f8df6698b2482ea925914a5c assets/create/blockstates/track.json 85b57776edf426c2f8df6698b2482ea925914a5c assets/create/blockstates/track.json
5a61450b6f6aac63f75580f8dcbc3d3fabfd54d8 assets/create/blockstates/track_signal.json 408ae1009ee8bb2f2b83753d5909c53744f7865f assets/create/blockstates/track_signal.json
aa08785f906d41933e0dd1086ea7b08f5b93aa24 assets/create/blockstates/track_station.json aa08785f906d41933e0dd1086ea7b08f5b93aa24 assets/create/blockstates/track_station.json
29af21c8d82891139d48d69f0393f612f2b6f8f1 assets/create/blockstates/tuff_pillar.json 29af21c8d82891139d48d69f0393f612f2b6f8f1 assets/create/blockstates/tuff_pillar.json
a8094531617e27a545c4815ab2062bf0ffca3633 assets/create/blockstates/turntable.json a8094531617e27a545c4815ab2062bf0ffca3633 assets/create/blockstates/turntable.json
@ -541,21 +541,21 @@ bf2b0310500213ff853c748c236eb5d01f61658e assets/create/blockstates/yellow_toolbo
7f39521b211441f5c3e06d60c5978cebe16cacfb assets/create/blockstates/zinc_block.json 7f39521b211441f5c3e06d60c5978cebe16cacfb assets/create/blockstates/zinc_block.json
b7181bcd8182b2f17088e5aa881f374c9c65470c assets/create/blockstates/zinc_ore.json b7181bcd8182b2f17088e5aa881f374c9c65470c assets/create/blockstates/zinc_ore.json
255e47ab7894ba35851a2f34c82be3dc9c9e8784 assets/create/lang/en_ud.json 255e47ab7894ba35851a2f34c82be3dc9c9e8784 assets/create/lang/en_ud.json
b908dd7c47b286d05a57b8a58c60051363ffff86 assets/create/lang/en_us.json e6698e4672c04cf7b8251e2267b1fe3d6c60e50c assets/create/lang/en_us.json
29ad91d27ee183b9d4de8d152d0fb8ed2a388a0c assets/create/lang/unfinished/de_de.json dfd5234276f508edfb10160fa2b7859363acc313 assets/create/lang/unfinished/de_de.json
8d33ba82d3cca307c5ae0dda41a49ec16f1c7b74 assets/create/lang/unfinished/es_cl.json c23ca905e7aa9983057c970d9e799b4ba91a9acd assets/create/lang/unfinished/es_cl.json
ae674553b564a9e09a7d10c9c2d8c1be1307f40a assets/create/lang/unfinished/es_es.json 1781ed3854e04116319528699b4edee9cff4a435 assets/create/lang/unfinished/es_es.json
318149855f383fb90439f2f0818e8e83fee3924e assets/create/lang/unfinished/fr_fr.json 24d421795a67a19efd3a092b693c2397b3b84d9b assets/create/lang/unfinished/fr_fr.json
e7321dbe8f98c96e778b5c46823c98500b3001bc assets/create/lang/unfinished/it_it.json 50107fc09f422527f20b0a2ddee7e961540e4f6c assets/create/lang/unfinished/it_it.json
5a2da425fd0dd276c3d7cf66f6bb43f229574306 assets/create/lang/unfinished/ja_jp.json 570cc561fcb7f1839212e7ba3e1a68886874c846 assets/create/lang/unfinished/ja_jp.json
98fc1956e8b25aec27e889ada3a160800b958dfd assets/create/lang/unfinished/ko_kr.json f5ddd8b98314636c4c763cdd8aee7acd462b00a9 assets/create/lang/unfinished/ko_kr.json
67cefb6690f5cccce93d776a760ebdadc7f2631e assets/create/lang/unfinished/nl_nl.json f3696cf3bfcdff92eb11882be9be9fa2759c51c6 assets/create/lang/unfinished/nl_nl.json
9a7f4d369d2a54aaf5c422fae740244025a13af4 assets/create/lang/unfinished/pl_pl.json 3df68a7b3fb44dd666de541d7437c300d596aa7b assets/create/lang/unfinished/pl_pl.json
3656897ffc5c89bf16a32ad11efe33cd5b4bfb23 assets/create/lang/unfinished/pt_br.json 6a7fb1146f15117abca1a0ea970ab4054a1ee6a7 assets/create/lang/unfinished/pt_br.json
c84995ec524b6469fc42f39574be21b9dbee24e3 assets/create/lang/unfinished/pt_pt.json 6e993c9e6f5faa55448a8b24456082096cb10935 assets/create/lang/unfinished/pt_pt.json
bcf41d9fd8454296dc56326b6e26ee41e2ad52e5 assets/create/lang/unfinished/ru_ru.json d8fc231286d4a53e249037905c92513857432c4c assets/create/lang/unfinished/ru_ru.json
d3dfdab017748fa82f9e952a35bd6e82fe063771 assets/create/lang/unfinished/zh_cn.json 3aedca5bf7cf2f08cb53d6e8b5949009cddc1843 assets/create/lang/unfinished/zh_cn.json
dda3aee2ece5503edd53dbd4aa9db7363e4beafc assets/create/lang/unfinished/zh_tw.json 30f01dd054c4706d37f6d5ece0fc09ad241a6214 assets/create/lang/unfinished/zh_tw.json
487a511a01b2a4531fb672f917922312db78f958 assets/create/models/block/acacia_window.json 487a511a01b2a4531fb672f917922312db78f958 assets/create/models/block/acacia_window.json
b48060cba1a382f373a05bf0039054053eccf076 assets/create/models/block/acacia_window_pane_noside.json b48060cba1a382f373a05bf0039054053eccf076 assets/create/models/block/acacia_window_pane_noside.json
3066db1bf03cffa1a9c7fbacf47ae586632f4eb3 assets/create/models/block/acacia_window_pane_noside_alt.json 3066db1bf03cffa1a9c7fbacf47ae586632f4eb3 assets/create/models/block/acacia_window_pane_noside_alt.json

View file

@ -1,7 +1,10 @@
{ {
"variants": { "variants": {
"": { "type=entry_signal": {
"model": "create:block/track_signal/block" "model": "create:block/track_signal/block_entry_signal"
},
"type=cross_signal": {
"model": "create:block/track_signal/block_cross_signal"
} }
} }
} }

View file

@ -1424,6 +1424,10 @@
"create.train.relocate.invalid": "Cannot relocate Train to here", "create.train.relocate.invalid": "Cannot relocate Train to here",
"create.train.relocate.too_far": "Cannot relocate Train this far away", "create.train.relocate.too_far": "Cannot relocate Train this far away",
"create.track_signal.cannot_change_mode": "Unable to switch mode of this Signal",
"create.track_signal.mode_change.entry_signal": "-> Allow passage if section unoccupied",
"create.track_signal.mode_change.cross_signal": "-> Allow passage if section fully traversable",
"create.contraption.controls.start_controlling": "Now controlling: %1$s", "create.contraption.controls.start_controlling": "Now controlling: %1$s",
"create.contraption.controls.stop_controlling": "Stopped controlling contraption", "create.contraption.controls.stop_controlling": "Stopped controlling contraption",
"create.contraption.controls.approach_station": "Hold %1$s to approach %2$s", "create.contraption.controls.approach_station": "Hold %1$s to approach %2$s",

View file

@ -1,5 +1,5 @@
{ {
"_": "Missing Localizations: 1435", "_": "Missing Localizations: 1438",
"_": "->------------------------] Game Elements [------------------------<-", "_": "->------------------------] Game Elements [------------------------<-",
@ -1425,6 +1425,10 @@
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here", "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here",
"create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away", "create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away",
"create.track_signal.cannot_change_mode": "UNLOCALIZED: Unable to switch mode of this Signal",
"create.track_signal.mode_change.entry_signal": "UNLOCALIZED: -> Allow passage if section unoccupied",
"create.track_signal.mode_change.cross_signal": "UNLOCALIZED: -> Allow passage if section fully traversable",
"create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s", "create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s",
"create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption", "create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption",
"create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s", "create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s",

View file

@ -1,5 +1,5 @@
{ {
"_": "Missing Localizations: 446", "_": "Missing Localizations: 449",
"_": "->------------------------] Game Elements [------------------------<-", "_": "->------------------------] Game Elements [------------------------<-",
@ -1425,6 +1425,10 @@
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here", "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here",
"create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away", "create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away",
"create.track_signal.cannot_change_mode": "UNLOCALIZED: Unable to switch mode of this Signal",
"create.track_signal.mode_change.entry_signal": "UNLOCALIZED: -> Allow passage if section unoccupied",
"create.track_signal.mode_change.cross_signal": "UNLOCALIZED: -> Allow passage if section fully traversable",
"create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s", "create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s",
"create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption", "create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption",
"create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s", "create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s",

View file

@ -1,5 +1,5 @@
{ {
"_": "Missing Localizations: 446", "_": "Missing Localizations: 449",
"_": "->------------------------] Game Elements [------------------------<-", "_": "->------------------------] Game Elements [------------------------<-",
@ -1425,6 +1425,10 @@
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here", "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here",
"create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away", "create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away",
"create.track_signal.cannot_change_mode": "UNLOCALIZED: Unable to switch mode of this Signal",
"create.track_signal.mode_change.entry_signal": "UNLOCALIZED: -> Allow passage if section unoccupied",
"create.track_signal.mode_change.cross_signal": "UNLOCALIZED: -> Allow passage if section fully traversable",
"create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s", "create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s",
"create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption", "create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption",
"create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s", "create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s",

View file

@ -1,5 +1,5 @@
{ {
"_": "Missing Localizations: 1697", "_": "Missing Localizations: 1700",
"_": "->------------------------] Game Elements [------------------------<-", "_": "->------------------------] Game Elements [------------------------<-",
@ -1425,6 +1425,10 @@
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here", "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here",
"create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away", "create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away",
"create.track_signal.cannot_change_mode": "UNLOCALIZED: Unable to switch mode of this Signal",
"create.track_signal.mode_change.entry_signal": "UNLOCALIZED: -> Allow passage if section unoccupied",
"create.track_signal.mode_change.cross_signal": "UNLOCALIZED: -> Allow passage if section fully traversable",
"create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s", "create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s",
"create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption", "create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption",
"create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s", "create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s",

View file

@ -1,5 +1,5 @@
{ {
"_": "Missing Localizations: 1386", "_": "Missing Localizations: 1389",
"_": "->------------------------] Game Elements [------------------------<-", "_": "->------------------------] Game Elements [------------------------<-",
@ -1425,6 +1425,10 @@
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here", "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here",
"create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away", "create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away",
"create.track_signal.cannot_change_mode": "UNLOCALIZED: Unable to switch mode of this Signal",
"create.track_signal.mode_change.entry_signal": "UNLOCALIZED: -> Allow passage if section unoccupied",
"create.track_signal.mode_change.cross_signal": "UNLOCALIZED: -> Allow passage if section fully traversable",
"create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s", "create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s",
"create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption", "create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption",
"create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s", "create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s",

View file

@ -1,5 +1,5 @@
{ {
"_": "Missing Localizations: 116", "_": "Missing Localizations: 119",
"_": "->------------------------] Game Elements [------------------------<-", "_": "->------------------------] Game Elements [------------------------<-",
@ -1425,6 +1425,10 @@
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here", "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here",
"create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away", "create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away",
"create.track_signal.cannot_change_mode": "UNLOCALIZED: Unable to switch mode of this Signal",
"create.track_signal.mode_change.entry_signal": "UNLOCALIZED: -> Allow passage if section unoccupied",
"create.track_signal.mode_change.cross_signal": "UNLOCALIZED: -> Allow passage if section fully traversable",
"create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s", "create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s",
"create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption", "create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption",
"create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s", "create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s",

View file

@ -1,5 +1,5 @@
{ {
"_": "Missing Localizations: 118", "_": "Missing Localizations: 121",
"_": "->------------------------] Game Elements [------------------------<-", "_": "->------------------------] Game Elements [------------------------<-",
@ -1425,6 +1425,10 @@
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here", "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here",
"create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away", "create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away",
"create.track_signal.cannot_change_mode": "UNLOCALIZED: Unable to switch mode of this Signal",
"create.track_signal.mode_change.entry_signal": "UNLOCALIZED: -> Allow passage if section unoccupied",
"create.track_signal.mode_change.cross_signal": "UNLOCALIZED: -> Allow passage if section fully traversable",
"create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s", "create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s",
"create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption", "create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption",
"create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s", "create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s",

View file

@ -1,5 +1,5 @@
{ {
"_": "Missing Localizations: 2050", "_": "Missing Localizations: 2053",
"_": "->------------------------] Game Elements [------------------------<-", "_": "->------------------------] Game Elements [------------------------<-",
@ -1425,6 +1425,10 @@
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here", "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here",
"create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away", "create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away",
"create.track_signal.cannot_change_mode": "UNLOCALIZED: Unable to switch mode of this Signal",
"create.track_signal.mode_change.entry_signal": "UNLOCALIZED: -> Allow passage if section unoccupied",
"create.track_signal.mode_change.cross_signal": "UNLOCALIZED: -> Allow passage if section fully traversable",
"create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s", "create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s",
"create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption", "create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption",
"create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s", "create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s",

View file

@ -1,5 +1,5 @@
{ {
"_": "Missing Localizations: 485", "_": "Missing Localizations: 488",
"_": "->------------------------] Game Elements [------------------------<-", "_": "->------------------------] Game Elements [------------------------<-",
@ -1425,6 +1425,10 @@
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here", "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here",
"create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away", "create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away",
"create.track_signal.cannot_change_mode": "UNLOCALIZED: Unable to switch mode of this Signal",
"create.track_signal.mode_change.entry_signal": "UNLOCALIZED: -> Allow passage if section unoccupied",
"create.track_signal.mode_change.cross_signal": "UNLOCALIZED: -> Allow passage if section fully traversable",
"create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s", "create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s",
"create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption", "create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption",
"create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s", "create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s",

View file

@ -1,5 +1,5 @@
{ {
"_": "Missing Localizations: 1669", "_": "Missing Localizations: 1672",
"_": "->------------------------] Game Elements [------------------------<-", "_": "->------------------------] Game Elements [------------------------<-",
@ -1425,6 +1425,10 @@
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here", "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here",
"create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away", "create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away",
"create.track_signal.cannot_change_mode": "UNLOCALIZED: Unable to switch mode of this Signal",
"create.track_signal.mode_change.entry_signal": "UNLOCALIZED: -> Allow passage if section unoccupied",
"create.track_signal.mode_change.cross_signal": "UNLOCALIZED: -> Allow passage if section fully traversable",
"create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s", "create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s",
"create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption", "create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption",
"create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s", "create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s",

View file

@ -1,5 +1,5 @@
{ {
"_": "Missing Localizations: 1669", "_": "Missing Localizations: 1672",
"_": "->------------------------] Game Elements [------------------------<-", "_": "->------------------------] Game Elements [------------------------<-",
@ -1425,6 +1425,10 @@
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here", "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here",
"create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away", "create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away",
"create.track_signal.cannot_change_mode": "UNLOCALIZED: Unable to switch mode of this Signal",
"create.track_signal.mode_change.entry_signal": "UNLOCALIZED: -> Allow passage if section unoccupied",
"create.track_signal.mode_change.cross_signal": "UNLOCALIZED: -> Allow passage if section fully traversable",
"create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s", "create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s",
"create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption", "create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption",
"create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s", "create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s",

View file

@ -1,5 +1,5 @@
{ {
"_": "Missing Localizations: 490", "_": "Missing Localizations: 493",
"_": "->------------------------] Game Elements [------------------------<-", "_": "->------------------------] Game Elements [------------------------<-",
@ -1425,6 +1425,10 @@
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here", "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here",
"create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away", "create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away",
"create.track_signal.cannot_change_mode": "UNLOCALIZED: Unable to switch mode of this Signal",
"create.track_signal.mode_change.entry_signal": "UNLOCALIZED: -> Allow passage if section unoccupied",
"create.track_signal.mode_change.cross_signal": "UNLOCALIZED: -> Allow passage if section fully traversable",
"create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s", "create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s",
"create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption", "create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption",
"create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s", "create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s",

View file

@ -1,5 +1,5 @@
{ {
"_": "Missing Localizations: 116", "_": "Missing Localizations: 119",
"_": "->------------------------] Game Elements [------------------------<-", "_": "->------------------------] Game Elements [------------------------<-",
@ -1425,6 +1425,10 @@
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here", "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here",
"create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away", "create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away",
"create.track_signal.cannot_change_mode": "UNLOCALIZED: Unable to switch mode of this Signal",
"create.track_signal.mode_change.entry_signal": "UNLOCALIZED: -> Allow passage if section unoccupied",
"create.track_signal.mode_change.cross_signal": "UNLOCALIZED: -> Allow passage if section fully traversable",
"create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s", "create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s",
"create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption", "create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption",
"create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s", "create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s",

View file

@ -1,5 +1,5 @@
{ {
"_": "Missing Localizations: 504", "_": "Missing Localizations: 507",
"_": "->------------------------] Game Elements [------------------------<-", "_": "->------------------------] Game Elements [------------------------<-",
@ -1425,6 +1425,10 @@
"create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here", "create.train.relocate.invalid": "UNLOCALIZED: Cannot relocate Train to here",
"create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away", "create.train.relocate.too_far": "UNLOCALIZED: Cannot relocate Train this far away",
"create.track_signal.cannot_change_mode": "UNLOCALIZED: Unable to switch mode of this Signal",
"create.track_signal.mode_change.entry_signal": "UNLOCALIZED: -> Allow passage if section unoccupied",
"create.track_signal.mode_change.cross_signal": "UNLOCALIZED: -> Allow passage if section fully traversable",
"create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s", "create.contraption.controls.start_controlling": "UNLOCALIZED: Now controlling: %1$s",
"create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption", "create.contraption.controls.stop_controlling": "UNLOCALIZED: Stopped controlling contraption",
"create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s", "create.contraption.controls.approach_station": "UNLOCALIZED: Hold %1$s to approach %2$s",

View file

@ -151,6 +151,9 @@ public class AllBlockPartials {
SIGNAL_RED_CUBE = block("track_signal/red_cube"), SIGNAL_RED_CUBE = block("track_signal/red_cube"),
SIGNAL_RED_GLOW = block("track_signal/red_glow"), SIGNAL_RED_GLOW = block("track_signal/red_glow"),
SIGNAL_RED = block("track_signal/red_tube"), SIGNAL_RED = block("track_signal/red_tube"),
SIGNAL_YELLOW_CUBE = block("track_signal/yellow_cube"),
SIGNAL_YELLOW_GLOW = block("track_signal/yellow_glow"),
SIGNAL_YELLOW = block("track_signal/yellow_tube"),
CRAFTING_BLUEPRINT_1x1 = entity("crafting_blueprint_small"), CRAFTING_BLUEPRINT_1x1 = entity("crafting_blueprint_small"),
CRAFTING_BLUEPRINT_2x2 = entity("crafting_blueprint_medium"), CRAFTING_BLUEPRINT_2x2 = entity("crafting_blueprint_medium"),

View file

@ -1336,7 +1336,11 @@ public class AllBlocks {
.initialProperties(SharedProperties::softMetal) .initialProperties(SharedProperties::softMetal)
.properties(p -> p.sound(SoundType.NETHERITE_BLOCK)) .properties(p -> p.sound(SoundType.NETHERITE_BLOCK))
.transform(pickaxeOnly()) .transform(pickaxeOnly())
.blockstate((c, p) -> p.simpleBlock(c.get(), AssetLookup.partialBaseModel(c, p))) .blockstate((c, p) -> p.getVariantBuilder(c.get())
.forAllStates(state -> ConfiguredModel.builder()
.modelFile(AssetLookup.partialBaseModel(c, p, state.getValue(SignalBlock.TYPE)
.getSerializedName()))
.build()))
.lang("Train Signal") .lang("Train Signal")
.item(TrackTargetingBlockItem::new) .item(TrackTargetingBlockItem::new)
.transform(customItemModel()) .transform(customItemModel())

View file

@ -177,10 +177,11 @@ public class NixieTubeRenderer extends SafeTileEntityRenderer<NixieTubeTileEntit
if (first && !te.signalState.isRedLight(renderTime)) if (first && !te.signalState.isRedLight(renderTime))
continue; continue;
if (!first && !te.signalState.isGreenLight(renderTime)) if (!first && !te.signalState.isGreenLight(renderTime) && !te.signalState.isYellowLight(renderTime))
continue; continue;
boolean flip = first == invertTubes; boolean flip = first == invertTubes;
boolean yellow = te.signalState.isYellowLight(renderTime);
ms.pushPose(); ms.pushPose();
ms.translate(flip ? 4 / 16f : -4 / 16f, 0, 0); ms.translate(flip ? 4 / 16f : -4 / 16f, 0, 0);
@ -188,22 +189,29 @@ public class NixieTubeRenderer extends SafeTileEntityRenderer<NixieTubeTileEntit
if (diff.lengthSqr() < 36 * 36) { if (diff.lengthSqr() < 36 * 36) {
boolean vert = first ^ facing.getAxis() boolean vert = first ^ facing.getAxis()
.isHorizontal(); .isHorizontal();
float longSide = yellow ? 1 : 4;
float longSideGlow = yellow ? 2 : 5.125f;
CachedBufferer.partial(AllBlockPartials.SIGNAL_WHITE_CUBE, blockState) CachedBufferer.partial(AllBlockPartials.SIGNAL_WHITE_CUBE, blockState)
.light(0xf000f0) .light(0xf000f0)
.disableDiffuseMult() .disableDiffuseMult()
.scale(vert ? 4 : 1, vert ? 1 : 4, 1) .scale(vert ? longSide : 1, vert ? 1 : longSide, 1)
.renderInto(ms, buffer.getBuffer(RenderType.translucent())); .renderInto(ms, buffer.getBuffer(RenderType.translucent()));
CachedBufferer CachedBufferer
.partial(first ? AllBlockPartials.SIGNAL_RED_GLOW : AllBlockPartials.SIGNAL_WHITE_GLOW, blockState) .partial(
first ? AllBlockPartials.SIGNAL_RED_GLOW
: yellow ? AllBlockPartials.SIGNAL_YELLOW_GLOW : AllBlockPartials.SIGNAL_WHITE_GLOW,
blockState)
.light(0xf000f0) .light(0xf000f0)
.disableDiffuseMult() .disableDiffuseMult()
.scale(vert ? 5.125f : 2, vert ? 2 : 5.125f, 2) .scale(vert ? longSideGlow : 2, vert ? 2 : longSideGlow, 2)
.renderInto(ms, buffer.getBuffer(RenderTypes.getAdditive())); .renderInto(ms, buffer.getBuffer(RenderTypes.getAdditive()));
} }
CachedBufferer.partial(first ? AllBlockPartials.SIGNAL_RED : AllBlockPartials.SIGNAL_WHITE, blockState) CachedBufferer
.partial(first ? AllBlockPartials.SIGNAL_RED
: yellow ? AllBlockPartials.SIGNAL_YELLOW : AllBlockPartials.SIGNAL_WHITE, blockState)
.light(0xF000F0) .light(0xF000F0)
.disableDiffuseMult() .disableDiffuseMult()
.scale(1 + 1 / 16f) .scale(1 + 1 / 16f)

View file

@ -3,6 +3,8 @@ package com.simibubi.create.content.logistics.trains;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
@ -39,6 +41,9 @@ public class GlobalRailwayManager {
public Map<UUID, Train> trains; public Map<UUID, Train> trains;
public TrackGraphSync sync; public TrackGraphSync sync;
private List<Train> movingTrains;
private List<Train> waitingTrains;
private RailwaySavedData savedData; private RailwaySavedData savedData;
public GlobalRailwayManager() { public GlobalRailwayManager() {
@ -75,6 +80,8 @@ public class GlobalRailwayManager {
trains = savedData.getTrains(); trains = savedData.getTrains();
trackNetworks = savedData.getTrackNetworks(); trackNetworks = savedData.getTrackNetworks();
signalEdgeGroups = savedData.getSignalBlocks(); signalEdgeGroups = savedData.getSignalBlocks();
trains.values()
.forEach(movingTrains::add);
} }
public void cleanUp() { public void cleanUp() {
@ -82,6 +89,8 @@ public class GlobalRailwayManager {
signalEdgeGroups = new HashMap<>(); signalEdgeGroups = new HashMap<>();
trains = new HashMap<>(); trains = new HashMap<>();
sync = new TrackGraphSync(); sync = new TrackGraphSync();
movingTrains = new LinkedList<>();
waitingTrains = new LinkedList<>();
} }
public void markTracksDirty() { public void markTracksDirty() {
@ -89,6 +98,19 @@ public class GlobalRailwayManager {
savedData.setDirty(); savedData.setDirty();
} }
public void addTrain(Train train) {
trains.put(train.id, train);
movingTrains.add(train);
}
public void removeTrain(UUID id) {
Train removed = trains.remove(id);
if (removed == null)
return;
movingTrains.remove(removed);
waitingTrains.remove(removed);
}
// //
public TrackGraph getOrCreateGraph(UUID graphID) { public TrackGraph getOrCreateGraph(UUID graphID) {
@ -145,22 +167,51 @@ public class GlobalRailwayManager {
group.trains.clear(); group.trains.clear();
group.reserved = null; group.reserved = null;
} }
for (TrackGraph graph : trackNetworks.values()) for (TrackGraph graph : trackNetworks.values())
graph.tickPoints(); graph.tickPoints(true);
for (Train train : trains.values())
train.earlyTick(level);
for (Train train : trains.values())
train.tick(level);
// if (AllKeys.isKeyDown(GLFW.GLFW_KEY_H) && AllKeys.altDown()) tickTrains(level);
for (TrackGraph graph : trackNetworks.values())
graph.tickPoints(false);
// if (AllKeys.isKeyDown(GLFW.GLFW_KEY_K))
// trackNetworks.values() // trackNetworks.values()
// .forEach(TrackGraph::debugViewSignalData); // .forEach(TrackGraph::debugViewReserved);
// if (AllKeys.isKeyDown(GLFW.GLFW_KEY_J) && AllKeys.altDown()) // if (AllKeys.isKeyDown(GLFW.GLFW_KEY_J) && AllKeys.altDown())
// trackNetworks.values() // trackNetworks.values()
// .forEach(TrackGraph::debugViewNodes); // .forEach(TrackGraph::debugViewNodes);
} }
private void tickTrains(Level level) {
// keeping two lists ensures a tick order starting at longest waiting
for (Train train : waitingTrains)
train.earlyTick(level);
for (Train train : movingTrains)
train.earlyTick(level);
for (Train train : waitingTrains)
train.tick(level);
for (Train train : movingTrains)
train.tick(level);
for (Iterator<Train> iterator = waitingTrains.iterator(); iterator.hasNext();) {
Train train = iterator.next();
if (train.navigation.waitingForSignal != null)
continue;
movingTrains.add(train);
iterator.remove();
}
for (Iterator<Train> iterator = movingTrains.iterator(); iterator.hasNext();) {
Train train = iterator.next();
if (train.navigation.waitingForSignal == null)
continue;
waitingTrains.add(train);
iterator.remove();
}
}
public void clientTick() { public void clientTick() {
if (AllKeys.isKeyDown(GLFW.GLFW_KEY_H) && !AllKeys.altDown()) if (AllKeys.isKeyDown(GLFW.GLFW_KEY_H) && !AllKeys.altDown())
trackNetworks.values() trackNetworks.values()

View file

@ -98,8 +98,8 @@ public class TrackGraph {
return removed; return removed;
} }
public void tickPoints() { public void tickPoints(boolean preTrains) {
edgePoints.tick(this); edgePoints.tick(this, preTrains);
} }
// //
@ -425,6 +425,129 @@ public class TrackGraph {
return graph; return graph;
} }
public void debugViewReserved() {
Entity cameraEntity = Minecraft.getInstance().cameraEntity;
if (cameraEntity == null)
return;
Set<UUID> reserved = new HashSet<>();
Set<UUID> occupied = new HashSet<>();
for (Train train : Create.RAILWAYS.trains.values()) {
reserved.addAll(train.reservedSignalBlocks);
occupied.addAll(train.occupiedSignalBlocks.keySet());
}
reserved.removeAll(occupied);
Vec3 camera = cameraEntity.getEyePosition();
for (Entry<TrackNodeLocation, TrackNode> nodeEntry : nodes.entrySet()) {
TrackNodeLocation nodeLocation = nodeEntry.getKey();
TrackNode node = nodeEntry.getValue();
if (nodeLocation == null)
continue;
Vec3 location = nodeLocation.getLocation();
if (location.distanceTo(camera) > 100)
continue;
Map<TrackNode, TrackEdge> map = connectionsByNode.get(node);
if (map == null)
continue;
int hashCode = node.hashCode();
for (Entry<TrackNode, TrackEdge> entry : map.entrySet()) {
TrackNode other = entry.getKey();
if (other.hashCode() > hashCode && !AllKeys.isKeyDown(GLFW.GLFW_KEY_LEFT_CONTROL))
continue;
Vec3 yOffset = new Vec3(0, (other.hashCode() > hashCode ? 6 : 4) / 16f, 0);
TrackEdge edge = entry.getValue();
EdgeData signalData = edge.getEdgeData();
UUID singleGroup = signalData.singleSignalGroup;
SignalEdgeGroup signalEdgeGroup =
singleGroup == null ? null : Create.RAILWAYS.sided(null).signalEdgeGroups.get(singleGroup);
if (!edge.isTurn()) {
Vec3 p1 = edge.getPosition(node, other, 0);
Vec3 p2 = edge.getPosition(node, other, 1);
if (signalData.hasPoints()) {
double prev = 0;
double length = edge.getLength(node, other);
SignalBoundary prevBoundary = null;
SignalEdgeGroup group = null;
for (TrackEdgePoint trackEdgePoint : signalData.getPoints()) {
if (!(trackEdgePoint instanceof SignalBoundary boundary))
continue;
prevBoundary = boundary;
UUID groupId = boundary.getGroup(node);
group = Create.RAILWAYS.sided(null).signalEdgeGroups.get(groupId);
double start = prev + (prev == 0 ? 0 : 1 / 16f / length);
prev = (boundary.getLocationOn(node, other, edge) / length) - 1 / 16f / length;
if (group != null
&& (group.reserved != null || occupied.contains(groupId) || reserved.contains(groupId)))
CreateClient.OUTLINER
.showLine(Pair.of(boundary, edge), edge.getPosition(node, other, start)
.add(yOffset),
edge.getPosition(node, other, prev)
.add(yOffset))
.colored(occupied.contains(groupId) ? 0xF68989
: group.reserved != null ? 0xC5D8A4 : 0xF6E7D8)
.lineWidth(1 / 16f);
}
if (prevBoundary != null) {
UUID groupId = prevBoundary.getGroup(other);
SignalEdgeGroup lastGroup = Create.RAILWAYS.sided(null).signalEdgeGroups.get(groupId);
if (lastGroup != null && ((lastGroup.reserved != null || occupied.contains(groupId)
|| reserved.contains(groupId))))
CreateClient.OUTLINER
.showLine(edge, edge.getPosition(node, other, prev + 1 / 16f / length)
.add(yOffset), p2.add(yOffset))
.colored(occupied.contains(groupId) ? 0xF68989
: lastGroup.reserved != null ? 0xC5D8A4 : 0xF6E7D8)
.lineWidth(1 / 16f);
continue;
}
}
if (signalEdgeGroup == null || !(signalEdgeGroup.reserved != null || occupied.contains(singleGroup)
|| reserved.contains(singleGroup)))
continue;
CreateClient.OUTLINER.showLine(edge, p1.add(yOffset), p2.add(yOffset))
.colored(occupied.contains(singleGroup) ? 0xF68989
: signalEdgeGroup.reserved != null ? 0xC5D8A4 : 0xF6E7D8)
.lineWidth(1 / 16f);
continue;
}
if (signalEdgeGroup == null || !(signalEdgeGroup.reserved != null || occupied.contains(singleGroup)
|| reserved.contains(singleGroup)))
continue;
int color =
occupied.contains(singleGroup) ? 0xF68989 : signalEdgeGroup.reserved != null ? 0xC5D8A4 : 0xF6E7D8;
Vec3 previous = null;
BezierConnection turn = edge.getTurn();
for (int i = 0; i <= turn.getSegmentCount(); i++) {
Vec3 current = edge.getPosition(node, other, i * 1f / turn.getSegmentCount());
if (previous != null)
CreateClient.OUTLINER
.showLine(Pair.of(edge, previous), previous.add(yOffset), current.add(yOffset))
.colored(color)
.lineWidth(1 / 16f);
previous = current;
}
}
}
}
public void debugViewSignalData() { public void debugViewSignalData() {
Entity cameraEntity = Minecraft.getInstance().cameraEntity; Entity cameraEntity = Minecraft.getInstance().cameraEntity;
if (cameraEntity == null) if (cameraEntity == null)

View file

@ -11,7 +11,7 @@ import javax.annotation.Nullable;
import org.apache.commons.lang3.mutable.MutableDouble; import org.apache.commons.lang3.mutable.MutableDouble;
import com.simibubi.create.content.logistics.trains.TrackGraph; import com.simibubi.create.content.logistics.trains.TrackGraph;
import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.ISignalBoundaryListener; import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.IEdgePointListener;
import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.ITrackSelector; import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.ITrackSelector;
import com.simibubi.create.foundation.utility.Couple; import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.Iterate;
@ -131,9 +131,9 @@ public class Carriage {
boolean atBack = boolean atBack =
(type == LAST || type == BOTH) && !actuallyFirstWheel && (!actuallyFirstBogey || !onTwoBogeys); (type == LAST || type == BOTH) && !actuallyFirstWheel && (!actuallyFirstBogey || !onTwoBogeys);
ISignalBoundaryListener frontListener = train.frontSignalListener(); IEdgePointListener frontListener = train.frontSignalListener();
ISignalBoundaryListener backListener = train.backSignalListener(); IEdgePointListener backListener = train.backSignalListener();
ISignalBoundaryListener passiveListener = point.ignoreSignals(); IEdgePointListener passiveListener = point.ignoreEdgePoints();
toMove += correction + bogeyCorrection; toMove += correction + bogeyCorrection;
double moved = double moved =

View file

@ -239,7 +239,7 @@ public class CarriageSyncData {
TravellingPoint toApproach = pointsToApproach[index]; TravellingPoint toApproach = pointsToApproach[index];
point.travel(graph, partial * f, point.travel(graph, partial * f,
point.follow(toApproach, b -> success.setValue(success.booleanValue() && b)), point.ignoreSignals(), point.follow(toApproach, b -> success.setValue(success.booleanValue() && b)), point.ignoreEdgePoints(),
point.ignoreTurns()); point.ignoreTurns());
// could not pathfind to server location // could not pathfind to server location

View file

@ -24,6 +24,7 @@ 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.entity.TravellingPoint.ITrackSelector;
import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgeData; 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.EdgePointType;
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.SignalBoundary;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalEdgeGroup; import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalEdgeGroup;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.TrackEdgePoint; import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.TrackEdgePoint;
@ -52,6 +53,7 @@ public class Navigation {
private TravellingPoint signalScout; private TravellingPoint signalScout;
public Pair<UUID, Boolean> waitingForSignal; public Pair<UUID, Boolean> waitingForSignal;
private Set<UUID> waitingForChainedGroups;
public double distanceToSignal; public double distanceToSignal;
public int ticksWaitingForSignal; public int ticksWaitingForSignal;
@ -59,6 +61,7 @@ public class Navigation {
this.train = train; this.train = train;
currentPath = new ArrayList<>(); currentPath = new ArrayList<>();
signalScout = new TravellingPoint(); signalScout = new TravellingPoint();
waitingForChainedGroups = new HashSet<>();
} }
public void tick(Level level) { public void tick(Level level) {
@ -97,8 +100,13 @@ public class Navigation {
// Signals // Signals
if (train.graph != null) { if (train.graph != null) {
if (waitingForSignal != null && checkBlockingSignal()) if (waitingForSignal != null && currentSignalResolved()) {
SignalBoundary signal = train.graph.getPoint(EdgePointType.SIGNAL, waitingForSignal.getFirst());
if (signal.types.get(waitingForSignal.getSecond()) == SignalType.CROSS_SIGNAL)
train.reservedSignalBlocks.addAll(waitingForChainedGroups);
waitingForSignal = null; waitingForSignal = null;
waitingForChainedGroups.clear();
}
TravellingPoint leadingPoint = !destinationBehindTrain ? train.carriages.get(0) TravellingPoint leadingPoint = !destinationBehindTrain ? train.carriages.get(0)
.getLeadingPoint() .getLeadingPoint()
@ -120,49 +128,103 @@ public class Navigation {
double brakingDistanceNoFlicker = double brakingDistanceNoFlicker =
Math.max(preDepartureLookAhead, brakingDistance + 3 - (brakingDistance % 3)); Math.max(preDepartureLookAhead, brakingDistance + 3 - (brakingDistance % 3));
double scanDistance = Math.min(distanceToDestination - .5f, brakingDistanceNoFlicker); double scanDistance = Math.min(distanceToDestination, brakingDistanceNoFlicker);
signalScout.travel(train.graph, scanDistance * speedMod, controlSignalScout(), (distance, couple) -> { MutableDouble crossSignalDistanceTracker = new MutableDouble(-1);
UUID entering = couple.getSecond() MutableObject<Pair<UUID, Boolean>> trackingCrossSignal = new MutableObject<>(null);
.getSecond(); waitingForChainedGroups.clear();
SignalEdgeGroup signalEdgeGroup = Create.RAILWAYS.signalEdgeGroups.get(entering); // train.reservedSignalBlocks.clear();
if (signalEdgeGroup == null)
return; signalScout.travel(train.graph, distanceToDestination * speedMod, controlSignalScout(),
SignalBoundary boundary = couple.getFirst(); (distance, couple) -> {
if (signalEdgeGroup.isOccupiedUnless(train)) { // > scanDistance and not following down a cross signal
distanceToSignal = Math.min(distance, distanceToSignal); boolean crossSignalTracked = trackingCrossSignal.getValue() != null;
waitingForSignal = Pair.of(boundary.id, entering.equals(boundary.groups.getFirst())); if (!crossSignalTracked && distance > scanDistance)
return; return true;
Couple<TrackNode> nodes = couple.getSecond();
TrackEdgePoint boundary = couple.getFirst();
if (boundary == destination && ((GlobalStation) boundary).canApproachFrom(nodes.getSecond()))
return true;
if (!(boundary instanceof SignalBoundary signal))
return false;
UUID entering = signal.getGroup(nodes.getSecond());
SignalEdgeGroup signalEdgeGroup = Create.RAILWAYS.signalEdgeGroups.get(entering);
if (signalEdgeGroup == null)
return false;
boolean primary = entering.equals(signal.groups.getFirst());
boolean crossSignal = signal.types.get(primary) == SignalType.CROSS_SIGNAL;
boolean occupied = signalEdgeGroup.isOccupiedUnless(train);
if (!occupied && distance < distanceToSignal + .25)
signalEdgeGroup.reserved = signal; // Reserve group for traversal, unless waiting at an
// earlier cross signal
if (!crossSignalTracked) {
if (crossSignal) { // Now entering cross signal path
trackingCrossSignal.setValue(Pair.of(boundary.id, primary));
crossSignalDistanceTracker.setValue(distance);
waitingForChainedGroups.add(entering);
}
if (occupied) { // Section is occupied
waitingForSignal = Pair.of(boundary.id, primary);
distanceToSignal = distance;
if (!crossSignal)
return true; // Standard entry signal, do not collect any further segments
}
} else {
waitingForChainedGroups.add(entering); // Add group to chain
if (occupied) { // Section is occupied, but wait at the cross signal that started the chain
waitingForSignal = trackingCrossSignal.getValue();
distanceToSignal = crossSignalDistanceTracker.doubleValue();
if (!crossSignal)
return true; // Entry signals end a chain
}
if (!crossSignal) {
if (distance < distanceToSignal + .25) {
// Collect and reset the signal chain because none were blocked
trackingCrossSignal.setValue(null);
train.reservedSignalBlocks.addAll(waitingForChainedGroups);
waitingForChainedGroups.clear();
return false;
} else
return true; // End of a blocked signal chain
}
}
return false;
}, (distance, edge) -> {
float current = curveDistanceTracker.floatValue();
if (current == -1 || distance < current)
curveDistanceTracker.setValue(distance);
});
if (trackingCrossSignal.getValue() != null) {
if (waitingForSignal == null) {
train.reservedSignalBlocks.addAll(waitingForChainedGroups);
waitingForChainedGroups.clear();
} }
signalEdgeGroup.reserved = boundary; }
}, (distance, edge) -> {
float current = curveDistanceTracker.floatValue();
if (current == -1 || distance < current)
curveDistanceTracker.setValue(distance);
});
distanceToNextCurve = curveDistanceTracker.floatValue(); distanceToNextCurve = curveDistanceTracker.floatValue();
} else { } else {
ticksWaitingForSignal++; ticksWaitingForSignal++;
// if chain signal try finding new path/destination every x ticks
} }
} }
double targetDistance = waitingForSignal != null ? distanceToSignal : distanceToDestination; double targetDistance = waitingForSignal != null ? distanceToSignal : distanceToDestination;
if (targetDistance < 1 / 32f) { // always overshoot to ensure the travelling point crosses the target
train.speed = 0; targetDistance += 0.25d;
if (waitingForSignal != null) {
distanceToSignal = 0; // dont leave until green light
return; if (targetDistance > 1 / 32f && train.getCurrentStation() != null) {
}
distanceToDestination = 0;
currentPath.clear();
train.arriveAt(destination);
destination = null;
return;
} else if (train.getCurrentStation() != null) {
// dont leave until green light
if (waitingForSignal != null && distanceToSignal < preDepartureLookAhead) if (waitingForSignal != null && distanceToSignal < preDepartureLookAhead)
return; return;
train.leaveStation(); train.leaveStation();
@ -170,8 +232,13 @@ public class Navigation {
train.currentlyBackwards = destinationBehindTrain; train.currentlyBackwards = destinationBehindTrain;
if (targetDistance < -10) {
cancelNavigation();
return;
}
if (targetDistance - Math.abs(train.speed) < 1 / 32f) { if (targetDistance - Math.abs(train.speed) < 1 / 32f) {
train.speed = targetDistance * speedMod; train.speed = Math.max(targetDistance, 1 / 32f) * speedMod;
return; return;
} }
@ -198,12 +265,26 @@ public class Navigation {
train.approachTargetSpeed(1); train.approachTargetSpeed(1);
} }
private boolean checkBlockingSignal() { private boolean currentSignalResolved() {
if (distanceToDestination < .5f) if (distanceToDestination < .5f)
return true; return true;
SignalBoundary signal = train.graph.getPoint(EdgePointType.SIGNAL, waitingForSignal.getFirst()); SignalBoundary signal = train.graph.getPoint(EdgePointType.SIGNAL, waitingForSignal.getFirst());
if (signal == null) if (signal == null)
return true; return true;
// Cross Signal
if (signal.types.get(waitingForSignal.getSecond()) == SignalType.CROSS_SIGNAL) {
for (UUID groupId : waitingForChainedGroups) {
SignalEdgeGroup signalEdgeGroup = Create.RAILWAYS.signalEdgeGroups.get(groupId);
if (signalEdgeGroup == null)
continue;
if (signalEdgeGroup.isOccupiedUnless(train))
return false;
}
return true;
}
// Entry Signal
UUID groupId = signal.groups.get(waitingForSignal.getSecond()); UUID groupId = signal.groups.get(waitingForSignal.getSecond());
if (groupId == null) if (groupId == null)
return true; return true;
@ -255,6 +336,7 @@ public class Navigation {
destination.cancelReservation(train); destination.cancelReservation(train);
destination = null; destination = null;
train.runtime.transitInterrupted(); train.runtime.transitInterrupted();
train.reservedSignalBlocks.clear();
} }
public double startNavigation(GlobalStation destination, double maxCost, boolean simulate) { public double startNavigation(GlobalStation destination, double maxCost, boolean simulate) {
@ -267,16 +349,19 @@ public class Navigation {
return cost; return cost;
distanceToDestination = distance; distanceToDestination = distance;
currentPath = pathTo.path;
if (noneFound) { if (noneFound) {
distanceToDestination = 0; distanceToDestination = 0;
currentPath = new ArrayList<>();
if (this.destination != null) if (this.destination != null)
cancelNavigation(); cancelNavigation();
return -1; return -1;
} }
currentPath = pathTo.path;
destinationBehindTrain = pathTo.distance < 0; destinationBehindTrain = pathTo.distance < 0;
train.reservedSignalBlocks.clear();
train.navigation.waitingForSignal = null;
if (this.destination == destination) if (this.destination == destination)
return 0; return 0;
@ -315,7 +400,9 @@ public class Navigation {
Couple<DiscoveredPath> results = Couple.create(null, null); Couple<DiscoveredPath> results = Couple.create(null, null);
for (boolean forward : Iterate.trueAndFalse) { for (boolean forward : Iterate.trueAndFalse) {
if (this.destination == destination && destinationBehindTrain == forward)
// When updating destinations midtransit, avoid reversing out of path
if (this.destination != null && destinationBehindTrain == forward)
continue; continue;
TravellingPoint initialPoint = forward ? train.carriages.get(0) TravellingPoint initialPoint = forward ? train.carriages.get(0)
@ -377,6 +464,9 @@ public class Navigation {
if (!canDriveForward) if (!canDriveForward)
return back; return back;
// Debug.debugChat("Front: " + front.distance + ", Back: " + back.distance);
// Debug.debugChat("FrontCost: " + front.cost + ", BackCost: " + back.cost);
boolean frontBetter = maxCost == -1 ? -back.distance > front.distance : back.cost > front.cost; boolean frontBetter = maxCost == -1 ? -back.distance > front.distance : back.cost > front.cost;
return frontBetter ? front : back; return frontBetter ? front : back;
} }
@ -438,6 +528,8 @@ public class Navigation {
for (Train otherTrain : Create.RAILWAYS.trains.values()) { for (Train otherTrain : Create.RAILWAYS.trains.values()) {
if (otherTrain.graph != graph) if (otherTrain.graph != graph)
continue; continue;
if (otherTrain == train)
continue;
int navigationPenalty = otherTrain.getNavigationPenalty(); int navigationPenalty = otherTrain.getNavigationPenalty();
otherTrain.getEndpointEdges() otherTrain.getEndpointEdges()
.forEach(nodes -> { .forEach(nodes -> {
@ -474,6 +566,8 @@ public class Navigation {
Search: while (!frontier.isEmpty()) { Search: while (!frontier.isEmpty()) {
FrontierEntry entry = frontier.poll(); FrontierEntry entry = frontier.poll();
if (!visited.add(entry.edge))
continue;
double distance = entry.distance; double distance = entry.distance;
int penalty = entry.penalty; int penalty = entry.penalty;
@ -499,12 +593,15 @@ public class Navigation {
if (!point.canNavigateVia(node2)) if (!point.canNavigateVia(node2))
continue Search; continue Search;
if (point instanceof GlobalStation station) { if (point instanceof GlobalStation station) {
if (station.getPresentTrain() != null) Train presentTrain = station.getPresentTrain();
boolean isOwnStation = presentTrain == train;
if (presentTrain != null && !isOwnStation)
penalty += Train.Penalties.STATION_WITH_TRAIN; penalty += Train.Penalties.STATION_WITH_TRAIN;
if (station.canApproachFrom(node2) && stationTest.test(distance, distance + penalty, reachedVia, if (station.canApproachFrom(node2) && stationTest.test(distance, distance + penalty, reachedVia,
Pair.of(Couple.create(node1, node2), edge), station)) Pair.of(Couple.create(node1, node2), edge), station))
return; return;
penalty += Train.Penalties.STATION; if (!isOwnStation)
penalty += Train.Penalties.STATION;
} }
} }
} }
@ -521,8 +618,6 @@ public class Navigation {
Vec3 newDirection = newEdge.getDirection(node2, newNode, true); Vec3 newDirection = newEdge.getDirection(node2, newNode, true);
if (currentDirection.dot(newDirection) < 3 / 4f) if (currentDirection.dot(newDirection) < 3 / 4f)
continue; continue;
if (!visited.add(connection.getValue()))
continue;
validTargets.add(connection); validTargets.add(connection);
} }

View file

@ -1,6 +1,7 @@
package com.simibubi.create.content.logistics.trains.entity; package com.simibubi.create.content.logistics.trains.entity;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.Iterator; import java.util.Iterator;
@ -23,11 +24,12 @@ import com.simibubi.create.content.logistics.trains.GraphLocation;
import com.simibubi.create.content.logistics.trains.TrackEdge; import com.simibubi.create.content.logistics.trains.TrackEdge;
import com.simibubi.create.content.logistics.trains.TrackGraph; import com.simibubi.create.content.logistics.trains.TrackGraph;
import com.simibubi.create.content.logistics.trains.TrackNode; import com.simibubi.create.content.logistics.trains.TrackNode;
import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.ISignalBoundaryListener; import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.IEdgePointListener;
import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.ITrackSelector; import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.ITrackSelector;
import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.SteerDirection; 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.EdgeData;
import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointType; import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointType;
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.SignalBoundary;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalEdgeGroup; 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.GlobalStation;
@ -82,7 +84,8 @@ public class Train {
public List<Integer> carriageSpacing; public List<Integer> carriageSpacing;
public boolean updateSignalBlocks; public boolean updateSignalBlocks;
public List<UUID> occupiedSignalBlocks; public Map<UUID, UUID> occupiedSignalBlocks;
public Set<UUID> reservedSignalBlocks;
List<TrainMigration> migratingPoints; List<TrainMigration> migratingPoints;
public int migrationCooldown; public int migrationCooldown;
@ -112,28 +115,35 @@ public class Train {
migratingPoints = new ArrayList<>(); migratingPoints = new ArrayList<>();
currentStation = null; currentStation = null;
manualSteer = SteerDirection.NONE; manualSteer = SteerDirection.NONE;
occupiedSignalBlocks = new ArrayList<>(); occupiedSignalBlocks = new HashMap<>();
reservedSignalBlocks = new HashSet<>();
} }
public void earlyTick(Level level) { public void earlyTick(Level level) {
status.tick(level); status.tick(level);
if (graph == null && !migratingPoints.isEmpty()) if (graph == null && !migratingPoints.isEmpty())
reattachToTracks(level); reattachToTracks(level);
addToSignalGroups(occupiedSignalBlocks.keySet());
if (graph == null) if (graph == null)
return; return;
if (updateSignalBlocks) { if (updateSignalBlocks) {
updateSignalBlocks = false; updateSignalBlocks = false;
collectInitiallyOccupiedSignalBlocks(); collectInitiallyOccupiedSignalBlocks();
} }
for (Iterator<UUID> iterator = occupiedSignalBlocks.iterator(); iterator.hasNext();) { addToSignalGroups(reservedSignalBlocks);
SignalEdgeGroup signalEdgeGroup = Create.RAILWAYS.signalEdgeGroups.get(iterator.next()); }
if (signalEdgeGroup == null) {
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(); iterator.remove();
continue; else
} signalEdgeGroup.trains.add(this);
signalEdgeGroup.trains.add(this);
} }
} }
@ -212,6 +222,8 @@ public class Train {
if (index == 0) { if (index == 0) {
distance = actualDistance; distance = actualDistance;
collideWithOtherTrains(level, carriage); collideWithOtherTrains(level, carriage);
if (graph == null)
return;
} }
} }
@ -226,23 +238,58 @@ public class Train {
updateNavigationTarget(distance); updateNavigationTarget(distance);
} }
public ISignalBoundaryListener frontSignalListener() { public IEdgePointListener frontSignalListener() {
return (distance, couple) -> { return (distance, couple) -> {
UUID groupId = couple.getSecond()
.getSecond(); if (couple.getFirst()instanceof GlobalStation station) {
SignalEdgeGroup signalEdgeGroup = Create.RAILWAYS.signalEdgeGroups.get(groupId); if (!station.canApproachFrom(couple.getSecond()
SignalBoundary boundary = couple.getFirst(); .getSecond()) || navigation.destination != station)
if (signalEdgeGroup != null) { return false;
signalEdgeGroup.reserved = boundary; speed = 0;
occupiedSignalBlocks.add(groupId); navigation.distanceToDestination = 0;
navigation.currentPath.clear();
arriveAt(navigation.destination);
navigation.destination = null;
return true;
} }
if (!(couple.getFirst()instanceof SignalBoundary signal))
return false;
if (navigation.waitingForSignal != null && navigation.waitingForSignal.getFirst()
.equals(signal.id)) {
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;
signalEdgeGroup.reserved = signal;
occupy(groupId, signal.id);
return false;
}; };
} }
public ISignalBoundaryListener backSignalListener() { 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) -> { return (distance, couple) -> {
occupiedSignalBlocks.remove(couple.getSecond() if (!(couple.getFirst()instanceof SignalBoundary signal))
return false;
UUID groupId = signal.getGroup(couple.getSecond()
.getFirst()); .getFirst());
occupiedSignalBlocks.remove(groupId);
return false;
}; };
} }
@ -250,32 +297,43 @@ public class Train {
if (navigation.destination == null) if (navigation.destination == null)
return; return;
boolean recalculate = navigation.distanceToDestination % 100 > 20; Pair<UUID, Boolean> blockingSignal = navigation.waitingForSignal;
boolean imminentRecalculate = navigation.distanceToDestination > 5; 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; double toSubstract = navigation.destinationBehindTrain ? -distance : distance;
navigation.distanceToDestination -= toSubstract;
boolean signalMode = navigation.waitingForSignal != null;
boolean navigatingManually = runtime.paused; boolean navigatingManually = runtime.paused;
if (signalMode) { navigation.distanceToDestination -= toSubstract;
if (blockingSignal != null) {
navigation.distanceToSignal -= toSubstract; navigation.distanceToSignal -= toSubstract;
recalculate = navigation.distanceToSignal % 100 > 20; signalRefresh &= navigation.distanceToSignal % 50 < 5;
} }
if (recalculate && (signalMode ? navigation.distanceToSignal : navigation.distanceToDestination) % 100 <= 20 fullRefresh &= navigation.distanceToDestination % 100 <= 20;
|| imminentRecalculate && navigation.distanceToDestination <= 5) { partialRefresh &= navigation.distanceToDestination % 50 <= 5;
if (signalMode) {
navigation.waitingForSignal = null; if (blockingSignal != null && navigation.ticksWaitingForSignal % 100 == 50) {
return; SignalBoundary signal = graph.getPoint(EdgePointType.SIGNAL, blockingSignal.getFirst());
} fullRefresh |= signal != null && signal.types.get(blockingSignal.getSecond()) == SignalType.CROSS_SIGNAL;
GlobalStation destination = navigation.destination;
if (!navigatingManually) {
GlobalStation preferredDestination = runtime.findNextStation();
if (preferredDestination != null)
destination = preferredDestination;
}
navigation.startNavigation(destination, navigatingManually ? -1 : Double.MAX_VALUE, false);
} }
if (signalRefresh)
navigation.waitingForSignal = null;
if (!fullRefresh && !partialRefresh)
return;
if (!reservedSignalBlocks.isEmpty())
return;
GlobalStation destination = navigation.destination;
if (!navigatingManually && fullRefresh) {
GlobalStation preferredDestination = runtime.findNextStation();
if (preferredDestination != null)
destination = preferredDestination;
}
navigation.startNavigation(destination, navigatingManually ? -1 : Double.MAX_VALUE, false);
} }
private void tickDerailedSlowdown() { private void tickDerailedSlowdown() {
@ -439,7 +497,7 @@ public class Train {
if (currentStation != null) if (currentStation != null)
currentStation.cancelReservation(this); currentStation.cancelReservation(this);
Create.RAILWAYS.trains.remove(id); Create.RAILWAYS.removeTrain(id);
AllPackets.channel.send(PacketDistributor.ALL.noArg(), new TrainPacket(this, false)); AllPackets.channel.send(PacketDistributor.ALL.noArg(), new TrainPacket(this, false));
return true; return true;
} }
@ -598,6 +656,7 @@ public class Train {
public void arriveAt(GlobalStation station) { public void arriveAt(GlobalStation station) {
setCurrentStation(station); setCurrentStation(station);
reservedSignalBlocks.clear();
runtime.destinationReached(); runtime.destinationReached();
} }
@ -645,6 +704,7 @@ public class Train {
EdgeData signalData = edge.getEdgeData(); EdgeData signalData = edge.getEdgeData();
occupiedSignalBlocks.clear(); occupiedSignalBlocks.clear();
reservedSignalBlocks.clear();
TravellingPoint signalScout = new TravellingPoint(node1, node2, edge, position); TravellingPoint signalScout = new TravellingPoint(node1, node2, edge, position);
Map<UUID, SignalEdgeGroup> allGroups = Create.RAILWAYS.signalEdgeGroups; Map<UUID, SignalEdgeGroup> allGroups = Create.RAILWAYS.signalEdgeGroups;
@ -664,7 +724,7 @@ public class Train {
if (prev != null) { if (prev != null) {
UUID group = prev.getGroup(node2); UUID group = prev.getGroup(node2);
if (Create.RAILWAYS.signalEdgeGroups.containsKey(group)) { if (Create.RAILWAYS.signalEdgeGroups.containsKey(group)) {
occupiedSignalBlocks.add(group); occupy(group, null);
prevGroup.setValue(group); prevGroup.setValue(group);
} }
} }
@ -672,26 +732,32 @@ public class Train {
} else { } else {
UUID group = nextBoundary.getGroup(node1); UUID group = nextBoundary.getGroup(node1);
if (Create.RAILWAYS.signalEdgeGroups.containsKey(group)) { if (Create.RAILWAYS.signalEdgeGroups.containsKey(group)) {
occupiedSignalBlocks.add(group); occupy(group, null);
prevGroup.setValue(group); prevGroup.setValue(group);
} }
} }
} else if (signalData.singleSignalGroup != null && allGroups.containsKey(signalData.singleSignalGroup)) { } else if (signalData.singleSignalGroup != null && allGroups.containsKey(signalData.singleSignalGroup)) {
occupiedSignalBlocks.add(signalData.singleSignalGroup); occupy(signalData.singleSignalGroup, null);
prevGroup.setValue(signalData.singleSignalGroup); prevGroup.setValue(signalData.singleSignalGroup);
} }
forEachTravellingPointBackwards((tp, d) -> { forEachTravellingPointBackwards((tp, d) -> {
signalScout.travel(graph, d, signalScout.follow(tp), (distance, couple) -> couple.getSecond() signalScout.travel(graph, d, signalScout.follow(tp), (distance, couple) -> {
.forEach(id -> { if (!(couple.getFirst()instanceof SignalBoundary signal))
if (!Create.RAILWAYS.signalEdgeGroups.containsKey(id)) return false;
return; couple.getSecond()
if (id.equals(prevGroup.getValue())) .map(signal::getGroup)
return; .forEach(id -> {
occupiedSignalBlocks.add(id); if (!Create.RAILWAYS.signalEdgeGroups.containsKey(id))
prevGroup.setValue(id); return;
}), signalScout.ignoreTurns()); if (id.equals(prevGroup.getValue()))
return;
occupy(id, null);
prevGroup.setValue(id);
});
return false;
}, signalScout.ignoreTurns());
}); });
} }
@ -740,7 +806,14 @@ public class Train {
tag.putBoolean("StillAssembling", heldForAssembly); tag.putBoolean("StillAssembling", heldForAssembly);
tag.putBoolean("Derailed", derailed); tag.putBoolean("Derailed", derailed);
tag.putBoolean("UpdateSignals", updateSignalBlocks); tag.putBoolean("UpdateSignals", updateSignalBlocks);
tag.put("SignalBlocks", NBTHelper.writeCompoundList(occupiedSignalBlocks, uid -> { 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 compoundTag = new CompoundTag();
compoundTag.putUUID("Id", uid); compoundTag.putUUID("Id", uid);
return compoundTag; return compoundTag;
@ -778,9 +851,10 @@ public class Train {
train.derailed = tag.getBoolean("Derailed"); train.derailed = tag.getBoolean("Derailed");
train.updateSignalBlocks = tag.getBoolean("UpdateSignals"); train.updateSignalBlocks = tag.getBoolean("UpdateSignals");
NBTHelper.iterateCompoundList(tag.getList("SignalBlocks", Tag.TAG_COMPOUND), NBTHelper.iterateCompoundList(tag.getList("SignalBlocks", Tag.TAG_COMPOUND), c -> train.occupiedSignalBlocks
c -> train.occupiedSignalBlocks.add(c.getUUID("Id"))); .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("MigratingPoints", Tag.TAG_COMPOUND), NBTHelper.iterateCompoundList(tag.getList("MigratingPoints", Tag.TAG_COMPOUND),
c -> train.migratingPoints.add(TrainMigration.read(c))); c -> train.migratingPoints.add(TrainMigration.read(c)));

View file

@ -22,7 +22,7 @@ import com.simibubi.create.content.logistics.trains.TrackEdge;
import com.simibubi.create.content.logistics.trains.TrackGraph; import com.simibubi.create.content.logistics.trains.TrackGraph;
import com.simibubi.create.content.logistics.trains.TrackGraphHelper; import com.simibubi.create.content.logistics.trains.TrackGraphHelper;
import com.simibubi.create.content.logistics.trains.TrackNode; import com.simibubi.create.content.logistics.trains.TrackNode;
import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.ISignalBoundaryListener; import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.IEdgePointListener;
import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.ITrackSelector; import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.ITrackSelector;
import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.ITurnListener; import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.ITurnListener;
import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.SteerDirection; import com.simibubi.create.content.logistics.trains.entity.TravellingPoint.SteerDirection;
@ -158,7 +158,7 @@ public class TrainRelocator {
return false; return false;
TravellingPoint probe = new TravellingPoint(node1, node2, edge, graphLocation.position); TravellingPoint probe = new TravellingPoint(node1, node2, edge, graphLocation.position);
ISignalBoundaryListener ignoreSignals = probe.ignoreSignals(); IEdgePointListener ignoreSignals = probe.ignoreEdgePoints();
ITurnListener ignoreTurns = probe.ignoreTurns(); ITurnListener ignoreTurns = probe.ignoreTurns();
List<Pair<Couple<TrackNode>, Double>> recordedLocations = new ArrayList<>(); List<Pair<Couple<TrackNode>, Double>> recordedLocations = new ArrayList<>();
List<Vec3> recordedVecs = new ArrayList<>(); List<Vec3> recordedVecs = new ArrayList<>();
@ -206,6 +206,7 @@ public class TrainRelocator {
train.leaveStation(); train.leaveStation();
train.derailed = false; train.derailed = false;
train.heldForAssembly = false;
train.navigation.waitingForSignal = null; train.navigation.waitingForSignal = null;
train.occupiedSignalBlocks.clear(); train.occupiedSignalBlocks.clear();
train.graph = graph; train.graph = graph;
@ -322,13 +323,12 @@ public class TrainRelocator {
@OnlyIn(Dist.CLIENT) @OnlyIn(Dist.CLIENT)
public static boolean carriageWrenched(Vec3 vec3, CarriageContraptionEntity entity) { public static boolean carriageWrenched(Vec3 vec3, CarriageContraptionEntity entity) {
Train train = getTrainFromEntity(entity); Train train = getTrainFromEntity(entity);
if (train != null && !train.heldForAssembly) { if (train == null)
relocatingOrigin = vec3; return false;
relocatingTrain = train.id; relocatingOrigin = vec3;
relocatingEntityId = entity.getId(); relocatingTrain = train.id;
return true; relocatingEntityId = entity.getId();
} return true;
return false;
} }
@OnlyIn(Dist.CLIENT) @OnlyIn(Dist.CLIENT)

View file

@ -6,10 +6,10 @@ import java.util.List;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.Objects; import java.util.Objects;
import java.util.Set; import java.util.Set;
import java.util.UUID;
import java.util.Vector; import java.util.Vector;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.Consumer; import java.util.function.Consumer;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -21,8 +21,7 @@ import com.simibubi.create.content.logistics.trains.TrackGraph;
import com.simibubi.create.content.logistics.trains.TrackNode; import com.simibubi.create.content.logistics.trains.TrackNode;
import com.simibubi.create.content.logistics.trains.TrackNodeLocation; 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.EdgeData;
import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointType; import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.TrackEdgePoint;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalBoundary;
import com.simibubi.create.foundation.utility.Couple; import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Pair; import com.simibubi.create.foundation.utility.Pair;
@ -53,7 +52,7 @@ public class TravellingPoint {
extends BiFunction<TrackGraph, Pair<Boolean, List<Entry<TrackNode, TrackEdge>>>, Entry<TrackNode, TrackEdge>> { extends BiFunction<TrackGraph, Pair<Boolean, List<Entry<TrackNode, TrackEdge>>>, Entry<TrackNode, TrackEdge>> {
}; };
public static interface ISignalBoundaryListener extends BiConsumer<Double, Pair<SignalBoundary, Couple<UUID>>> { public static interface IEdgePointListener extends BiPredicate<Double, Pair<TrackEdgePoint, Couple<TrackNode>>> {
}; };
public static interface ITurnListener extends BiConsumer<Double, TrackEdge> { public static interface ITurnListener extends BiConsumer<Double, TrackEdge> {
@ -68,9 +67,8 @@ public class TravellingPoint {
this.position = position; this.position = position;
} }
public ISignalBoundaryListener ignoreSignals() { public IEdgePointListener ignoreEdgePoints() {
return (d, c) -> { return (d, c) -> false;
};
} }
public ITurnListener ignoreTurns() { public ITurnListener ignoreTurns() {
@ -178,7 +176,7 @@ public class TravellingPoint {
} }
public double travel(TrackGraph graph, double distance, ITrackSelector trackSelector, public double travel(TrackGraph graph, double distance, ITrackSelector trackSelector,
ISignalBoundaryListener signalListener, ITurnListener turnListener) { IEdgePointListener signalListener, ITurnListener turnListener) {
blocked = false; blocked = false;
double edgeLength = edge.getLength(node1, node2); double edgeLength = edge.getLength(node1, node2);
if (distance == 0) if (distance == 0)
@ -193,7 +191,14 @@ public class TravellingPoint {
boolean forward = distance > 0; boolean forward = distance > 0;
double collectedDistance = forward ? -prevPos : -edgeLength + prevPos; double collectedDistance = forward ? -prevPos : -edgeLength + prevPos;
edgeTraversedFrom(graph, forward, signalListener, turnListener, prevPos, collectedDistance);
Double blockedLocation =
edgeTraversedFrom(graph, forward, signalListener, turnListener, prevPos, collectedDistance);
if (blockedLocation != null) {
position = blockedLocation.doubleValue();
traveled = position - prevPos;
return traveled;
}
if (forward) { if (forward) {
// Moving forward // Moving forward
@ -233,9 +238,17 @@ public class TravellingPoint {
collectedDistance += edgeLength; collectedDistance += edgeLength;
if (edge.isTurn()) if (edge.isTurn())
turnListener.accept(collectedDistance, edge); turnListener.accept(collectedDistance, edge);
edgeTraversedFrom(graph, forward, signalListener, turnListener, 0, collectedDistance);
prevPos = 0;
blockedLocation = edgeTraversedFrom(graph, forward, signalListener, turnListener, 0, collectedDistance);
if (blockedLocation != null) {
traveled -= position;
position = blockedLocation.doubleValue();
traveled += position;
break;
}
prevPos = 0;
edgeLength = edge.getLength(node1, node2); edgeLength = edge.getLength(node1, node2);
} }
@ -261,7 +274,7 @@ public class TravellingPoint {
} }
if (validTargets.isEmpty()) { if (validTargets.isEmpty()) {
traveled -= position; traveled += position;
position = 0; position = 0;
blocked = true; blocked = true;
break; break;
@ -278,7 +291,15 @@ public class TravellingPoint {
edgeLength = edge.getLength(node1, node2); edgeLength = edge.getLength(node1, node2);
position += edgeLength; position += edgeLength;
edgeTraversedFrom(graph, forward, signalListener, turnListener, edgeLength, collectedDistance); blockedLocation =
edgeTraversedFrom(graph, forward, signalListener, turnListener, edgeLength, collectedDistance);
if (blockedLocation != null) {
traveled -= position;
position = blockedLocation.doubleValue();
traveled += position;
break;
}
} }
} }
@ -286,42 +307,30 @@ public class TravellingPoint {
return traveled; return traveled;
} }
private void edgeTraversedFrom(TrackGraph graph, boolean forward, ISignalBoundaryListener signalListener, private Double edgeTraversedFrom(TrackGraph graph, boolean forward, IEdgePointListener edgePointListener,
ITurnListener turnListener, double prevPos, double totalDistance) { ITurnListener turnListener, double prevPos, double totalDistance) {
if (edge.isTurn()) if (edge.isTurn())
turnListener.accept(Math.max(0, totalDistance), edge); turnListener.accept(Math.max(0, totalDistance), edge);
EdgeData signalsOnEdge = edge.getEdgeData();
if (!signalsOnEdge.hasSignalBoundaries())
return;
EdgeData edgeData = edge.getEdgeData();
double from = forward ? prevPos : position; double from = forward ? prevPos : position;
double to = forward ? position : prevPos; double to = forward ? position : prevPos;
SignalBoundary nextBoundary = signalsOnEdge.next(EdgePointType.SIGNAL, node1, node2, edge, from); List<TrackEdgePoint> edgePoints = edgeData.getPoints();
List<SignalBoundary> discoveredBoundaries = null;
while (nextBoundary != null) { double length = edge.getLength(node1, node2);
double d = nextBoundary.getLocationOn(node1, node2, edge); for (int i = 0; i < edgePoints.size(); i++) {
if (d > to) int index = forward ? i : edgePoints.size() - i - 1;
break; TrackEdgePoint nextBoundary = edgePoints.get(index);
if (discoveredBoundaries == null) double locationOn = nextBoundary.getLocationOn(node1, node2, edge);
discoveredBoundaries = new ArrayList<>(); double distance = forward ? locationOn : length - locationOn;
discoveredBoundaries.add(nextBoundary); if (forward ? (locationOn < from || locationOn >= to) : (locationOn <= from || locationOn > to))
nextBoundary = signalsOnEdge.next(EdgePointType.SIGNAL, node1, node2, edge, d); continue;
Couple<TrackNode> nodes = Couple.create(node1, node2);
if (edgePointListener.test(totalDistance + distance, Pair.of(nextBoundary, forward ? nodes : nodes.swap())))
return locationOn;
} }
if (discoveredBoundaries == null) return null;
return;
for (int i = 0; i < discoveredBoundaries.size(); i++) {
int index = forward ? i : discoveredBoundaries.size() - i - 1;
nextBoundary = discoveredBoundaries.get(index);
double d = nextBoundary.getLocationOn(node1, node2, edge);
if (!forward)
d = edge.getLength(node1, node2) - d;
Couple<UUID> nodes = Couple.create(nextBoundary.getGroup(node1), nextBoundary.getGroup(node2));
signalListener.accept(totalDistance + d, Pair.of(nextBoundary, forward ? nodes : nodes.swap()));
}
} }
public void reverse(TrackGraph graph) { public void reverse(TrackGraph graph) {

View file

@ -70,6 +70,14 @@ public class EdgeData {
return null; return null;
} }
@Nullable
public TrackEdgePoint next(TrackNode node1, TrackNode node2, TrackEdge edge, double minPosition) {
for (TrackEdgePoint point : points)
if (point.getLocationOn(node1, node2, edge) > minPosition)
return point;
return null;
}
@Nullable @Nullable
public <T extends TrackEdgePoint> T get(EdgePointType<T> type, TrackNode node1, TrackNode node2, TrackEdge edge, public <T extends TrackEdgePoint> T get(EdgePointType<T> type, TrackNode node1, TrackNode node2, TrackEdge edge,
double exactPosition) { double exactPosition) {

View file

@ -49,10 +49,10 @@ public class EdgePointStorage {
return pointsByType.computeIfAbsent(type, t -> new HashMap<>()); return pointsByType.computeIfAbsent(type, t -> new HashMap<>());
} }
public void tick(TrackGraph graph) { public void tick(TrackGraph graph, boolean preTrains) {
pointsByType.values() pointsByType.values()
.forEach(map -> map.values() .forEach(map -> map.values()
.forEach(p -> p.tick(graph))); .forEach(p -> p.tick(graph, preTrains)));
} }
public void transferAll(TrackGraph target, EdgePointStorage other) { public void transferAll(TrackGraph target, EdgePointStorage other) {

View file

@ -143,7 +143,7 @@ public class TrackTargetingBehaviour<T extends TrackEdgePoint> extends TileEntit
} }
if (!otherPoint.canMerge()) if (!otherPoint.canMerge())
return null; return null;
otherPoint.tileAdded(getPos(), front); otherPoint.tileAdded(tileEntity, front);
id = otherPoint.getId(); id = otherPoint.getId();
tileEntity.setChanged(); tileEntity.setChanged();
return (T) otherPoint; return (T) otherPoint;
@ -158,7 +158,7 @@ public class TrackTargetingBehaviour<T extends TrackEdgePoint> extends TileEntit
point.setId(id); point.setId(id);
point.setLocation(reverseEdge ? loc.edge : loc.edge.swap(), reverseEdge ? loc.position : length - loc.position); point.setLocation(reverseEdge ? loc.edge : loc.edge.swap(), reverseEdge ? loc.position : length - loc.position);
point.tileAdded(getPos(), front); point.tileAdded(tileEntity, front);
loc.graph.addPoint(edgePointType, point); loc.graph.addPoint(edgePointType, point);
return point; return point;
} }

View file

@ -3,21 +3,41 @@ package com.simibubi.create.content.logistics.trains.management.edgePoint.signal
import java.util.Random; import java.util.Random;
import com.simibubi.create.AllTileEntities; import com.simibubi.create.AllTileEntities;
import com.simibubi.create.content.contraptions.wrench.IWrenchable;
import com.simibubi.create.foundation.block.ITE; import com.simibubi.create.foundation.block.ITE;
import com.simibubi.create.foundation.utility.Lang;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.StringRepresentable;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.SimpleWaterloggedBlock;
import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition.Builder;
import net.minecraft.world.level.block.state.properties.EnumProperty;
public class SignalBlock extends Block implements SimpleWaterloggedBlock, ITE<SignalTileEntity> { public class SignalBlock extends Block implements ITE<SignalTileEntity>, IWrenchable {
public static final EnumProperty<SignalType> TYPE = EnumProperty.create("type", SignalType.class);
public enum SignalType implements StringRepresentable {
ENTRY_SIGNAL, CROSS_SIGNAL;
@Override
public String getSerializedName() {
return Lang.asId(name());
}
}
public SignalBlock(Properties p_53182_) { public SignalBlock(Properties p_53182_) {
super(p_53182_); super(p_53182_);
registerDefaultState(defaultBlockState().setValue(TYPE, SignalType.ENTRY_SIGNAL));
} }
@Override @Override
@ -25,11 +45,36 @@ public class SignalBlock extends Block implements SimpleWaterloggedBlock, ITE<Si
return SignalTileEntity.class; return SignalTileEntity.class;
} }
@Override
protected void createBlockStateDefinition(Builder<Block, BlockState> pBuilder) {
super.createBlockStateDefinition(pBuilder.add(TYPE));
}
@Override @Override
public BlockEntityType<? extends SignalTileEntity> getTileEntityType() { public BlockEntityType<? extends SignalTileEntity> getTileEntityType() {
return AllTileEntities.TRACK_SIGNAL.get(); return AllTileEntities.TRACK_SIGNAL.get();
} }
@Override
public InteractionResult onWrenched(BlockState state, UseOnContext context) {
Level level = context.getLevel();
BlockPos pos = context.getClickedPos();
if (level.isClientSide)
return InteractionResult.SUCCESS;
withTileEntityDo(level, pos, ste -> {
SignalBoundary signal = ste.getSignal();
Player player = context.getPlayer();
if (signal != null) {
signal.cycleSignalType(pos);
if (player != null)
player.displayClientMessage(Lang.translate("track_signal.mode_change." + signal.getTypeFor(pos)
.getSerializedName()), true);
} else if (player != null)
player.displayClientMessage(Lang.translate("track_signal.cannot_change_mode"), true);
});
return InteractionResult.SUCCESS;
}
@Override @Override
public boolean canConnectRedstone(BlockState state, BlockGetter world, BlockPos pos, Direction side) { public boolean canConnectRedstone(BlockState state, BlockGetter world, BlockPos pos, Direction side) {
return side != null; return side != null;

View file

@ -2,6 +2,7 @@ package com.simibubi.create.content.logistics.trains.management.edgePoint.signal
import java.util.HashSet; import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
@ -9,6 +10,8 @@ import com.google.common.base.Objects;
import com.simibubi.create.Create; import com.simibubi.create.Create;
import com.simibubi.create.content.logistics.trains.TrackGraph; import com.simibubi.create.content.logistics.trains.TrackGraph;
import com.simibubi.create.content.logistics.trains.TrackNode; import com.simibubi.create.content.logistics.trains.TrackNode;
import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointType;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalBlock.SignalType;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalTileEntity.OverlayState; import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalTileEntity.OverlayState;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalTileEntity.SignalState; import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalTileEntity.SignalState;
import com.simibubi.create.foundation.utility.Couple; import com.simibubi.create.foundation.utility.Couple;
@ -21,23 +24,32 @@ import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.Tag; import net.minecraft.nbt.Tag;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.entity.BlockEntity;
public class SignalBoundary extends TrackEdgePoint { public class SignalBoundary extends TrackEdgePoint {
public Couple<Set<BlockPos>> signals; public Couple<Set<BlockPos>> blockEntities;
public Couple<SignalType> types;
public Couple<UUID> groups; public Couple<UUID> groups;
public Couple<Boolean> sidesToUpdate; public Couple<Boolean> sidesToUpdate;
public Couple<SignalState> cachedStates;
private Couple<Map<UUID, Boolean>> chainedSignals;
public SignalBoundary() { public SignalBoundary() {
signals = Couple.create(HashSet::new); blockEntities = Couple.create(HashSet::new);
chainedSignals = Couple.create(null, null);
groups = Couple.create(null, null); groups = Couple.create(null, null);
sidesToUpdate = Couple.create(true, true); sidesToUpdate = Couple.create(true, true);
types = Couple.create(() -> SignalType.ENTRY_SIGNAL);
cachedStates = Couple.create(() -> SignalState.GREEN);
} }
public void setGroup(TrackNode side, UUID groupId) { public void setGroup(TrackNode side, UUID groupId) {
boolean primary = isPrimary(side); boolean primary = isPrimary(side);
groups.set(primary, groupId); groups.set(primary, groupId);
sidesToUpdate.set(primary, false); sidesToUpdate.set(primary, false);
chainedSignals.set(primary, null);
} }
@Override @Override
@ -47,19 +59,23 @@ public class SignalBoundary extends TrackEdgePoint {
@Override @Override
public void invalidate(LevelAccessor level) { public void invalidate(LevelAccessor level) {
signals.forEach(s -> s.forEach(pos -> invalidateAt(level, pos))); blockEntities.forEach(s -> s.forEach(pos -> invalidateAt(level, pos)));
} }
@Override @Override
public void tileAdded(BlockPos tilePos, boolean front) { public void tileAdded(BlockEntity tile, boolean front) {
signals.get(front) Set<BlockPos> tilesOnSide = blockEntities.get(front);
.add(tilePos); if (tilesOnSide.isEmpty())
tile.getBlockState()
.getOptionalValue(SignalBlock.TYPE)
.ifPresent(type -> types.set(front, type));
tilesOnSide.add(tile.getBlockPos());
} }
@Override @Override
public void tileRemoved(BlockPos tilePos, boolean front) { public void tileRemoved(BlockPos tilePos, boolean front) {
signals.forEach(s -> s.remove(tilePos)); blockEntities.forEach(s -> s.remove(tilePos));
if (signals.both(Set::isEmpty)) if (blockEntities.both(Set::isEmpty))
removeFromAllGraphs(); removeFromAllGraphs();
} }
@ -79,16 +95,16 @@ public class SignalBoundary extends TrackEdgePoint {
@Override @Override
public boolean canNavigateVia(TrackNode side) { public boolean canNavigateVia(TrackNode side) {
return !signals.get(isPrimary(side)) return !blockEntities.get(isPrimary(side))
.isEmpty(); .isEmpty();
} }
public OverlayState getOverlayFor(BlockPos tile) { public OverlayState getOverlayFor(BlockPos tile) {
for (boolean first : Iterate.trueAndFalse) { for (boolean first : Iterate.trueAndFalse) {
Set<BlockPos> set = signals.get(first); Set<BlockPos> set = blockEntities.get(first);
for (BlockPos blockPos : set) { for (BlockPos blockPos : set) {
if (blockPos.equals(tile)) if (blockPos.equals(tile))
return signals.get(!first) return blockEntities.get(!first)
.isEmpty() ? OverlayState.RENDER : OverlayState.DUAL; .isEmpty() ? OverlayState.RENDER : OverlayState.DUAL;
return OverlayState.SKIP; return OverlayState.SKIP;
} }
@ -96,58 +112,123 @@ public class SignalBoundary extends TrackEdgePoint {
return OverlayState.SKIP; return OverlayState.SKIP;
} }
public SignalType getTypeFor(BlockPos tile) {
return types.get(blockEntities.getFirst()
.contains(tile));
}
public SignalState getStateFor(BlockPos tile) { public SignalState getStateFor(BlockPos tile) {
for (boolean first : Iterate.trueAndFalse) { for (boolean first : Iterate.trueAndFalse) {
Set<BlockPos> set = signals.get(first); Set<BlockPos> set = blockEntities.get(first);
if (!set.contains(tile)) if (set.contains(tile))
continue; return cachedStates.get(first);
UUID group = groups.get(first);
if (Objects.equal(group, groups.get(!first)))
return SignalState.INVALID;
Map<UUID, SignalEdgeGroup> signalEdgeGroups = Create.RAILWAYS.signalEdgeGroups;
SignalEdgeGroup signalEdgeGroup = signalEdgeGroups.get(group);
if (signalEdgeGroup == null)
return SignalState.INVALID;
return signalEdgeGroup.isOccupiedUnless(this) ? SignalState.RED : SignalState.GREEN;
} }
return SignalState.INVALID; return SignalState.INVALID;
} }
@Override @Override
public void tick(TrackGraph graph) { public void tick(TrackGraph graph, boolean preTrains) {
super.tick(graph); super.tick(graph, preTrains);
if (!preTrains) {
tickState(graph);
return;
}
for (boolean front : Iterate.trueAndFalse) { for (boolean front : Iterate.trueAndFalse) {
if (!sidesToUpdate.get(front)) if (!sidesToUpdate.get(front))
continue; continue;
sidesToUpdate.set(front, false); sidesToUpdate.set(front, false);
SignalPropagator.propagateSignalGroup(graph, this, front); SignalPropagator.propagateSignalGroup(graph, this, front);
chainedSignals.set(front, null);
} }
} }
private void tickState(TrackGraph graph) {
for (boolean current : Iterate.trueAndFalse) {
Set<BlockPos> set = blockEntities.get(current);
if (set.isEmpty())
continue;
UUID group = groups.get(current);
if (Objects.equal(group, groups.get(!current))) {
cachedStates.set(current, SignalState.INVALID);
continue;
}
Map<UUID, SignalEdgeGroup> signalEdgeGroups = Create.RAILWAYS.signalEdgeGroups;
SignalEdgeGroup signalEdgeGroup = signalEdgeGroups.get(group);
if (signalEdgeGroup == null) {
cachedStates.set(current, SignalState.INVALID);
continue;
}
boolean occupiedUnlessBySelf = signalEdgeGroup.isOccupiedUnless(this);
cachedStates.set(current, occupiedUnlessBySelf ? SignalState.RED : resolveSignalChain(graph, current));
}
}
private SignalState resolveSignalChain(TrackGraph graph, boolean side) {
if (types.get(side) != SignalType.CROSS_SIGNAL)
return SignalState.GREEN;
if (chainedSignals.get(side) == null)
chainedSignals.set(side, SignalPropagator.collectChainedSignals(graph, this, side));
boolean allPathsFree = true;
boolean noPathsFree = true;
boolean invalid = false;
for (Entry<UUID, Boolean> entry : chainedSignals.get(side)
.entrySet()) {
UUID uuid = entry.getKey();
boolean sideOfOther = entry.getValue();
SignalBoundary otherSignal = graph.getPoint(EdgePointType.SIGNAL, uuid);
if (otherSignal == null) {
invalid = true;
break;
}
SignalState otherState = otherSignal.cachedStates.get(sideOfOther);
allPathsFree &= otherState == SignalState.GREEN || otherState == SignalState.INVALID;
noPathsFree &= otherState == SignalState.RED;
}
if (invalid) {
chainedSignals.set(side, null);
return SignalState.INVALID;
}
if (allPathsFree)
return SignalState.GREEN;
if (noPathsFree)
return SignalState.RED;
return SignalState.YELLOW;
}
@Override @Override
public void read(CompoundTag nbt, boolean migration) { public void read(CompoundTag nbt, boolean migration) {
super.read(nbt, migration); super.read(nbt, migration);
if (migration) if (migration)
return; return;
sidesToUpdate = Couple.create(true, true); blockEntities = Couple.create(HashSet::new);
signals = Couple.create(HashSet::new);
groups = Couple.create(null, null); groups = Couple.create(null, null);
for (int i = 1; i <= 2; i++) for (int i = 1; i <= 2; i++)
if (nbt.contains("Tiles" + i)) { if (nbt.contains("Tiles" + i)) {
boolean first = i == 1; boolean first = i == 1;
NBTHelper.iterateCompoundList(nbt.getList("Tiles" + i, Tag.TAG_COMPOUND), c -> signals.get(first) NBTHelper.iterateCompoundList(nbt.getList("Tiles" + i, Tag.TAG_COMPOUND), c -> blockEntities.get(first)
.add(NbtUtils.readBlockPos(c))); .add(NbtUtils.readBlockPos(c)));
} }
for (int i = 1; i <= 2; i++) for (int i = 1; i <= 2; i++)
if (nbt.contains("Group" + i)) if (nbt.contains("Group" + i))
groups.set(i == 1, nbt.getUUID("Group" + i)); groups.set(i == 1, nbt.getUUID("Group" + i));
for (int i = 1; i <= 2; i++) for (int i = 1; i <= 2; i++)
sidesToUpdate.set(i == 1, nbt.contains("Update" + i)); sidesToUpdate.set(i == 1, nbt.contains("Update" + i));
for (int i = 1; i <= 2; i++)
types.set(i == 1, NBTHelper.readEnum(nbt, "Type" + i, SignalType.class));
for (int i = 1; i <= 2; i++)
cachedStates.set(i == 1, NBTHelper.readEnum(nbt, "State" + i, SignalState.class));
} }
@Override @Override
public void read(FriendlyByteBuf buffer) { public void read(FriendlyByteBuf buffer) {
super.read(buffer); super.read(buffer);
@ -161,17 +242,21 @@ public class SignalBoundary extends TrackEdgePoint {
public void write(CompoundTag nbt) { public void write(CompoundTag nbt) {
super.write(nbt); super.write(nbt);
for (int i = 1; i <= 2; i++) for (int i = 1; i <= 2; i++)
if (!signals.get(i == 1) if (!blockEntities.get(i == 1)
.isEmpty()) .isEmpty())
nbt.put("Tiles" + i, NBTHelper.writeCompoundList(signals.get(i == 1), NbtUtils::writeBlockPos)); nbt.put("Tiles" + i, NBTHelper.writeCompoundList(blockEntities.get(i == 1), NbtUtils::writeBlockPos));
for (int i = 1; i <= 2; i++) for (int i = 1; i <= 2; i++)
if (groups.get(i == 1) != null) if (groups.get(i == 1) != null)
nbt.putUUID("Group" + i, groups.get(i == 1)); nbt.putUUID("Group" + i, groups.get(i == 1));
for (int i = 1; i <= 2; i++) for (int i = 1; i <= 2; i++)
if (sidesToUpdate.get(i == 1)) if (sidesToUpdate.get(i == 1))
nbt.putBoolean("Update" + i, true); nbt.putBoolean("Update" + i, true);
for (int i = 1; i <= 2; i++)
NBTHelper.writeEnum(nbt, "Type" + i, types.get(i == 1));
for (int i = 1; i <= 2; i++)
NBTHelper.writeEnum(nbt, "State" + i, cachedStates.get(i == 1));
} }
@Override @Override
public void write(FriendlyByteBuf buffer) { public void write(FriendlyByteBuf buffer) {
super.write(buffer); super.write(buffer);
@ -183,4 +268,9 @@ public class SignalBoundary extends TrackEdgePoint {
} }
} }
public void cycleSignalType(BlockPos pos) {
types.set(blockEntities.getFirst()
.contains(pos), SignalType.values()[(getTypeFor(pos).ordinal() + 1) % SignalType.values().length]);
}
} }

View file

@ -1,6 +1,7 @@
package com.simibubi.create.content.logistics.trains.management.edgePoint.signal; package com.simibubi.create.content.logistics.trains.management.edgePoint.signal;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -23,6 +24,8 @@ import com.simibubi.create.foundation.utility.Couple;
import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.Pair; import com.simibubi.create.foundation.utility.Pair;
import net.minecraft.world.phys.Vec3;
public class SignalPropagator { public class SignalPropagator {
public static void onSignalRemoved(TrackGraph graph, SignalBoundary signal) { public static void onSignalRemoved(TrackGraph graph, SignalBoundary signal) {
@ -38,7 +41,7 @@ public class SignalPropagator {
SignalBoundary boundary = pair.getSecond(); SignalBoundary boundary = pair.getSecond();
boundary.queueUpdate(node1); boundary.queueUpdate(node1);
return false; return false;
}, Predicates.alwaysFalse()); }, Predicates.alwaysFalse(), false);
} }
} }
@ -50,7 +53,7 @@ public class SignalPropagator {
SignalBoundary boundary = pair.getSecond(); SignalBoundary boundary = pair.getSecond();
boundary.queueUpdate(node1); boundary.queueUpdate(node1);
return false; return false;
}, Predicates.alwaysFalse()); }, Predicates.alwaysFalse(), false);
} }
public static void propagateSignalGroup(TrackGraph graph, SignalBoundary signal, boolean front) { public static void propagateSignalGroup(TrackGraph graph, SignalBoundary signal, boolean front) {
@ -82,11 +85,22 @@ public class SignalPropagator {
signalData.singleSignalGroup = groupId; signalData.singleSignalGroup = groupId;
return true; return true;
}); }, false);
}
public static Map<UUID, Boolean> collectChainedSignals(TrackGraph graph, SignalBoundary signal, boolean front) {
HashMap<UUID, Boolean> map = new HashMap<>();
walkSignals(graph, signal, front, pair -> {
SignalBoundary boundary = pair.getSecond();
map.put(boundary.id, !boundary.isPrimary(pair.getFirst()));
return false;
}, Predicates.alwaysFalse(), true);
return map;
} }
public static void walkSignals(TrackGraph graph, SignalBoundary signal, boolean front, public static void walkSignals(TrackGraph graph, SignalBoundary signal, boolean front,
Predicate<Pair<TrackNode, SignalBoundary>> boundaryCallback, Predicate<EdgeData> nonBoundaryCallback) { Predicate<Pair<TrackNode, SignalBoundary>> boundaryCallback, Predicate<EdgeData> nonBoundaryCallback,
boolean forCollection) {
Couple<TrackNodeLocation> edgeLocation = signal.edgeLocation; Couple<TrackNodeLocation> edgeLocation = signal.edgeLocation;
Couple<TrackNode> startNodes = edgeLocation.map(graph::locateNode); Couple<TrackNode> startNodes = edgeLocation.map(graph::locateNode);
@ -101,7 +115,8 @@ public class SignalPropagator {
if (startEdge == null) if (startEdge == null)
return; return;
Create.RAILWAYS.sync.edgeDataChanged(graph, node1, node2, startEdge, oppositeEdge); if (!forCollection)
Create.RAILWAYS.sync.edgeDataChanged(graph, node1, node2, startEdge, oppositeEdge);
// Check for signal on the same edge // Check for signal on the same edge
SignalBoundary immediateBoundary = startEdge.getEdgeData() SignalBoundary immediateBoundary = startEdge.getEdgeData()
@ -115,11 +130,12 @@ public class SignalPropagator {
// Search for any connected signals // Search for any connected signals
List<Couple<TrackNode>> frontier = new ArrayList<>(); List<Couple<TrackNode>> frontier = new ArrayList<>();
frontier.add(Couple.create(node2, node1)); frontier.add(Couple.create(node2, node1));
walkSignals(graph, frontier, boundaryCallback, nonBoundaryCallback); walkSignals(graph, frontier, boundaryCallback, nonBoundaryCallback, forCollection);
} }
private static void walkSignals(TrackGraph graph, List<Couple<TrackNode>> frontier, private static void walkSignals(TrackGraph graph, List<Couple<TrackNode>> frontier,
Predicate<Pair<TrackNode, SignalBoundary>> boundaryCallback, Predicate<EdgeData> nonBoundaryCallback) { Predicate<Pair<TrackNode, SignalBoundary>> boundaryCallback, Predicate<EdgeData> nonBoundaryCallback,
boolean forCollection) {
Set<TrackEdge> visited = new HashSet<>(); Set<TrackEdge> visited = new HashSet<>();
while (!frontier.isEmpty()) { while (!frontier.isEmpty()) {
Couple<TrackNode> couple = frontier.remove(0); Couple<TrackNode> couple = frontier.remove(0);
@ -138,6 +154,16 @@ public class SignalPropagator {
if (!visited.add(edge)) if (!visited.add(edge))
continue; continue;
// chain signal: check if reachable
if (forCollection) {
Vec3 currentDirection = graph.getConnectionsFrom(prevNode)
.get(currentNode)
.getDirection(prevNode, currentNode, false);
Vec3 newDirection = edge.getDirection(currentNode, nextNode, true);
if (currentDirection.dot(newDirection) < 3 / 4f)
continue;
}
TrackEdge oppositeEdge = graph.getConnectionsFrom(nextNode) TrackEdge oppositeEdge = graph.getConnectionsFrom(nextNode)
.get(currentNode); .get(currentNode);
visited.add(oppositeEdge); visited.add(oppositeEdge);

View file

@ -6,6 +6,7 @@ import javax.annotation.Nullable;
import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointType; import com.simibubi.create.content.logistics.trains.management.edgePoint.EdgePointType;
import com.simibubi.create.content.logistics.trains.management.edgePoint.TrackTargetingBehaviour; import com.simibubi.create.content.logistics.trains.management.edgePoint.TrackTargetingBehaviour;
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalBlock.SignalType;
import com.simibubi.create.foundation.tileEntity.SmartTileEntity; import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour; import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
import com.simibubi.create.foundation.utility.NBTHelper; import com.simibubi.create.foundation.utility.NBTHelper;
@ -25,19 +26,23 @@ public class SignalTileEntity extends SmartTileEntity {
} }
public static enum SignalState { public static enum SignalState {
RED, GREEN, INVALID, TRAIN_ENTERING; RED, YELLOW, GREEN, INVALID;
public boolean isRedLight(float renderTime) { public boolean isRedLight(float renderTime) {
return this == RED || this == INVALID && renderTime % 40 < 3; return this == RED || this == INVALID && renderTime % 40 < 3;
} }
public boolean isGreenLight(float renderTime) { public boolean isYellowLight(float renderTime) {
return this == GREEN || this == TRAIN_ENTERING; return this == YELLOW;
}
public boolean isGreenLight(float renderTime) {
return this == GREEN;
} }
} }
public TrackTargetingBehaviour<SignalBoundary> edgePoint; public TrackTargetingBehaviour<SignalBoundary> edgePoint;
private SignalState state; private SignalState state;
private OverlayState overlay; private OverlayState overlay;
private int switchToRedAfterTrainEntered; private int switchToRedAfterTrainEntered;
@ -67,7 +72,7 @@ public class SignalTileEntity extends SmartTileEntity {
public SignalBoundary getSignal() { public SignalBoundary getSignal() {
return edgePoint.getEdgePoint(); return edgePoint.getEdgePoint();
} }
public boolean isPowered() { public boolean isPowered() {
return state == SignalState.RED; return state == SignalState.RED;
} }
@ -94,12 +99,23 @@ public class SignalTileEntity extends SmartTileEntity {
super.tick(); super.tick();
if (level.isClientSide) if (level.isClientSide)
return; return;
SignalBoundary boundary = getSignal(); SignalBoundary boundary = getSignal();
if (boundary == null) { if (boundary == null) {
enterState(SignalState.INVALID); enterState(SignalState.INVALID);
setOverlay(OverlayState.RENDER); setOverlay(OverlayState.RENDER);
return; return;
} }
getBlockState().getOptionalValue(SignalBlock.TYPE)
.ifPresent(stateType -> {
SignalType targetType = boundary.getTypeFor(worldPosition);
if (stateType != targetType) {
level.setBlock(worldPosition, getBlockState().setValue(SignalBlock.TYPE, targetType), 3);
refreshBlockState();
}
});
enterState(boundary.getStateFor(worldPosition)); enterState(boundary.getStateFor(worldPosition));
setOverlay(boundary.getOverlayFor(worldPosition)); setOverlay(boundary.getOverlayFor(worldPosition));
} }
@ -127,7 +143,7 @@ public class SignalTileEntity extends SmartTileEntity {
if (state == SignalState.RED && switchToRedAfterTrainEntered > 0) if (state == SignalState.RED && switchToRedAfterTrainEntered > 0)
return; return;
this.state = state; this.state = state;
switchToRedAfterTrainEntered = state == SignalState.GREEN ? 15 : 0; switchToRedAfterTrainEntered = state == SignalState.GREEN || state == SignalState.YELLOW ? 15 : 0;
notifyUpdate(); notifyUpdate();
scheduleBlockTick(); scheduleBlockTick();
} }

View file

@ -4,6 +4,7 @@ import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtUtils; import net.minecraft.nbt.NbtUtils;
import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.entity.BlockEntity;
public abstract class SingleTileEdgePoint extends TrackEdgePoint { public abstract class SingleTileEdgePoint extends TrackEdgePoint {
@ -14,8 +15,8 @@ public abstract class SingleTileEdgePoint extends TrackEdgePoint {
} }
@Override @Override
public void tileAdded(BlockPos tilePos, boolean front) { public void tileAdded(BlockEntity tile, boolean front) {
this.tilePos = tilePos; this.tilePos = tile.getBlockPos();
} }
@Override @Override

View file

@ -18,6 +18,7 @@ import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.Tag; import net.minecraft.nbt.Tag;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.entity.BlockEntity;
public abstract class TrackEdgePoint { public abstract class TrackEdgePoint {
@ -59,7 +60,7 @@ public abstract class TrackEdgePoint {
behaviour.invalidateEdgePoint(migrationData); behaviour.invalidateEdgePoint(migrationData);
} }
public abstract void tileAdded(BlockPos tilePos, boolean front); public abstract void tileAdded(BlockEntity tile, boolean front);
public abstract void tileRemoved(BlockPos tilePos, boolean front); public abstract void tileRemoved(BlockPos tilePos, boolean front);
@ -112,7 +113,7 @@ public abstract class TrackEdgePoint {
buffer.writeDouble(position); buffer.writeDouble(position);
} }
public void tick(TrackGraph graph) {} public void tick(TrackGraph graph, boolean preTrains) {}
protected void removeFromAllGraphs() { protected void removeFromAllGraphs() {
for (TrackGraph trackGraph : Create.RAILWAYS.trackNetworks.values()) for (TrackGraph trackGraph : Create.RAILWAYS.trackNetworks.values())

View file

@ -496,7 +496,7 @@ public class StationTileEntity extends SmartTileEntity {
} }
train.collectInitiallyOccupiedSignalBlocks(); train.collectInitiallyOccupiedSignalBlocks();
Create.RAILWAYS.trains.put(train.id, train); Create.RAILWAYS.addTrain(train);
AllPackets.channel.send(PacketDistributor.ALL.noArg(), new TrainPacket(train, true)); AllPackets.channel.send(PacketDistributor.ALL.noArg(), new TrainPacket(train, true));
clearException(); clearException();
} }

View file

@ -648,6 +648,10 @@
"create.train.relocate.invalid": "Cannot relocate Train to here", "create.train.relocate.invalid": "Cannot relocate Train to here",
"create.train.relocate.too_far": "Cannot relocate Train this far away", "create.train.relocate.too_far": "Cannot relocate Train this far away",
"create.track_signal.cannot_change_mode": "Unable to switch mode of this Signal",
"create.track_signal.mode_change.entry_signal": "-> Allow passage if section unoccupied",
"create.track_signal.mode_change.cross_signal": "-> Allow passage if section fully traversable",
"create.contraption.controls.start_controlling": "Now controlling: %1$s", "create.contraption.controls.start_controlling": "Now controlling: %1$s",
"create.contraption.controls.stop_controlling": "Stopped controlling contraption", "create.contraption.controls.stop_controlling": "Stopped controlling contraption",
"create.contraption.controls.approach_station": "Hold %1$s to approach %2$s", "create.contraption.controls.approach_station": "Hold %1$s to approach %2$s",

View file

@ -0,0 +1,85 @@
{
"credit": "Made with Blockbench",
"parent": "block/block",
"textures": {
"0": "create:block/chain_signal_box",
"1": "create:block/chain_signal_box_top",
"particle": "create:block/chain_signal_box"
},
"elements": [
{
"from": [0, 14, 0],
"to": [16, 16, 16],
"faces": {
"north": {"uv": [0, 0, 16, 2], "texture": "#0"},
"east": {"uv": [0, 0, 16, 2], "texture": "#0"},
"south": {"uv": [0, 0, 16, 2], "texture": "#0"},
"west": {"uv": [0, 0, 16, 2], "texture": "#0"},
"up": {"uv": [0, 0, 16, 16], "texture": "#1"},
"down": {"uv": [0, 0, 16, 16], "texture": "#1"}
}
},
{
"from": [0, 0, 0],
"to": [16, 2, 16],
"faces": {
"north": {"uv": [0, 14, 16, 16], "texture": "#0"},
"east": {"uv": [0, 14, 16, 16], "texture": "#0"},
"south": {"uv": [0, 14, 16, 16], "texture": "#0"},
"west": {"uv": [0, 14, 16, 16], "texture": "#0"},
"up": {"uv": [0, 0, 16, 16], "texture": "#1"},
"down": {"uv": [0, 0, 16, 16], "texture": "#1"}
}
},
{
"from": [1, 2, 1],
"to": [15, 14, 15],
"faces": {
"north": {"uv": [1, 2, 15, 14], "texture": "#0"},
"east": {"uv": [1, 2, 15, 14], "texture": "#0"},
"south": {"uv": [1, 2, 15, 14], "texture": "#0"},
"west": {"uv": [1, 2, 15, 14], "texture": "#0"}
}
},
{
"from": [13, 2, 13],
"to": [16, 14, 16],
"faces": {
"north": {"uv": [13, 2, 16, 14], "texture": "#0"},
"east": {"uv": [0, 2, 3, 14], "texture": "#0"},
"south": {"uv": [13, 2, 16, 14], "texture": "#0"},
"west": {"uv": [0, 2, 3, 14], "texture": "#0"}
}
},
{
"from": [0, 2, 0],
"to": [3, 14, 3],
"faces": {
"north": {"uv": [13, 2, 16, 14], "texture": "#0"},
"east": {"uv": [0, 2, 3, 14], "texture": "#0"},
"south": {"uv": [13, 2, 16, 14], "texture": "#0"},
"west": {"uv": [0, 2, 3, 14], "texture": "#0"}
}
},
{
"from": [13, 2, 0],
"to": [16, 14, 3],
"faces": {
"north": {"uv": [0, 2, 3, 14], "texture": "#0"},
"east": {"uv": [13, 2, 16, 14], "texture": "#0"},
"south": {"uv": [0, 2, 3, 14], "texture": "#0"},
"west": {"uv": [13, 2, 16, 14], "texture": "#0"}
}
},
{
"from": [0, 2, 13],
"to": [3, 14, 16],
"faces": {
"north": {"uv": [0, 2, 3, 14], "texture": "#0"},
"east": {"uv": [13, 2, 16, 14], "texture": "#0"},
"south": {"uv": [0, 2, 3, 14], "texture": "#0"},
"west": {"uv": [13, 2, 16, 14], "texture": "#0"}
}
}
]
}

View file

@ -0,0 +1,21 @@
{
"credit": "Made with Blockbench",
"textures": {
"0": "create:block/signal_glow_2",
"particle": "create:block/signal_glow_2"
},
"elements": [
{
"from": [-0.5, -0.5, -0.5],
"to": [0.5, 0.5, 0.5],
"faces": {
"north": {"uv": [1, 1, 2, 2], "texture": "#0"},
"east": {"uv": [1, 1, 2, 2], "texture": "#0"},
"south": {"uv": [1, 1, 2, 2], "texture": "#0"},
"west": {"uv": [1, 1, 2, 2], "texture": "#0"},
"up": {"uv": [1, 1, 2, 2], "texture": "#0"},
"down": {"uv": [1, 1, 2, 2], "texture": "#0"}
}
}
]
}

View file

@ -0,0 +1,21 @@
{
"credit": "Made with Blockbench",
"textures": {
"0": "create:block/signal_glow_2",
"particle": "create:block/signal_glow_2"
},
"elements": [
{
"from": [-0.5, -0.5, -0.5],
"to": [0.5, 0.5, 0.5],
"faces": {
"north": {"uv": [1, 0, 2, 1], "texture": "#0"},
"east": {"uv": [1, 0, 2, 1], "texture": "#0"},
"south": {"uv": [1, 0, 2, 1], "texture": "#0"},
"west": {"uv": [1, 0, 2, 1], "texture": "#0"},
"up": {"uv": [1, 0, 2, 1], "texture": "#0"},
"down": {"uv": [1, 0, 2, 1], "texture": "#0"}
}
}
]
}

View file

@ -0,0 +1,39 @@
{
"credit": "Made with Blockbench",
"parent": "block/block",
"ambientocclusion": false,
"textures": {
"2": "create:block/signal_glow_2",
"particle": "create:block/signal_glow_2"
},
"elements": [
{
"name": "tube3",
"from": [-3, -4.5, -3],
"to": [3, 4.5, 3],
"rotation": {"angle": 0, "axis": "z", "origin": [8, 8, 8]},
"faces": {
"north": {"uv": [10, 7, 16, 16], "texture": "#2"},
"east": {"uv": [10, 7, 16, 16], "texture": "#2"},
"south": {"uv": [10, 7, 16, 16], "texture": "#2"},
"west": {"uv": [10, 7, 16, 16], "texture": "#2"},
"up": {"uv": [10, 0, 16, 6], "rotation": 90, "texture": "#2"},
"down": {"uv": [10, 0, 16, 6], "rotation": 90, "texture": "#2"}
}
}
],
"groups": [
{
"name": "group",
"origin": [17, 14, 13],
"color": 0,
"children": []
},
{
"name": "group",
"origin": [17, 14, 13],
"color": 0,
"children": [0]
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 303 B

After

Width:  |  Height:  |  Size: 323 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 469 B