From 7e167f3b29d69ab56f8d5c971c8ca7853f8975ac Mon Sep 17 00:00:00 2001 From: simibubi <31564874+simibubi@users.noreply.github.com> Date: Wed, 5 Aug 2020 22:10:05 +0200 Subject: [PATCH] Couple things, Part I - Added Foundation and POC of minecart couplings and carriage contraptions. highly unstable --- src/generated/resources/.cache/cache | 23 +- .../resources/assets/create/lang/en_ud.json | 1 + .../resources/assets/create/lang/en_us.json | 1 + .../assets/create/lang/unfinished/de_de.json | 3 +- .../assets/create/lang/unfinished/fr_fr.json | 3 +- .../assets/create/lang/unfinished/it_it.json | 3 +- .../assets/create/lang/unfinished/ja_jp.json | 3 +- .../assets/create/lang/unfinished/ko_kr.json | 3 +- .../assets/create/lang/unfinished/nl_nl.json | 3 +- .../assets/create/lang/unfinished/pt_br.json | 3 +- .../assets/create/lang/unfinished/ru_ru.json | 3 +- .../assets/create/lang/unfinished/zh_cn.json | 3 +- .../create/models/item/minecart_coupling.json | 6 + .../com/simibubi/create/AllBlockPartials.java | 15 +- .../java/com/simibubi/create/AllItems.java | 11 +- src/main/java/com/simibubi/create/Create.java | 17 +- .../com/simibubi/create/CreateClient.java | 57 +-- .../content/contraptions/KineticDebugger.java | 9 +- .../structureMovement/Contraption.java | 8 +- .../ContraptionCollider.java | 151 +------ .../structureMovement/ContraptionEntity.java | 48 ++- .../structureMovement/ContraptionHandler.java | 51 +++ .../mounted/CartAssemblerBlock.java | 14 +- .../mounted/MinecartContraptionItem.java | 6 +- .../mounted/MountedContraption.java | 35 +- .../train/ClientMinecartCouplingHandler.java | 71 ++++ .../train/MinecartCoupling.java | 117 ++++++ .../train/MinecartCouplingCreationPacket.java | 45 ++ .../train/MinecartCouplingHandler.java | 383 ++++++++++++++++++ .../train/MinecartCouplingItem.java | 54 +++ .../train/MinecartCouplingRenderer.java | 197 +++++++++ .../train/MinecartCouplingSerializer.java | 65 +++ .../train/MinecartCouplingSyncPacket.java | 31 ++ .../train/MinecartSim2020.java | 224 ++++++++++ .../train/MinecartTrain.java | 367 +++++++++++++++++ .../train/PersistantDataPacket.java | 53 +++ .../train/PersistantDataPacketRequest.java | 49 +++ .../simibubi/create/events/ClientEvents.java | 98 +++-- .../simibubi/create/events/CommonEvents.java | 64 ++- .../simibubi/create/events/InputEvents.java | 54 +++ .../foundation/command/AllCommands.java | 18 + .../foundation/command/CreateCommand.java | 18 - .../create/foundation/config/CKinetics.java | 2 + .../foundation/networking/AllPackets.java | 8 + .../create/foundation/utility/Couple.java | 72 ++++ .../create/foundation/utility/Pair.java | 64 +++ .../utility/ServerSpeedProvider.java | 30 +- .../utility/SuperByteBufferCache.java | 43 +- .../create/foundation/utility/VecHelper.java | 31 ++ .../foundation/utility/WorldAttached.java | 43 ++ .../utility/outliner/LineOutline.java | 4 +- .../foundation/utility/outliner/Outline.java | 17 + .../entity/minecart_coupling/attachment.json | 56 +++ .../minecart_coupling/cart_coupling.bbmodel | 1 + .../entity/minecart_coupling/connector.json | 22 + .../models/entity/minecart_coupling/ring.json | 22 + .../create/textures/entity/coupling.png | Bin 0 -> 471 bytes .../textures/item/minecart_coupling.png | Bin 0 -> 341 bytes 58 files changed, 2426 insertions(+), 377 deletions(-) create mode 100644 src/generated/resources/assets/create/models/item/minecart_coupling.json create mode 100644 src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionHandler.java create mode 100644 src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/ClientMinecartCouplingHandler.java create mode 100644 src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCoupling.java create mode 100644 src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCouplingCreationPacket.java create mode 100644 src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCouplingHandler.java create mode 100644 src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCouplingItem.java create mode 100644 src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCouplingRenderer.java create mode 100644 src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCouplingSerializer.java create mode 100644 src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCouplingSyncPacket.java create mode 100644 src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartSim2020.java create mode 100644 src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartTrain.java create mode 100644 src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/PersistantDataPacket.java create mode 100644 src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/PersistantDataPacketRequest.java create mode 100644 src/main/java/com/simibubi/create/events/InputEvents.java create mode 100644 src/main/java/com/simibubi/create/foundation/command/AllCommands.java delete mode 100644 src/main/java/com/simibubi/create/foundation/command/CreateCommand.java create mode 100644 src/main/java/com/simibubi/create/foundation/utility/Couple.java create mode 100644 src/main/java/com/simibubi/create/foundation/utility/Pair.java create mode 100644 src/main/java/com/simibubi/create/foundation/utility/WorldAttached.java create mode 100644 src/main/resources/assets/create/models/entity/minecart_coupling/attachment.json create mode 100644 src/main/resources/assets/create/models/entity/minecart_coupling/cart_coupling.bbmodel create mode 100644 src/main/resources/assets/create/models/entity/minecart_coupling/connector.json create mode 100644 src/main/resources/assets/create/models/entity/minecart_coupling/ring.json create mode 100644 src/main/resources/assets/create/textures/entity/coupling.png create mode 100644 src/main/resources/assets/create/textures/item/minecart_coupling.png diff --git a/src/generated/resources/.cache/cache b/src/generated/resources/.cache/cache index 71f3b472a..213ad3d3e 100644 --- a/src/generated/resources/.cache/cache +++ b/src/generated/resources/.cache/cache @@ -357,17 +357,17 @@ c77b46d8b459e5c7cc495393546f3fcca8a1fa1d assets/create/blockstates/weathered_lim a3a11524cd3515fc01d905767b4b7ea782adaf03 assets/create/blockstates/yellow_seat.json 7f39521b211441f5c3e06d60c5978cebe16cacfb assets/create/blockstates/zinc_block.json b7181bcd8182b2f17088e5aa881f374c9c65470c assets/create/blockstates/zinc_ore.json -da8ae8561b7827b4aca53ef32f2dd3860a39fba1 assets/create/lang/en_ud.json -bf48621dfa7345a765c6a1e1c6a39689ddefd965 assets/create/lang/en_us.json -bc1018ede1b186c9e7656e85a57682bfd8f13814 assets/create/lang/unfinished/de_de.json -2b0ad2444e9c14c1ffcee91954940fef74061fb6 assets/create/lang/unfinished/fr_fr.json -2b3595491cfc672f3d6da7bd9e3600f139445f2b assets/create/lang/unfinished/it_it.json -3f559bd4e42159edea64e9f958901ee9c7049111 assets/create/lang/unfinished/ja_jp.json -a9e22722c8b424bc3316b3d2c6d7f21da937a2d4 assets/create/lang/unfinished/ko_kr.json -28c53f4ee00201fdf65e167c96affbcc72f3f585 assets/create/lang/unfinished/nl_nl.json -12053da965398e50eccf5d549a2caa06d1555e5f assets/create/lang/unfinished/pt_br.json -9d5f4355a84883b5e499d7af432431e0592f687b assets/create/lang/unfinished/ru_ru.json -bf2894922ec5e14db2d516002d9be46e08461a58 assets/create/lang/unfinished/zh_cn.json +a9bcfd546e95865633a97e4b29e39c4aec940338 assets/create/lang/en_ud.json +aa14daef8d31ca69ace4e643ffdc5cc9aba22818 assets/create/lang/en_us.json +b4435a02a94ae72032f3ffb8f9282e41b1dca953 assets/create/lang/unfinished/de_de.json +4f9cc39db1e0de14e9aeabea93676b8fa8ba58ec assets/create/lang/unfinished/fr_fr.json +a5a1d2d2e6154c07be187dfe2e33c203db1dd678 assets/create/lang/unfinished/it_it.json +022fee71a855d3cd206c2c1d5c36c38f089f8120 assets/create/lang/unfinished/ja_jp.json +07da262b3005fd53abd22b5da558e3494bbefa90 assets/create/lang/unfinished/ko_kr.json +f1a7c021d2a48a56141ffe70ddec7128c5ad7261 assets/create/lang/unfinished/nl_nl.json +5e10814eb0606a6bd20097067394a93842ef7957 assets/create/lang/unfinished/pt_br.json +f1367be00730002ee0f233dfebb5e920eed56900 assets/create/lang/unfinished/ru_ru.json +76928b7d9f7f41f4fa622824a872bec8e5635cea assets/create/lang/unfinished/zh_cn.json 846200eb548d3bfa2e77b41039de159b4b6cfb45 assets/create/models/block/acacia_window.json 1930fa3a3c98d53dd19e4ee7f55bc27fd47aa281 assets/create/models/block/acacia_window_pane_noside.json 1763ea2c9b981d187f5031ba608f3d5d3be3986a assets/create/models/block/acacia_window_pane_noside_alt.json @@ -1243,6 +1243,7 @@ f8d0d4b2a890ea7a69ab0c390947b48fe0478d3f assets/create/models/item/mechanical_pi bca99d467ec8ead10124becb60ac24b39be83de4 assets/create/models/item/mechanical_saw.json 0eb5726c8c0de462f432411c210d6132b2c446a4 assets/create/models/item/millstone.json 1134bc8ecdfefe5d30ee4973c37aa9a349c368b4 assets/create/models/item/minecart_contraption.json +5f44acb8a784611c17913ddf64fb4098b3a8aee9 assets/create/models/item/minecart_coupling.json dc43c88dc8ae1f425e1c10f422b09d97719af5bc assets/create/models/item/mossy_andesite.json 4ce9aabf9fa9e9e6af6b4339291e635708bdbcdf assets/create/models/item/mossy_dark_scoria.json d084f03d068d0b8c3b7c4d00014c168f61836770 assets/create/models/item/mossy_diorite.json diff --git a/src/generated/resources/assets/create/lang/en_ud.json b/src/generated/resources/assets/create/lang/en_ud.json index 845ea00c8..c7193b404 100644 --- a/src/generated/resources/assets/create/lang/en_ud.json +++ b/src/generated/resources/assets/create/lang/en_ud.json @@ -396,6 +396,7 @@ "item.create.iron_sheet": "\u0287\u01DD\u01DD\u0265S uo\u0279I", "item.create.lapis_sheet": "\u0287\u01DD\u01DD\u0265S s\u0131d\u0250\uA780", "item.create.minecart_contraption": "uo\u0131\u0287d\u0250\u0279\u0287uo\u0186 \u0287\u0279\u0250\u0254\u01DDu\u0131W", + "item.create.minecart_coupling": "bu\u0131\u05DFdno\u0186 \u0287\u0279\u0250\u0254\u01DDu\u0131W", "item.create.polished_rose_quartz": "z\u0287\u0279\u0250n\u1F49 \u01DDso\u1D1A p\u01DD\u0265s\u0131\u05DFo\u0500", "item.create.powdered_obsidian": "u\u0250\u0131p\u0131sqO p\u01DD\u0279\u01DDp\u028Do\u0500", "item.create.propeller": "\u0279\u01DD\u05DF\u05DF\u01DDdo\u0279\u0500", diff --git a/src/generated/resources/assets/create/lang/en_us.json b/src/generated/resources/assets/create/lang/en_us.json index 86e5a6ea3..e2b28974a 100644 --- a/src/generated/resources/assets/create/lang/en_us.json +++ b/src/generated/resources/assets/create/lang/en_us.json @@ -401,6 +401,7 @@ "item.create.iron_sheet": "Iron Sheet", "item.create.lapis_sheet": "Lapis Sheet", "item.create.minecart_contraption": "Minecart Contraption", + "item.create.minecart_coupling": "Minecart Coupling", "item.create.polished_rose_quartz": "Polished Rose Quartz", "item.create.powdered_obsidian": "Powdered Obsidian", "item.create.propeller": "Propeller", diff --git a/src/generated/resources/assets/create/lang/unfinished/de_de.json b/src/generated/resources/assets/create/lang/unfinished/de_de.json index a8d3157f0..107a02e1e 100644 --- a/src/generated/resources/assets/create/lang/unfinished/de_de.json +++ b/src/generated/resources/assets/create/lang/unfinished/de_de.json @@ -1,5 +1,5 @@ { - "_": "Missing Localizations: 813", + "_": "Missing Localizations: 814", "_": "->------------------------] Game Elements [------------------------<-", @@ -402,6 +402,7 @@ "item.create.iron_sheet": "Eisenblech", "item.create.lapis_sheet": "UNLOCALIZED: Lapis Sheet", "item.create.minecart_contraption": "UNLOCALIZED: Minecart Contraption", + "item.create.minecart_coupling": "UNLOCALIZED: Minecart Coupling", "item.create.polished_rose_quartz": "UNLOCALIZED: Polished Rose Quartz", "item.create.powdered_obsidian": "UNLOCALIZED: Powdered Obsidian", "item.create.propeller": "Propeller", diff --git a/src/generated/resources/assets/create/lang/unfinished/fr_fr.json b/src/generated/resources/assets/create/lang/unfinished/fr_fr.json index 962dbc00f..484c243b7 100644 --- a/src/generated/resources/assets/create/lang/unfinished/fr_fr.json +++ b/src/generated/resources/assets/create/lang/unfinished/fr_fr.json @@ -1,5 +1,5 @@ { - "_": "Missing Localizations: 416", + "_": "Missing Localizations: 417", "_": "->------------------------] Game Elements [------------------------<-", @@ -402,6 +402,7 @@ "item.create.iron_sheet": "Plaque de Fer", "item.create.lapis_sheet": "UNLOCALIZED: Lapis Sheet", "item.create.minecart_contraption": "UNLOCALIZED: Minecart Contraption", + "item.create.minecart_coupling": "UNLOCALIZED: Minecart Coupling", "item.create.polished_rose_quartz": "Quartz rose poli", "item.create.powdered_obsidian": "UNLOCALIZED: Powdered Obsidian", "item.create.propeller": "Hélice", diff --git a/src/generated/resources/assets/create/lang/unfinished/it_it.json b/src/generated/resources/assets/create/lang/unfinished/it_it.json index 2bbe58597..ae1d277e1 100644 --- a/src/generated/resources/assets/create/lang/unfinished/it_it.json +++ b/src/generated/resources/assets/create/lang/unfinished/it_it.json @@ -1,5 +1,5 @@ { - "_": "Missing Localizations: 400", + "_": "Missing Localizations: 401", "_": "->------------------------] Game Elements [------------------------<-", @@ -402,6 +402,7 @@ "item.create.iron_sheet": "Lamiera di Ferro", "item.create.lapis_sheet": "UNLOCALIZED: Lapis Sheet", "item.create.minecart_contraption": "UNLOCALIZED: Minecart Contraption", + "item.create.minecart_coupling": "UNLOCALIZED: Minecart Coupling", "item.create.polished_rose_quartz": "Quarzo Rosa Levigato", "item.create.powdered_obsidian": "UNLOCALIZED: Powdered Obsidian", "item.create.propeller": "Elica", diff --git a/src/generated/resources/assets/create/lang/unfinished/ja_jp.json b/src/generated/resources/assets/create/lang/unfinished/ja_jp.json index c4f699b59..c32b645d8 100644 --- a/src/generated/resources/assets/create/lang/unfinished/ja_jp.json +++ b/src/generated/resources/assets/create/lang/unfinished/ja_jp.json @@ -1,5 +1,5 @@ { - "_": "Missing Localizations: 395", + "_": "Missing Localizations: 396", "_": "->------------------------] Game Elements [------------------------<-", @@ -402,6 +402,7 @@ "item.create.iron_sheet": "鉄板", "item.create.lapis_sheet": "UNLOCALIZED: Lapis Sheet", "item.create.minecart_contraption": "UNLOCALIZED: Minecart Contraption", + "item.create.minecart_coupling": "UNLOCALIZED: Minecart Coupling", "item.create.polished_rose_quartz": "磨かれたローズクォーツ", "item.create.powdered_obsidian": "UNLOCALIZED: Powdered Obsidian", "item.create.propeller": "プロペラ", diff --git a/src/generated/resources/assets/create/lang/unfinished/ko_kr.json b/src/generated/resources/assets/create/lang/unfinished/ko_kr.json index dacd740e5..987f35f24 100644 --- a/src/generated/resources/assets/create/lang/unfinished/ko_kr.json +++ b/src/generated/resources/assets/create/lang/unfinished/ko_kr.json @@ -1,5 +1,5 @@ { - "_": "Missing Localizations: 400", + "_": "Missing Localizations: 401", "_": "->------------------------] Game Elements [------------------------<-", @@ -402,6 +402,7 @@ "item.create.iron_sheet": "철 판", "item.create.lapis_sheet": "UNLOCALIZED: Lapis Sheet", "item.create.minecart_contraption": "UNLOCALIZED: Minecart Contraption", + "item.create.minecart_coupling": "UNLOCALIZED: Minecart Coupling", "item.create.polished_rose_quartz": "윤나는 장밋빛 석영", "item.create.powdered_obsidian": "UNLOCALIZED: Powdered Obsidian", "item.create.propeller": "프로펠러", diff --git a/src/generated/resources/assets/create/lang/unfinished/nl_nl.json b/src/generated/resources/assets/create/lang/unfinished/nl_nl.json index 0f8d47ef3..e25f63bb0 100644 --- a/src/generated/resources/assets/create/lang/unfinished/nl_nl.json +++ b/src/generated/resources/assets/create/lang/unfinished/nl_nl.json @@ -1,5 +1,5 @@ { - "_": "Missing Localizations: 750", + "_": "Missing Localizations: 751", "_": "->------------------------] Game Elements [------------------------<-", @@ -402,6 +402,7 @@ "item.create.iron_sheet": "IJzeren Platen", "item.create.lapis_sheet": "UNLOCALIZED: Lapis Sheet", "item.create.minecart_contraption": "UNLOCALIZED: Minecart Contraption", + "item.create.minecart_coupling": "UNLOCALIZED: Minecart Coupling", "item.create.polished_rose_quartz": "UNLOCALIZED: Polished Rose Quartz", "item.create.powdered_obsidian": "UNLOCALIZED: Powdered Obsidian", "item.create.propeller": "Propeller", diff --git a/src/generated/resources/assets/create/lang/unfinished/pt_br.json b/src/generated/resources/assets/create/lang/unfinished/pt_br.json index c64bd695f..b58961752 100644 --- a/src/generated/resources/assets/create/lang/unfinished/pt_br.json +++ b/src/generated/resources/assets/create/lang/unfinished/pt_br.json @@ -1,5 +1,5 @@ { - "_": "Missing Localizations: 820", + "_": "Missing Localizations: 821", "_": "->------------------------] Game Elements [------------------------<-", @@ -402,6 +402,7 @@ "item.create.iron_sheet": "Placas de Ferro", "item.create.lapis_sheet": "UNLOCALIZED: Lapis Sheet", "item.create.minecart_contraption": "UNLOCALIZED: Minecart Contraption", + "item.create.minecart_coupling": "UNLOCALIZED: Minecart Coupling", "item.create.polished_rose_quartz": "UNLOCALIZED: Polished Rose Quartz", "item.create.powdered_obsidian": "UNLOCALIZED: Powdered Obsidian", "item.create.propeller": "Hélice", diff --git a/src/generated/resources/assets/create/lang/unfinished/ru_ru.json b/src/generated/resources/assets/create/lang/unfinished/ru_ru.json index d0ab81d18..c0713565b 100644 --- a/src/generated/resources/assets/create/lang/unfinished/ru_ru.json +++ b/src/generated/resources/assets/create/lang/unfinished/ru_ru.json @@ -1,5 +1,5 @@ { - "_": "Missing Localizations: 814", + "_": "Missing Localizations: 815", "_": "->------------------------] Game Elements [------------------------<-", @@ -402,6 +402,7 @@ "item.create.iron_sheet": "Железная пластина", "item.create.lapis_sheet": "UNLOCALIZED: Lapis Sheet", "item.create.minecart_contraption": "UNLOCALIZED: Minecart Contraption", + "item.create.minecart_coupling": "UNLOCALIZED: Minecart Coupling", "item.create.polished_rose_quartz": "UNLOCALIZED: Polished Rose Quartz", "item.create.powdered_obsidian": "UNLOCALIZED: Powdered Obsidian", "item.create.propeller": "Пропеллер", diff --git a/src/generated/resources/assets/create/lang/unfinished/zh_cn.json b/src/generated/resources/assets/create/lang/unfinished/zh_cn.json index decafb66f..7a67ca10c 100644 --- a/src/generated/resources/assets/create/lang/unfinished/zh_cn.json +++ b/src/generated/resources/assets/create/lang/unfinished/zh_cn.json @@ -1,5 +1,5 @@ { - "_": "Missing Localizations: 76", + "_": "Missing Localizations: 77", "_": "->------------------------] Game Elements [------------------------<-", @@ -402,6 +402,7 @@ "item.create.iron_sheet": "铁板", "item.create.lapis_sheet": "青金石板", "item.create.minecart_contraption": "装配过的矿车", + "item.create.minecart_coupling": "UNLOCALIZED: Minecart Coupling", "item.create.polished_rose_quartz": "磨制玫瑰石英", "item.create.powdered_obsidian": "黑曜石粉末", "item.create.propeller": "扇叶", diff --git a/src/generated/resources/assets/create/models/item/minecart_coupling.json b/src/generated/resources/assets/create/models/item/minecart_coupling.json new file mode 100644 index 000000000..2f2f7a357 --- /dev/null +++ b/src/generated/resources/assets/create/models/item/minecart_coupling.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "create:item/minecart_coupling" + } +} \ No newline at end of file diff --git a/src/main/java/com/simibubi/create/AllBlockPartials.java b/src/main/java/com/simibubi/create/AllBlockPartials.java index c0a1b9d1c..40d85a378 100644 --- a/src/main/java/com/simibubi/create/AllBlockPartials.java +++ b/src/main/java/com/simibubi/create/AllBlockPartials.java @@ -94,7 +94,13 @@ public class AllBlockPartials { FLAG_LONG_IN = get("mechanical_arm/flag/long_in"), FLAG_LONG_OUT = get("mechanical_arm/flag/long_out"), MECHANICAL_PUMP_ARROW = get("mechanical_pump/arrow"), MECHANICAL_PUMP_COG = get("mechanical_pump/cog"), - FLUID_PIPE_CASING = get("fluid_pipe/casing"); + FLUID_PIPE_CASING = get("fluid_pipe/casing"), + + COUPLING_ATTACHMENT = getEntity("minecart_coupling/attachment"), + COUPLING_RING = getEntity("minecart_coupling/ring"), + COUPLING_CONNECTOR = getEntity("minecart_coupling/connector") + + ; public static final Map PIPE_RIMS = map(); public static final Map BLAZES = map(); @@ -124,6 +130,13 @@ public class AllBlockPartials { return new HashMap<>(); } + private static AllBlockPartials getEntity(String path) { + AllBlockPartials partials = new AllBlockPartials(); + partials.modelLocation = new ResourceLocation(Create.ID, "entity/" + path); + all.add(partials); + return partials; + } + private static AllBlockPartials get(String path) { AllBlockPartials partials = new AllBlockPartials(); partials.modelLocation = new ResourceLocation(Create.ID, "block/" + path); diff --git a/src/main/java/com/simibubi/create/AllItems.java b/src/main/java/com/simibubi/create/AllItems.java index ce9ed043d..6b2e0205f 100644 --- a/src/main/java/com/simibubi/create/AllItems.java +++ b/src/main/java/com/simibubi/create/AllItems.java @@ -13,6 +13,7 @@ import static com.simibubi.create.content.AllSections.SCHEMATICS; import com.simibubi.create.content.contraptions.components.structureMovement.glue.SuperGlueItem; import com.simibubi.create.content.contraptions.components.structureMovement.mounted.MinecartContraptionItem; +import com.simibubi.create.content.contraptions.components.structureMovement.train.MinecartCouplingItem; import com.simibubi.create.content.contraptions.goggles.GogglesItem; import com.simibubi.create.content.contraptions.processing.burner.BlazeBurnerBlockItem; import com.simibubi.create.content.contraptions.relays.belt.item.BeltConnectorItem; @@ -122,15 +123,19 @@ public class AllItems { REGISTRATE.item("vertical_gearbox", VerticalGearboxItem::new) .model(AssetLookup.customItemModel("gearbox", "item_vertical")) .register(); - + public static final ItemEntry EMPTY_BLAZE_BURNER = REGISTRATE.item("empty_blaze_burner", BlazeBurnerBlockItem::empty) - .model(AssetLookup.customItemModel("blaze_burner", "block")) - .register(); + .model(AssetLookup.customItemModel("blaze_burner", "block")) + .register(); public static final ItemEntry SUPER_GLUE = REGISTRATE.item("super_glue", SuperGlueItem::new) .register(); + public static final ItemEntry MINECART_COUPLING = + REGISTRATE.item("minecart_coupling", MinecartCouplingItem::new) + .register(); + public static final ItemEntry SAND_PAPER = REGISTRATE.item("sand_paper", SandPaperItem::new) .transform(CreateRegistrate.customRenderedItem(() -> SandPaperModel::new)) .register(); diff --git a/src/main/java/com/simibubi/create/Create.java b/src/main/java/com/simibubi/create/Create.java index a10d6c7a9..765c5bb55 100644 --- a/src/main/java/com/simibubi/create/Create.java +++ b/src/main/java/com/simibubi/create/Create.java @@ -26,8 +26,10 @@ import net.minecraft.item.crafting.IRecipeSerializer; import net.minecraft.particles.ParticleType; import net.minecraft.util.ResourceLocation; import net.minecraft.util.SoundEvent; +import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.eventbus.api.EventPriority; import net.minecraftforge.eventbus.api.IEventBus; +import net.minecraftforge.fml.DistExecutor; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; import net.minecraftforge.fml.event.lifecycle.GatherDataEvent; @@ -70,8 +72,10 @@ public class Create { modEventBus.addListener(AllConfigs::onLoad); modEventBus.addListener(AllConfigs::onReload); modEventBus.addListener(EventPriority.LOWEST, this::gatherData); - CreateClient.addClientListeners(modEventBus); + AllConfigs.register(); + + DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> CreateClient.addClientListeners(modEventBus)); } public static void init(final FMLCommonSetupEvent event) { @@ -85,17 +89,6 @@ public class Create { AllWorldFeatures.reload(); } - public static void tick() { - if (schematicReceiver == null) - schematicReceiver = new ServerSchematicLoader(); - schematicReceiver.tick(); - lagger.tick(); - } - - public static void shutdown() { - schematicReceiver.shutdown(); - } - public static CreateRegistrate registrate() { return registrate.get(); } diff --git a/src/main/java/com/simibubi/create/CreateClient.java b/src/main/java/com/simibubi/create/CreateClient.java index e27420044..5aa0e32d0 100644 --- a/src/main/java/com/simibubi/create/CreateClient.java +++ b/src/main/java/com/simibubi/create/CreateClient.java @@ -5,16 +5,8 @@ import java.util.List; import java.util.Map; import java.util.function.Function; -import com.simibubi.create.content.contraptions.KineticDebugger; import com.simibubi.create.content.contraptions.base.KineticTileEntityRenderer; import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionRenderer; -import com.simibubi.create.content.contraptions.components.structureMovement.chassis.ChassisRangeDisplay; -import com.simibubi.create.content.contraptions.relays.belt.item.BeltConnectorHandler; -import com.simibubi.create.content.curiosities.tools.ExtendoGripRenderHandler; -import com.simibubi.create.content.curiosities.zapper.ZapperRenderHandler; -import com.simibubi.create.content.curiosities.zapper.blockzapper.BlockzapperRenderHandler; -import com.simibubi.create.content.curiosities.zapper.terrainzapper.WorldshaperRenderHandler; -import com.simibubi.create.content.logistics.block.mechanicalArm.ArmInteractionPointHandler; import com.simibubi.create.content.schematics.ClientSchematicLoader; import com.simibubi.create.content.schematics.client.SchematicAndQuillHandler; import com.simibubi.create.content.schematics.client.SchematicHandler; @@ -23,10 +15,6 @@ import com.simibubi.create.foundation.block.render.CustomBlockModels; import com.simibubi.create.foundation.block.render.SpriteShifter; import com.simibubi.create.foundation.item.CustomItemModels; import com.simibubi.create.foundation.item.CustomRenderedItems; -import com.simibubi.create.foundation.tileEntity.behaviour.edgeInteraction.EdgeInteractionRenderer; -import com.simibubi.create.foundation.tileEntity.behaviour.filtering.FilteringRenderer; -import com.simibubi.create.foundation.tileEntity.behaviour.linked.LinkRenderer; -import com.simibubi.create.foundation.tileEntity.behaviour.scrollvalue.ScrollValueRenderer; import com.simibubi.create.foundation.utility.SuperByteBufferCache; import com.simibubi.create.foundation.utility.outliner.Outliner; @@ -40,14 +28,11 @@ import net.minecraft.item.Item; import net.minecraft.resources.IReloadableResourceManager; import net.minecraft.resources.IResourceManager; import net.minecraft.util.ResourceLocation; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.client.event.ModelBakeEvent; import net.minecraftforge.client.event.ModelRegistryEvent; import net.minecraftforge.client.event.TextureStitchEvent; import net.minecraftforge.client.model.ModelLoader; import net.minecraftforge.eventbus.api.IEventBus; -import net.minecraftforge.fml.DistExecutor; import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; public class CreateClient { @@ -64,13 +49,11 @@ public class CreateClient { private static AllColorHandlers colorHandlers; public static void addClientListeners(IEventBus modEventBus) { - DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> { - modEventBus.addListener(CreateClient::clientInit); - modEventBus.addListener(CreateClient::onModelBake); - modEventBus.addListener(CreateClient::onModelRegistry); - modEventBus.addListener(CreateClient::onTextureStitch); - modEventBus.addListener(AllParticleTypes::registerFactories); - }); + modEventBus.addListener(CreateClient::clientInit); + modEventBus.addListener(CreateClient::onModelBake); + modEventBus.addListener(CreateClient::onModelRegistry); + modEventBus.addListener(CreateClient::onTextureStitch); + modEventBus.addListener(AllParticleTypes::registerFactories); } public static void clientInit(FMLClientSetupEvent event) { @@ -95,27 +78,6 @@ public class CreateClient { ((IReloadableResourceManager) resourceManager).addReloadListener(new ResourceReloadHandler()); } - public static void gameTick() { - schematicSender.tick(); - schematicAndQuillHandler.tick(); - schematicHandler.tick(); - BeltConnectorHandler.tick(); - FilteringRenderer.tick(); - LinkRenderer.tick(); - ScrollValueRenderer.tick(); - ChassisRangeDisplay.tick(); - EdgeInteractionRenderer.tick(); - WorldshaperRenderHandler.tick(); - BlockzapperRenderHandler.tick(); - KineticDebugger.tick(); - ZapperRenderHandler.tick(); - ExtendoGripRenderHandler.tick(); -// CollisionDebugger.tick(); - ArmInteractionPointHandler.tick(); - outliner.tickOutlines(); - } - - @OnlyIn(Dist.CLIENT) public static void onTextureStitch(TextureStitchEvent.Pre event) { if (!event.getMap() .getId() @@ -125,7 +87,6 @@ public class CreateClient { .forEach(event::addSprite); } - @OnlyIn(Dist.CLIENT) public static void onModelBake(ModelBakeEvent event) { Map modelRegistry = event.getModelRegistry(); AllBlockPartials.onModelBake(event); @@ -140,7 +101,6 @@ public class CreateClient { }); } - @OnlyIn(Dist.CLIENT) public static void onModelRegistry(ModelRegistryEvent event) { AllBlockPartials.onModelRegistry(event); @@ -149,12 +109,10 @@ public class CreateClient { .forEach(ModelLoader::addSpecialModel)); } - @OnlyIn(Dist.CLIENT) protected static ModelResourceLocation getItemModelLocation(Item item) { return new ModelResourceLocation(item.getRegistryName(), "inventory"); } - @OnlyIn(Dist.CLIENT) protected static List getAllBlockStateModelLocations(Block block) { List models = new ArrayList<>(); block.getStateContainer() @@ -165,20 +123,17 @@ public class CreateClient { return models; } - @OnlyIn(Dist.CLIENT) protected static ModelResourceLocation getBlockModelLocation(Block block, String suffix) { return new ModelResourceLocation(block.getRegistryName(), suffix); } - @OnlyIn(Dist.CLIENT) protected static void swapModels(Map modelRegistry, List locations, Function factory) { locations.forEach(location -> { swapModels(modelRegistry, location, factory); }); } - - @OnlyIn(Dist.CLIENT) + protected static void swapModels(Map modelRegistry, ModelResourceLocation location, Function factory) { modelRegistry.put(location, factory.apply(modelRegistry.get(location))); diff --git a/src/main/java/com/simibubi/create/content/contraptions/KineticDebugger.java b/src/main/java/com/simibubi/create/content/contraptions/KineticDebugger.java index 74a5e515f..aa087c7c7 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/KineticDebugger.java +++ b/src/main/java/com/simibubi/create/content/contraptions/KineticDebugger.java @@ -3,6 +3,7 @@ package com.simibubi.create.content.contraptions; import com.simibubi.create.CreateClient; import com.simibubi.create.content.contraptions.base.IRotate; import com.simibubi.create.content.contraptions.base.KineticTileEntity; +import com.simibubi.create.content.contraptions.base.KineticTileEntityRenderer; import com.simibubi.create.foundation.config.AllConfigs; import com.simibubi.create.foundation.utility.ColorHelper; import com.simibubi.create.foundation.utility.VecHelper; @@ -24,8 +25,14 @@ import net.minecraft.world.World; public class KineticDebugger { public static void tick() { - if (!isActive()) + if (!isActive()) { + if (KineticTileEntityRenderer.rainbowMode) { + KineticTileEntityRenderer.rainbowMode = false; + CreateClient.bufferCache.invalidate(); + } return; + } + KineticTileEntity te = getSelectedTE(); if (te == null) return; diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/Contraption.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/Contraption.java index f3f97426c..e93612c77 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/Contraption.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/Contraption.java @@ -234,7 +234,7 @@ public abstract class Contraption { return true; if (!BlockMovementTraits.movementNecessary(world, pos)) return true; - if (!BlockMovementTraits.movementAllowed(world, pos)) + if (!movementAllowed(world, pos)) return false; BlockState state = world.getBlockState(pos); if (isChassis(state) && !moveChassis(world, pos, forcedDirection, frontier, visited)) @@ -344,7 +344,7 @@ public abstract class Contraption { BlockState blockState = world.getBlockState(offsetPos); if (isAnchoringBlockAt(offsetPos)) continue; - if (!BlockMovementTraits.movementAllowed(world, offsetPos)) { + if (!movementAllowed(world, offsetPos)) { if (offset == forcedDirection && isSlimeBlock) return false; continue; @@ -367,6 +367,10 @@ public abstract class Contraption { return blocks.size() <= AllConfigs.SERVER.kinetics.maxBlocksMoved.get(); } + protected boolean movementAllowed(World world, BlockPos pos) { + return BlockMovementTraits.movementAllowed(world, pos); + } + protected boolean isAnchoringBlockAt(BlockPos pos) { return pos.equals(anchor); } diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionCollider.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionCollider.java index 6ae155701..dee4afe93 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionCollider.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionCollider.java @@ -1,6 +1,5 @@ package com.simibubi.create.content.contraptions.components.structureMovement; -import static java.util.concurrent.TimeUnit.SECONDS; import static net.minecraft.entity.Entity.collideBoundingBoxHeuristically; import static net.minecraft.entity.Entity.horizontalMag; @@ -8,15 +7,12 @@ import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Iterator; import java.util.List; -import java.util.concurrent.ExecutionException; import java.util.stream.Stream; import org.apache.commons.lang3.mutable.MutableBoolean; import org.apache.commons.lang3.mutable.MutableObject; import com.google.common.base.Predicates; -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; import com.google.common.collect.ImmutableSet; import com.simibubi.create.AllBlocks; import com.simibubi.create.content.contraptions.components.actors.BlockBreakingMovementBehaviour; @@ -31,18 +27,11 @@ import com.simibubi.create.foundation.utility.VecHelper; import net.minecraft.block.BlockState; import net.minecraft.block.CocoaBlock; -import net.minecraft.client.Minecraft; -import net.minecraft.client.world.ClientWorld; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; -import net.minecraft.entity.LivingEntity; -import net.minecraft.entity.MoverType; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.ServerPlayerEntity; -import net.minecraft.nbt.CompoundNBT; -import net.minecraft.util.DamageSource; import net.minecraft.util.Direction; -import net.minecraft.util.Direction.Axis; import net.minecraft.util.Direction.AxisDirection; import net.minecraft.util.ReuseableStream; import net.minecraft.util.math.AxisAlignedBB; @@ -54,78 +43,11 @@ import net.minecraft.util.math.shapes.VoxelShape; import net.minecraft.util.math.shapes.VoxelShapes; import net.minecraft.world.World; import net.minecraft.world.gen.feature.template.Template.BlockInfo; -import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.api.distmarker.OnlyIn; -import net.minecraftforge.common.util.Constants.NBT; -import net.minecraftforge.event.TickEvent.ClientTickEvent; -import net.minecraftforge.event.TickEvent.Phase; -import net.minecraftforge.event.TickEvent.WorldTickEvent; -import net.minecraftforge.event.entity.EntityJoinWorldEvent; -import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.common.Mod.EventBusSubscriber; -@EventBusSubscriber public class ContraptionCollider { - public static DamageSource damageSourceContraptionSuffocate = - new DamageSource("create.contraption_suffocate").setDamageBypassesArmor(); - public static boolean wasClientPlayerGrounded; - public static Cache>> activeContraptions = CacheBuilder.newBuilder() - .expireAfterAccess(400, SECONDS) - .build(); - - @SubscribeEvent - public static void addSpawnedContraptionsToCollisionList(EntityJoinWorldEvent event) { - Entity entity = event.getEntity(); - if (!(entity instanceof ContraptionEntity)) - return; - try { - List> list = activeContraptions.get(event.getWorld(), ArrayList::new); - ContraptionEntity contraption = (ContraptionEntity) entity; - list.add(new WeakReference<>(contraption)); - } catch (ExecutionException e) { - e.printStackTrace(); - } - } - - @SubscribeEvent - @OnlyIn(Dist.CLIENT) - public static void playerCollisionHappensOnClientTick(ClientTickEvent event) { - if (event.phase == Phase.START) - return; - ClientWorld world = Minecraft.getInstance().world; - if (world == null) - return; - runCollisions(world); - } - - @SubscribeEvent - public static void entityCollisionHappensPreWorldTick(WorldTickEvent event) { - if (event.phase == Phase.END) - return; - World world = event.world; - runCollisions(world); - } - - @SubscribeEvent - public static void entitiesWhoJustDismountedGetSentToTheRightLocation(LivingUpdateEvent event) { - LivingEntity entityLiving = event.getEntityLiving(); - if (entityLiving == null) - return; - if (entityLiving.world.isRemote) - return; - CompoundNBT data = entityLiving.getPersistentData(); - if (!data.contains("ContraptionDismountLocation")) - return; - Vec3d position = VecHelper.readNBT(data.getList("ContraptionDismountLocation", NBT.TAG_DOUBLE)); - if (entityLiving.getRidingEntity() == null) - entityLiving.setPositionAndUpdate(position.x, position.y, position.z); - data.remove("ContraptionDismountLocation"); - } - - private static void runCollisions(World world) { - List> list = activeContraptions.getIfPresent(world); + public static void runCollisions(World world) { + List> list = ContraptionHandler.activeContraptions.getIfPresent(world); if (list == null) return; for (Iterator> iterator = list.iterator(); iterator.hasNext();) { @@ -139,7 +61,7 @@ public class ContraptionCollider { } } - public static void collideEntities(ContraptionEntity contraptionEntity) { + private static void collideEntities(ContraptionEntity contraptionEntity) { World world = contraptionEntity.getEntityWorld(); Contraption contraption = contraptionEntity.getContraption(); AxisAlignedBB bounds = contraptionEntity.getBoundingBox(); @@ -289,7 +211,7 @@ public class ContraptionCollider { entity.fallDistance = 0; entity.onGround = true; contraptionEntity.collidingEntities.add(entity); - if (!serverPlayer) + if (!serverPlayer) contactPointMotion = contraptionEntity.getContactPointMotion(entityPosition); } @@ -325,7 +247,7 @@ public class ContraptionCollider { entity.setPosition(entityPosition.x + allowedMovement.x, entityPosition.y + allowedMovement.y, entityPosition.z + allowedMovement.z); entity.setMotion(entityMotion); - + if (!serverPlayer && player) AllPackets.channel.sendToServer(new ClientMotionPacket(entityMotion, true)); } @@ -373,68 +295,7 @@ public class ContraptionCollider { return vec3d; } - public static void pushEntityOutOfShape(Entity entity, VoxelShape voxelShape, Vec3d positionOffset, - Vec3d shapeMotion) { - AxisAlignedBB entityBB = entity.getBoundingBox() - .offset(positionOffset); - Vec3d entityMotion = entity.getMotion(); - - if (!voxelShape.toBoundingBoxList() - .stream() - .anyMatch(entityBB::intersects)) - return; - - AxisAlignedBB shapeBB = voxelShape.getBoundingBox(); - Direction bestSide = Direction.DOWN; - double bestOffset = 100; - double finalOffset = 0; - - for (Direction face : Direction.values()) { - Axis axis = face.getAxis(); - double d = axis == Axis.X ? entityBB.getXSize() + shapeBB.getXSize() - : axis == Axis.Y ? entityBB.getYSize() + shapeBB.getYSize() : entityBB.getZSize() + shapeBB.getZSize(); - d = d + .5f; - - Vec3d nudge = new Vec3d(face.getDirectionVec()).scale(d); - AxisAlignedBB nudgedBB = entityBB.offset(nudge.getX(), nudge.getY(), nudge.getZ()); - double nudgeDistance = face.getAxisDirection() == AxisDirection.POSITIVE ? -d : d; - double offset = voxelShape.getAllowedOffset(face.getAxis(), nudgedBB, nudgeDistance); - double abs = Math.abs(nudgeDistance - offset); - if (abs < Math.abs(bestOffset) && abs != 0) { - bestOffset = abs; - finalOffset = abs; - bestSide = face; - } - } - - if (bestOffset != 0) { - entity.move(MoverType.SELF, new Vec3d(bestSide.getDirectionVec()).scale(finalOffset)); - boolean positive = bestSide.getAxisDirection() == AxisDirection.POSITIVE; - - double clamped; - switch (bestSide.getAxis()) { - case X: - clamped = positive ? Math.max(shapeMotion.x, entityMotion.x) : Math.min(shapeMotion.x, entityMotion.x); - entity.setMotion(clamped, entityMotion.y, entityMotion.z); - break; - case Y: - clamped = positive ? Math.max(shapeMotion.y, entityMotion.y) : Math.min(shapeMotion.y, entityMotion.y); - if (bestSide == Direction.UP) - clamped = shapeMotion.y; - entity.setMotion(entityMotion.x, clamped, entityMotion.z); - entity.handleFallDamage(entity.fallDistance, 1); - entity.fallDistance = 0; - entity.onGround = true; - break; - case Z: - clamped = positive ? Math.max(shapeMotion.z, entityMotion.z) : Math.min(shapeMotion.z, entityMotion.z); - entity.setMotion(entityMotion.x, entityMotion.y, clamped); - break; - } - } - } - - public static ReuseableStream getPotentiallyCollidedShapes(World world, Contraption contraption, + private static ReuseableStream getPotentiallyCollidedShapes(World world, Contraption contraption, AxisAlignedBB localBB) { double height = localBB.getYSize(); diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionEntity.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionEntity.java index 49011f157..ff6f560eb 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionEntity.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionEntity.java @@ -17,6 +17,8 @@ import com.simibubi.create.content.contraptions.components.structureMovement.glu import com.simibubi.create.content.contraptions.components.structureMovement.mounted.CartAssemblerTileEntity.CartMovementMode; import com.simibubi.create.content.contraptions.components.structureMovement.mounted.MountedContraption; import com.simibubi.create.content.contraptions.components.structureMovement.sync.ContraptionSeatMappingPacket; +import com.simibubi.create.content.contraptions.components.structureMovement.train.MinecartCoupling; +import com.simibubi.create.content.contraptions.components.structureMovement.train.MinecartCouplingHandler; import com.simibubi.create.foundation.item.ItemHelper; import com.simibubi.create.foundation.networking.AllPackets; import com.simibubi.create.foundation.utility.AngleHelper; @@ -30,6 +32,7 @@ import net.minecraft.entity.EntityType; import net.minecraft.entity.IProjectile; import net.minecraft.entity.item.BoatEntity; import net.minecraft.entity.item.HangingEntity; +import net.minecraft.entity.item.minecart.AbstractMinecartEntity; import net.minecraft.entity.item.minecart.FurnaceMinecartEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; @@ -69,6 +72,7 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD protected Vec3d motionBeforeStall; protected boolean stationary; protected boolean initialized; + protected boolean onCoupling; final List collidingEntities = new ArrayList<>(); private boolean isSerializingFurnaceCart; @@ -105,10 +109,11 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD } public static ContraptionEntity createMounted(World world, Contraption contraption, float initialAngle, - Direction facing) { + Direction facing, boolean onCoupling) { ContraptionEntity entity = createMounted(world, contraption, initialAngle); entity.forcedAngle = facing.getHorizontalAngle(); entity.forceYaw(entity.forcedAngle); + entity.onCoupling = onCoupling; return entity; } @@ -197,7 +202,7 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD return; callback.accept(passenger, transformedVector.x, transformedVector.y, transformedVector.z); } - + protected Vec3d getPassengerPosition(Entity passenger) { AxisAlignedBB bb = passenger.getBoundingBox(); double ySize = bb.getYSize(); @@ -308,14 +313,33 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD boolean rotationLock = false; boolean pauseWhileRotating = false; - if (contraption instanceof MountedContraption) { - rotationLock = ((MountedContraption) contraption).rotationMode == CartMovementMode.ROTATION_LOCKED; - pauseWhileRotating = ((MountedContraption) contraption).rotationMode == CartMovementMode.ROTATE_PAUSED; - } - Entity riding = e; while (riding.getRidingEntity() != null) riding = riding.getRidingEntity(); + + if (contraption instanceof MountedContraption) { + MountedContraption mountedContraption = (MountedContraption) contraption; + if (onCoupling && riding instanceof AbstractMinecartEntity) { + MinecartCoupling coupling = MinecartCouplingHandler.getCoupling(world, riding.getUniqueID()); + if (coupling != null && coupling.areBothEndsPresent()) { + Vec3d positionVec = coupling.asCouple() + .getSecond() + .getPositionVec(); + prevYaw = yaw; + prevPitch = pitch; + double diffZ = positionVec.z - getZ(); + double diffX = positionVec.x - getX(); + yaw = 90 + (float) (MathHelper.atan2(diffZ, diffX) * 180 / Math.PI); + pitch = (float) (Math.atan2(positionVec.y - getY(), Math.sqrt(diffX * diffX + diffZ * diffZ)) * 180 + / Math.PI); + return; + } + } + + rotationLock = mountedContraption.rotationMode == CartMovementMode.ROTATION_LOCKED; + pauseWhileRotating = mountedContraption.rotationMode == CartMovementMode.ROTATE_PAUSED; + } + Vec3d movementVector = riding.getMotion(); if (riding instanceof BoatEntity) movementVector = getPositionVec().subtract(prevPosX, prevPosY, prevPosZ); @@ -355,12 +379,12 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD if (!isStalled() && (riding instanceof FurnaceMinecartEntity)) { FurnaceMinecartEntity furnaceCart = (FurnaceMinecartEntity) riding; - + // Notify to not trigger serialization side-effects isSerializingFurnaceCart = true; CompoundNBT nbt = furnaceCart.serializeNBT(); isSerializingFurnaceCart = false; - + int fuel = nbt.getInt("Fuel"); int fuelBefore = fuel; double pushX = nbt.getDouble("PushX"); @@ -560,6 +584,7 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD @Override protected void readAdditional(CompoundNBT compound) { initialized = compound.getBoolean("Initialized"); + onCoupling = compound.getBoolean("OnCoupling"); contraption = Contraption.fromNBT(world, compound.getCompound("Contraption")); initialAngle = compound.getFloat("InitialAngle"); forceYaw(compound.contains("ForcedYaw") ? compound.getFloat("ForcedYaw") : initialAngle); @@ -611,6 +636,7 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD compound.putFloat("InitialAngle", initialAngle); compound.putBoolean("Stalled", isStalled()); compound.putBoolean("Initialized", initialized); + compound.putBoolean("OnCoupling", onCoupling); } @Override @@ -682,7 +708,7 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD public CompoundNBT writeWithoutTypeId(CompoundNBT nbt) { if (isSerializingFurnaceCart) return nbt; - + Vec3d vec = getPositionVec(); List passengers = getPassengers(); @@ -802,6 +828,8 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD return false; if (e instanceof HangingEntity) return false; + if (e instanceof AbstractMinecartEntity) + return false; if (e instanceof SuperGlueEntity) return false; if (e instanceof SeatEntity) diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionHandler.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionHandler.java new file mode 100644 index 000000000..ff63f6662 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/ContraptionHandler.java @@ -0,0 +1,51 @@ +package com.simibubi.create.content.contraptions.components.structureMovement; + +import static java.util.concurrent.TimeUnit.SECONDS; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; + +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.simibubi.create.foundation.utility.VecHelper; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.LivingEntity; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; +import net.minecraftforge.common.util.Constants.NBT; + +public class ContraptionHandler { + + public static Cache>> activeContraptions = CacheBuilder.newBuilder() + .expireAfterAccess(400, SECONDS) + .build(); + + public static void addSpawnedContraptionsToCollisionList(Entity entity, World world) { + if (!(entity instanceof ContraptionEntity)) + return; + try { + List> list = activeContraptions.get(world, ArrayList::new); + ContraptionEntity contraption = (ContraptionEntity) entity; + list.add(new WeakReference<>(contraption)); + } catch (ExecutionException e) { + e.printStackTrace(); + } + } + + public static void entitiesWhoJustDismountedGetSentToTheRightLocation(LivingEntity entityLiving, World world) { + if (world.isRemote) + return; + CompoundNBT data = entityLiving.getPersistentData(); + if (!data.contains("ContraptionDismountLocation")) + return; + Vec3d position = VecHelper.readNBT(data.getList("ContraptionDismountLocation", NBT.TAG_DOUBLE)); + if (entityLiving.getRidingEntity() == null) + entityLiving.setPositionAndUpdate(position.x, position.y, position.z); + data.remove("ContraptionDismountLocation"); + } + +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/CartAssemblerBlock.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/CartAssemblerBlock.java index f44b9dc8f..b23dba5e6 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/CartAssemblerBlock.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/CartAssemblerBlock.java @@ -11,6 +11,7 @@ import com.simibubi.create.AllShapes; import com.simibubi.create.AllTileEntities; import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionEntity; import com.simibubi.create.content.contraptions.components.structureMovement.mounted.CartAssemblerTileEntity.CartMovementMode; +import com.simibubi.create.content.contraptions.components.structureMovement.train.MinecartCouplingHandler; import com.simibubi.create.content.contraptions.wrench.IWrenchable; import com.simibubi.create.content.schematics.ISpecialBlockItemRequirement; import com.simibubi.create.content.schematics.ItemRequirement; @@ -111,7 +112,7 @@ public class CartAssemblerBlock extends AbstractRailBlock @Override public void onMinecartPass(@Nonnull BlockState state, @Nonnull World world, @Nonnull BlockPos pos, AbstractMinecartEntity cart) { - if (!cart.canBeRidden() && !(cart instanceof FurnaceMinecartEntity)) + if (!canAssembleTo(cart)) return; withTileEntityDo(world, pos, te -> { @@ -163,6 +164,10 @@ public class CartAssemblerBlock extends AbstractRailBlock }); } + public static boolean canAssembleTo(AbstractMinecartEntity cart) { + return cart.canBeRidden() || cart instanceof FurnaceMinecartEntity; + } + @Override @Nonnull public ActionResultType onUse(@Nonnull BlockState state, @Nonnull World world, @Nonnull BlockPos pos, @@ -207,7 +212,12 @@ public class CartAssemblerBlock extends AbstractRailBlock float initialAngle = facing.getHorizontalAngle(); withTileEntityDo(world, pos, te -> contraption.rotationMode = CartMovementMode.values()[te.movementMode.value]); - ContraptionEntity entity = ContraptionEntity.createMounted(world, contraption, initialAngle, facing); + boolean couplingFound = contraption.connectedCart != null; + if (couplingFound) + MinecartCouplingHandler.connectCarts(null, world, cart.getEntityId(), + contraption.connectedCart.getEntityId()); + ContraptionEntity entity = + ContraptionEntity.createMounted(world, contraption, initialAngle, facing, couplingFound); entity.setPosition(pos.getX(), pos.getY(), pos.getZ()); world.addEntity(entity); entity.startRiding(cart); diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/MinecartContraptionItem.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/MinecartContraptionItem.java index ad7b991bb..e42b51b49 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/MinecartContraptionItem.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/MinecartContraptionItem.java @@ -159,7 +159,8 @@ public class MinecartContraptionItem extends Item { ContraptionEntity contraption; if (newFacing != null) - contraption = ContraptionEntity.createMounted(world, mountedContraption, initialAngle, newFacing); + contraption = + ContraptionEntity.createMounted(world, mountedContraption, initialAngle, newFacing, false); else contraption = ContraptionEntity.createMounted(world, mountedContraption, initialAngle); @@ -212,8 +213,7 @@ public class MinecartContraptionItem extends Item { public static ItemStack create(Type type, ContraptionEntity entity) { ItemStack stack = - (type == Type.RIDEABLE ? AllItems.MINECART_CONTRAPTION : AllItems.FURNACE_MINECART_CONTRAPTION) - .asStack(); + (type == Type.RIDEABLE ? AllItems.MINECART_CONTRAPTION : AllItems.FURNACE_MINECART_CONTRAPTION).asStack(); CompoundNBT tag = entity.getContraption() .writeNBT(); tag.remove("UUID"); diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/MountedContraption.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/MountedContraption.java index 928eff3c8..cae09b1ca 100644 --- a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/MountedContraption.java +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/mounted/MountedContraption.java @@ -10,15 +10,19 @@ import com.simibubi.create.AllBlocks; import com.simibubi.create.content.contraptions.components.structureMovement.AllContraptionTypes; import com.simibubi.create.content.contraptions.components.structureMovement.Contraption; import com.simibubi.create.content.contraptions.components.structureMovement.mounted.CartAssemblerTileEntity.CartMovementMode; +import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.NBTHelper; +import com.simibubi.create.foundation.utility.VecHelper; import net.minecraft.block.BlockState; +import net.minecraft.entity.item.minecart.AbstractMinecartEntity; import net.minecraft.nbt.CompoundNBT; import net.minecraft.state.properties.BlockStateProperties; import net.minecraft.state.properties.RailShape; import net.minecraft.tileentity.TileEntity; import net.minecraft.util.Direction; import net.minecraft.util.Direction.Axis; +import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.world.IWorld; import net.minecraft.world.World; @@ -27,6 +31,7 @@ import net.minecraft.world.gen.feature.template.Template.BlockInfo; public class MountedContraption extends Contraption { public CartMovementMode rotationMode; + public AbstractMinecartEntity connectedCart; public MountedContraption() { rotationMode = CartMovementMode.ROTATE; @@ -70,12 +75,34 @@ public class MountedContraption extends Contraption { protected Pair capture(World world, BlockPos pos) { Pair pair = super.capture(world, pos); BlockInfo capture = pair.getKey(); - if (AllBlocks.CART_ASSEMBLER.has(capture.state)) - return Pair.of(new BlockInfo(capture.pos, CartAssemblerBlock.createAnchor(capture.state), null), - pair.getValue()); + if (AllBlocks.CART_ASSEMBLER.has(capture.state)) { + if (!pos.equals(anchor)) { + for (Axis axis : Iterate.axes) { + if (axis.isVertical()) + continue; + if (VecHelper.onSameAxis(anchor, pos, axis) && connectedCart == null) { + for (AbstractMinecartEntity abstractMinecartEntity : world + .getEntitiesWithinAABB(AbstractMinecartEntity.class, new AxisAlignedBB(pos))) { + if (!CartAssemblerBlock.canAssembleTo(abstractMinecartEntity)) + break; + connectedCart = abstractMinecartEntity; + } + } + } + } + return Pair.of(new BlockInfo(pos, CartAssemblerBlock.createAnchor(capture.state), null), pair.getValue()); + } return pair; } + @Override + protected boolean movementAllowed(World world, BlockPos pos) { + BlockState blockState = world.getBlockState(pos); + if (!pos.equals(anchor) && AllBlocks.CART_ASSEMBLER.has(blockState)) + return true; + return super.movementAllowed(world, pos); + } + @Override public CompoundNBT writeNBT() { CompoundNBT writeNBT = super.writeNBT(); @@ -93,7 +120,7 @@ public class MountedContraption extends Contraption { protected boolean customBlockPlacement(IWorld world, BlockPos pos, BlockState state) { return AllBlocks.MINECART_ANCHOR.has(state); } - + @Override protected boolean customBlockRemoval(IWorld world, BlockPos pos, BlockState state) { return pos.equals(anchor); diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/ClientMinecartCouplingHandler.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/ClientMinecartCouplingHandler.java new file mode 100644 index 000000000..30a4fe4ee --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/ClientMinecartCouplingHandler.java @@ -0,0 +1,71 @@ +package com.simibubi.create.content.contraptions.components.structureMovement.train; + +import java.util.Random; + +import com.simibubi.create.AllItems; +import com.simibubi.create.foundation.networking.AllPackets; +import com.simibubi.create.foundation.utility.VecHelper; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.entity.player.ClientPlayerEntity; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.entity.item.minecart.AbstractMinecartEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.particles.IParticleData; +import net.minecraft.particles.ParticleTypes; +import net.minecraft.particles.RedstoneParticleData; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.Vec3d; + +public class ClientMinecartCouplingHandler { + + static AbstractMinecartEntity selectedCart; + static Random r = new Random(); + + public static void tick() { + if (selectedCart == null) + return; + spawnSelectionParticles(selectedCart.getBoundingBox(), false); + ClientPlayerEntity player = Minecraft.getInstance().player; + ItemStack heldItemMainhand = player.getHeldItemMainhand(); + ItemStack heldItemOffhand = player.getHeldItemOffhand(); + if (AllItems.MINECART_COUPLING.isIn(heldItemMainhand) || AllItems.MINECART_COUPLING.isIn(heldItemOffhand)) + return; + selectedCart = null; + } + + static void onCartClicked(PlayerEntity player, AbstractMinecartEntity entity) { + if (Minecraft.getInstance().player != player) + return; + if (selectedCart == null || selectedCart == entity) { + selectedCart = entity; + spawnSelectionParticles(selectedCart.getBoundingBox(), true); + return; + } + spawnSelectionParticles(entity.getBoundingBox(), true); + AllPackets.channel.sendToServer(new MinecartCouplingCreationPacket(selectedCart, entity)); + selectedCart = null; + } + + static void sneakClick() { + selectedCart = null; + } + + private static void spawnSelectionParticles(AxisAlignedBB axisAlignedBB, boolean highlight) { + ClientWorld world = Minecraft.getInstance().world; + Vec3d center = axisAlignedBB.getCenter(); + int amount = highlight ? 100 : 2; + IParticleData particleData = highlight ? ParticleTypes.END_ROD : new RedstoneParticleData(1, 1, 1, 1); + for (int i = 0; i < amount; i++) { + Vec3d v = VecHelper.offsetRandomly(Vec3d.ZERO, r, 1); + double yOffset = v.y; + v = v.mul(1, 0, 1) + .normalize() + .add(0, yOffset / 8f, 0) + .add(center); + world.addParticle(particleData, v.x, v.y, v.z, 0, 0, 0); + } + } + +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCoupling.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCoupling.java new file mode 100644 index 000000000..2f1241ad1 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCoupling.java @@ -0,0 +1,117 @@ +package com.simibubi.create.content.contraptions.components.structureMovement.train; + +import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import javax.annotation.Nullable; + +import com.simibubi.create.content.contraptions.components.structureMovement.train.MinecartCouplingSerializer.CouplingData; +import com.simibubi.create.foundation.utility.Couple; + +import net.minecraft.entity.item.minecart.AbstractMinecartEntity; +import net.minecraft.world.World; + +public class MinecartCoupling { + + WeakReference mainCart; + WeakReference connectedCart; + double length; + + private MinecartCoupling(AbstractMinecartEntity mainCart, AbstractMinecartEntity connectedCart, double length) { + this.mainCart = new WeakReference<>(mainCart); + this.connectedCart = new WeakReference<>(connectedCart); + this.length = length; + } + + public static MinecartCoupling create(AbstractMinecartEntity mainCart, AbstractMinecartEntity connectedCart) { + return new MinecartCoupling(mainCart, connectedCart, + Math.max(2, getDistanceBetweenCarts(mainCart, connectedCart))); + } + + @Nullable + public static List loadAllAttached(World world, AbstractMinecartEntity minecart) { + List loaded = new ArrayList<>(2); + List otherCartsInRange = + world.getEntitiesWithinAABB(AbstractMinecartEntity.class, minecart.getBoundingBox() + .grow(MinecartCouplingHandler.maxDistance()), c -> c != minecart); + + List couplingData = MinecartCouplingSerializer.getCouplingData(minecart); + Connections: for (CouplingData connection : couplingData) { + boolean cartIsMain = connection.main; + UUID cartCouplingUUID = connection.id; + + for (AbstractMinecartEntity otherCart : otherCartsInRange) { + List otherCouplingData = MinecartCouplingSerializer.getCouplingData(otherCart); + for (CouplingData otherConnection : otherCouplingData) { + boolean otherIsMain = otherConnection.main; + UUID otherCouplingUUID = otherConnection.id; + + if (cartIsMain == otherIsMain) + continue; + if (!otherCouplingUUID.equals(cartCouplingUUID)) + continue; + + AbstractMinecartEntity mainCart = cartIsMain ? minecart : otherCart; + AbstractMinecartEntity connectedCart = cartIsMain ? otherCart : minecart; + loaded.add(new MinecartCoupling(mainCart, connectedCart, connection.length)); + continue Connections; + } + } + } + + return loaded; + } + + public void writeToCarts() { + MinecartCouplingSerializer.removeCouplingFromCart(mainCart.get(), this); + MinecartCouplingSerializer.removeCouplingFromCart(connectedCart.get(), this); + + MinecartCouplingSerializer.addCouplingToCart(mainCart.get(), this); + MinecartCouplingSerializer.addCouplingToCart(connectedCart.get(), this); + } + + /** + * Swap main and connected cart for aliging couplings of a train.
+ * Changes are written to the carts' nbt data!
+ * Id of this coupling will change! + */ + public void flip() { + MinecartCouplingSerializer.removeCouplingFromCart(mainCart.get(), this); + MinecartCouplingSerializer.removeCouplingFromCart(connectedCart.get(), this); + + WeakReference oldMain = mainCart; + mainCart = connectedCart; + connectedCart = oldMain; + + MinecartCouplingSerializer.addCouplingToCart(mainCart.get(), this); + MinecartCouplingSerializer.addCouplingToCart(connectedCart.get(), this); + } + + public static double getDistanceBetweenCarts(AbstractMinecartEntity mainCart, + AbstractMinecartEntity connectedCart) { + return mainCart.getBoundingBox() + .getCenter() + .subtract(connectedCart.getBoundingBox() + .getCenter()) + .length(); + } + + public boolean areBothEndsPresent() { + return (mainCart.get() != null && mainCart.get() + .isAlive()) && (connectedCart.get() != null + && connectedCart.get() + .isAlive()); + } + + public UUID getId() { + return mainCart.get() + .getUniqueID(); + } + + public Couple asCouple() { + return Couple.create(mainCart.get(), connectedCart.get()); + } + +} \ No newline at end of file diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCouplingCreationPacket.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCouplingCreationPacket.java new file mode 100644 index 000000000..a382acc55 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCouplingCreationPacket.java @@ -0,0 +1,45 @@ +package com.simibubi.create.content.contraptions.components.structureMovement.train; + +import java.util.function.Supplier; + +import com.simibubi.create.foundation.networking.SimplePacketBase; + +import net.minecraft.entity.item.minecart.AbstractMinecartEntity; +import net.minecraft.entity.player.ServerPlayerEntity; +import net.minecraft.network.PacketBuffer; +import net.minecraftforge.fml.network.NetworkEvent.Context; + +public class MinecartCouplingCreationPacket extends SimplePacketBase { + + int id1, id2; + + public MinecartCouplingCreationPacket(AbstractMinecartEntity cart1, AbstractMinecartEntity cart2) { + id1 = cart1.getEntityId(); + id2 = cart2.getEntityId(); + } + + public MinecartCouplingCreationPacket(PacketBuffer buffer) { + id1 = buffer.readInt(); + id2 = buffer.readInt(); + } + + @Override + public void write(PacketBuffer buffer) { + buffer.writeInt(id1); + buffer.writeInt(id2); + } + + @Override + public void handle(Supplier context) { + context.get() + .enqueueWork(() -> { + ServerPlayerEntity sender = context.get() + .getSender(); + if (sender != null) + MinecartCouplingHandler.connectCarts(sender, sender.world, id1, id2); + }); + context.get() + .setPacketHandled(true); + } + +} \ No newline at end of file diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCouplingHandler.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCouplingHandler.java new file mode 100644 index 000000000..da611d089 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCouplingHandler.java @@ -0,0 +1,383 @@ +package com.simibubi.create.content.contraptions.components.structureMovement.train; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.UUID; + +import javax.annotation.Nullable; + +import org.apache.commons.lang3.tuple.Pair; + +import com.mojang.blaze3d.matrix.MatrixStack; +import com.simibubi.create.content.contraptions.components.structureMovement.train.MinecartCouplingSerializer.CouplingData; +import com.simibubi.create.foundation.config.AllConfigs; +import com.simibubi.create.foundation.networking.AllPackets; +import com.simibubi.create.foundation.utility.WorldAttached; + +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import it.unimi.dsi.fastutil.objects.ObjectLists; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.IRenderTypeBuffer; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.entity.Entity; +import net.minecraft.entity.item.minecart.AbstractMinecartEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.world.World; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.fml.network.PacketDistributor; + +/* + * + * Couplings are a directional connection of two Minecart entities + * - ID and Key is the UUID of the main cart + * - They are immediately written to both minecarts' nbt tags upon creation. + * {Main: true, Id: {L: ", M: "}, Length: 5} + * + * Trains are an ordered list of Couplings + * - ID and Key is the UUID of the main coupling + * - Every coupling is part of exactly one train, lonely couplings are still treated as such + * - When two trains are merged, the couplings have to be re-oriented to always point towards the main coupling + * + * Loaded carts are queued to be dealt with on world tick, + * so that the world functions are not accessed during the chunk deserialization + * + * Challenges: + * - Minecarts can only be corrected by changing their motion or their position + * - A Minecarts' motion vector does not represent its actual movement next tick + * - There is no accessible simulation step (can be copied / at'd) + * - It is not always known which cart is ahead/behind + * - If both ends keep a contant momentum, the link stress is not necessarily satisfied + * - Carts cannot be "dragged" directly towards resolving link stress; + * It is not entirely predictable how motions outside of the rail vector get projected + * + * + * + * Day III, couplings still too unstable. Why is that? What causes the instability, is it the general approach or a specific issue + * Explored strategies: + * + * Acellerate against violation diff -> perpetual motion, Jittering, bouncyness + * Brake and correct towards violation diff -> quick loss of momentum + * Move against diff -> de-rails carts on tricky paths + * + * Not yet explored: running an actual simulation step for the minecarts' movement. + * + * - satisfied link + * -- stretched link + * . shortened link + * ? not visible in ctx + * = cart + * => moving cart + * + * Create algorithm to find a tick order which maximizes resolved stress + * + * => ? <= ? = - => (@t) + * ^ tick here first + * + * cart[], motion[], position[] + * Predict through simulation + motion, that without any intervention, this happens: + * + * => ? <= ? = -- => (@t+1) + * + * Decision: Accelerate trailing? (free motion) + * Brake leading? (loss of momentum) + * -> Both? + * + * Soft collisions can always be resolved. Just have to adjust motions accordingly. + * Hard collisions should never be resolved by the soft/motion resolver, as it would generate or void momentum! + * + * Approach: Hard pass then soft pass. two iterations of the coupling list + * + * find starting point of hard resolve: the center of balance + * i from left, j from right + * compare and approach the exact center of the resolved chain. + * + * -3 -2-10v 0 + * 0-----0-0-0-0 + * 0--0--0--0--0 + * 2 1 + * 0---0-0---0---0--0--0-0-0-0-0 + * 0--0--0--0--0--0--0--0--0--0--0 + * + * v + * 0-0-0 + * 0--0--0 + * + * v + * 0---0---0---0 + * 0-0-0-0 + * + * -1 0 -1 0 + * 0-0---0--0---0-0 + * 0--0--0--0--0--0 + * + * + * + * iterate both ways from the center and resolve hard collisions. + * + * + * if coupling is NOT ok @t { + * Something unpredictable happened. + * Try to reach soft state asap. every lost cycle in hard will act strangely and render inconsistently + * Using motion to hard resolve is probably a bad idea + * + * if cart on rail -> force move along rail away from collision + * else straight-up push it + * use simulation to test if movements are possible + * + * hard resolves are usually quite tiny. If they go beyond 1m then something really messed up + * } + * + * if coupling could not be fixed { + * clear all motion of the two carts (?) + * A failed hard collision implies that the Minecarts cannot be forced together/apart, might aswell not make things worse + * } + * + * + * Soft collisions only mess with motion values. It is still good to find a good order of iteration- + * that way predictions of earlier couplings in the loop are still accurate + * + * =>>> - = - <= - =>> + * + * left to right + * =>> - = - => - => + * right to left + * =>> - => - = - => + * + * + * + * + * if now coupling is ok @t { + * + * Run Prediction I + * if (coupling is ok @t+1) + * my job here is done; return + * + * get required force to resolve (front or back) + * distribute equally over both carts + * + * Run Prediction II + * if (coupling is ok @t+1*) + * looks good; return + * + * re-distribute force to other cart + * all collisions should be fixed at this point. return; + * (in case of sudden changes/ bad predictions, the next cycle can handle the hard resolve) + * + * } + * + * + * + * NEXT STEPS + * 1. normalize diagonal rail vectors and debug all possible rail motion transfers. The required tools have to work properly + * 2. implement a prediction step + * 3. find a suitable hard collision resolver + * 4. find a suitable soft collision order + * + */ + +public class MinecartCouplingHandler { + + static WorldAttached> loadedCouplings = new WorldAttached<>(HashMap::new); + static WorldAttached> loadedTrains = new WorldAttached<>(HashMap::new); + + static WorldAttached> queuedCarts = + new WorldAttached<>(() -> ObjectLists.synchronize(new ObjectArrayList<>())); + + public static void connectCarts(@Nullable PlayerEntity player, World world, int cartId1, int cartId2) { + Entity entity1 = world.getEntityByID(cartId1); + Entity entity2 = world.getEntityByID(cartId2); + + if (!(entity1 instanceof AbstractMinecartEntity)) + return; + if (!(entity2 instanceof AbstractMinecartEntity)) + return; + if ((int) entity1.getPositionVec() + .distanceTo(entity2.getPositionVec()) > maxDistance()) + return; + + AbstractMinecartEntity cart1 = (AbstractMinecartEntity) entity1; + AbstractMinecartEntity cart2 = (AbstractMinecartEntity) entity2; + + if (alreadyCoupled(world, cart1, cart2)) + return; + + addCoupling(world, MinecartCoupling.create(cart1, cart2), false); + + if (world.isRemote) + return; + AllPackets.channel.send(PacketDistributor.TRACKING_ENTITY.with(() -> cart1), + new MinecartCouplingSyncPacket(cart1, cart2)); + } + + @OnlyIn(Dist.CLIENT) + public static void render(MatrixStack ms, IRenderTypeBuffer buffer) { + ClientWorld world = Minecraft.getInstance().world; + if (world == null) + return; + loadedCouplings.get(world) + .values() + .forEach(c -> MinecartCouplingRenderer.renderCoupling(ms, buffer, c)); + } + + public static void tick(World world) { + initQueuedCarts(world); + removeUnloadedCouplings(world); + loadedTrains.get(world) + .values() + .forEach(t -> t.tickCouplings(world)); + } + + private static void initQueuedCarts(World world) { + List queued = queuedCarts.get(world); + if (queued == null) + return; + for (AbstractMinecartEntity minecart : queued) + MinecartCoupling.loadAllAttached(world, minecart) + .forEach(c -> addCoupling(world, c, true)); + queued.clear(); + } + + private static void removeUnloadedCouplings(World world) { + List toRemove = new ArrayList<>(); + Map couplings = loadedCouplings.get(world); + if (couplings == null) + return; + for (Entry entry : couplings.entrySet()) + if (!entry.getValue() + .areBothEndsPresent()) + toRemove.add(entry.getKey()); + couplings.keySet() + .removeAll(toRemove); + } + + public static void handleAddedMinecart(Entity entity, World world) { + if (!(entity instanceof AbstractMinecartEntity)) + return; + if (world.isRemote) + queueLoadedMinecartClient(entity, world); + else + queueLoadedMinecart(entity, world); + } + + public static void queueLoadedMinecartClient(Entity entity, World world) { + AllPackets.channel.sendToServer(new PersistantDataPacketRequest(entity)); + } + + public static void queueLoadedMinecart(Entity entity, World world) { + AbstractMinecartEntity minecart = (AbstractMinecartEntity) entity; + CompoundNBT nbt = minecart.getPersistentData(); + if (!nbt.contains("Couplings")) + return; + queuedCarts.get(world) + .add(minecart); + } + + static int maxDistance() { + return AllConfigs.SERVER.kinetics.maxCartCouplingLength.get(); + } + + public static Pair getTrainIfComplete(World world, AbstractMinecartEntity minecart, + @Nullable UUID ignore) { + AbstractMinecartEntity current = minecart; + UUID trainId = current.getUniqueID(); + for (int i = 0; i < 100; i++) { + List couplingData = MinecartCouplingSerializer.getCouplingData(current); + for (CouplingData data : couplingData) { + if (data.main) + continue; + if (ignore != null && ignore.equals(data.id)) + continue; + trainId = data.id; + MinecartCoupling coupling = loadedCouplings.get(world) + .get(trainId); + + // Not fully loaded in + if (coupling == null) + return Pair.of(trainId, false); + + current = coupling.mainCart.get(); + } + } + + // Complete + return Pair.of(trainId, true); + } + + private static boolean alreadyCoupled(World world, AbstractMinecartEntity cart1, AbstractMinecartEntity cart2) { + Pair trainOf = getTrainIfComplete(world, cart1, null); + Pair trainOf2 = getTrainIfComplete(world, cart2, null); + return trainOf.getRight() && trainOf2.getRight() && trainOf.getLeft() + .equals(trainOf2.getLeft()); + } + + private static void addCoupling(World world, MinecartCoupling coupling, boolean loadedFromChunk) { + MinecartTrain train = new MinecartTrain(coupling); + Pair trainIdOfMain = getTrainIfComplete(world, coupling.mainCart.get(), null); + Pair trainIdOfConnected = + getTrainIfComplete(world, coupling.connectedCart.get(), loadedFromChunk ? coupling.getId() : null); + + // Something is not loaded + if (!loadedFromChunk && !(trainIdOfMain.getValue() && trainIdOfConnected.getValue())) + return; + + // Coupling was already loaded in + if (loadedFromChunk && loadedCouplings.get(world) + .containsKey(coupling.getId())) + return; + + if (!world.isRemote) { + Map trains = loadedTrains.get(world); + MinecartTrain trainOfMain = trains.get(trainIdOfMain.getKey()); + MinecartTrain trainOfConnected = trains.get(trainIdOfConnected.getKey()); + + // Connected cart is part of a train, merge it onto the newly created one + if (trainOfConnected != null) + trains.remove(trainIdOfConnected.getKey()) + .mergeOnto(world, train); + + // Main cart is part of a train, merge the newly created one onto it + boolean mainCartHasTrain = trainOfMain != null && trainIdOfMain.getKey() + .equals(coupling.getId()); + if (trainOfMain != null) { + if (mainCartHasTrain && !loadedFromChunk) + flipTrain(world, trainOfMain); + train.mergeOnto(world, trainOfMain); + train = null; + } + + // ...add the new train otherwise + if (train != null) + trains.put(train.getId(), train); + } + + loadedCouplings.get(world) + .put(coupling.getId(), coupling); + if (!loadedFromChunk) + coupling.writeToCarts(); + } + + public static void flipTrain(World world, MinecartTrain train) { + Map map = loadedTrains.get(world); + map.remove(train.getId()); + train.flip(world); + map.put(train.getId(), train); + } + + public static MinecartCoupling getCoupling(World world, UUID id) { + Map map = loadedCouplings.get(world); + return map.get(id); + } + + public static void flipCoupling(World world, MinecartCoupling coupling) { + Map map = loadedCouplings.get(world); + map.remove(coupling.getId()); + coupling.flip(); + map.put(coupling.getId(), coupling); + } + +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCouplingItem.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCouplingItem.java new file mode 100644 index 000000000..ba6f13fd7 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCouplingItem.java @@ -0,0 +1,54 @@ +package com.simibubi.create.content.contraptions.components.structureMovement.train; + +import com.simibubi.create.AllItems; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.item.minecart.AbstractMinecartEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.util.ActionResultType; +import net.minecraft.world.World; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.event.entity.player.PlayerInteractEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.DistExecutor; +import net.minecraftforge.fml.common.Mod.EventBusSubscriber; + +@EventBusSubscriber +public class MinecartCouplingItem extends Item { + + public MinecartCouplingItem(Properties p_i48487_1_) { + super(p_i48487_1_); + } + + @SubscribeEvent + public static void couplingItemCanBeUsedOnMinecarts(PlayerInteractEvent.EntityInteract event) { + Entity interacted = event.getTarget(); + if (!(interacted instanceof AbstractMinecartEntity)) + return; + AbstractMinecartEntity minecart = (AbstractMinecartEntity) interacted; + PlayerEntity player = event.getPlayer(); + if (player == null) + return; + ItemStack heldItem = player.getHeldItem(event.getHand()); + if (!AllItems.MINECART_COUPLING.isIn(heldItem)) + return; + + World world = event.getWorld(); + if (MinecartCouplingSerializer.getCouplingData(minecart).size() < 2) { + if (world != null && world.isRemote) + DistExecutor.runWhenOn(Dist.CLIENT, () -> () -> cartClicked(player, minecart)); + } + + event.setCanceled(true); + event.setCancellationResult(ActionResultType.SUCCESS); + } + + @OnlyIn(Dist.CLIENT) + private static void cartClicked(PlayerEntity player, AbstractMinecartEntity interacted) { + ClientMinecartCouplingHandler.onCartClicked(player, (AbstractMinecartEntity) interacted); + } + +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCouplingRenderer.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCouplingRenderer.java new file mode 100644 index 000000000..90fe48243 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCouplingRenderer.java @@ -0,0 +1,197 @@ +package com.simibubi.create.content.contraptions.components.structureMovement.train; + +import static net.minecraft.util.math.MathHelper.lerp; + +import com.mojang.blaze3d.matrix.MatrixStack; +import com.mojang.blaze3d.vertex.IVertexBuilder; +import com.simibubi.create.AllBlockPartials; +import com.simibubi.create.foundation.utility.Couple; +import com.simibubi.create.foundation.utility.MatrixStacker; +import com.simibubi.create.foundation.utility.SuperByteBuffer; +import com.simibubi.create.foundation.utility.VecHelper; + +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.IRenderTypeBuffer; +import net.minecraft.client.renderer.RenderType; +import net.minecraft.client.renderer.Vector3f; +import net.minecraft.client.renderer.WorldRenderer; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.entity.item.minecart.AbstractMinecartEntity; +import net.minecraft.util.Direction.Axis; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; + +public class MinecartCouplingRenderer { + + public static void renderCoupling(MatrixStack ms, IRenderTypeBuffer buffer, MinecartCoupling coupling) { + if (!coupling.areBothEndsPresent()) + return; + ClientWorld world = Minecraft.getInstance().world; + Couple carts = coupling.asCouple(); + Couple lightValues = + carts.map(c -> WorldRenderer.getLightmapCoordinates(world, new BlockPos(c.getBoundingBox() + .getCenter()))); + + Vec3d center = carts.getFirst() + .getPositionVec() + .add(carts.getSecond() + .getPositionVec()) + .scale(.5f); + + Couple transforms = carts.map(c -> getSuitableCartEndpoint(c, center)); + + BlockState renderState = Blocks.AIR.getDefaultState(); + IVertexBuilder builder = buffer.getBuffer(RenderType.getSolid()); + SuperByteBuffer attachment = AllBlockPartials.COUPLING_ATTACHMENT.renderOn(renderState); + SuperByteBuffer ring = AllBlockPartials.COUPLING_RING.renderOn(renderState); + SuperByteBuffer connector = AllBlockPartials.COUPLING_CONNECTOR.renderOn(renderState); + + Vec3d zero = Vec3d.ZERO; + Vec3d firstEndpoint = transforms.getFirst() + .apply(zero); + Vec3d secondEndpoint = transforms.getSecond() + .apply(zero); + Vec3d endPointDiff = secondEndpoint.subtract(firstEndpoint); + double connectorYaw = -Math.atan2(endPointDiff.z, endPointDiff.x) * 180.0D / Math.PI; + double connectorPitch = Math.atan2(endPointDiff.y, endPointDiff.mul(1, 0, 1) + .length()) * 180 / Math.PI; + + MatrixStacker msr = MatrixStacker.of(ms); + carts.forEachWithContext((cart, isFirst) -> { + CartEndpoint cartTransform = transforms.get(isFirst); + + ms.push(); + cartTransform.apply(ms); + attachment.light(lightValues.get(isFirst)) + .renderInto(ms, builder); + msr.rotateY(connectorYaw - cartTransform.yaw); + ring.light(lightValues.get(isFirst)) + .renderInto(ms, builder); + ms.pop(); + }); + + int l1 = lightValues.getFirst(); + int l2 = lightValues.getSecond(); + int meanBlockLight = (((l1 >> 4) & 0xf) + ((l2 >> 4) & 0xf)) / 2; + int meanSkyLight = (((l1 >> 20) & 0xf) + ((l2 >> 20) & 0xf)) / 2; + + ms.push(); + msr.translate(firstEndpoint) + .rotateY(connectorYaw) + .rotateZ(connectorPitch); + ms.scale((float) endPointDiff.length(), 1, 1); + + connector.light(meanSkyLight << 20 | meanBlockLight << 4) + .renderInto(ms, builder); + ms.pop(); + } + + private static CartEndpoint getSuitableCartEndpoint(AbstractMinecartEntity cart, Vec3d centerOfCoupling) { + long i = cart.getEntityId() * 493286711L; + i = i * i * 4392167121L + i * 98761L; + float x = (((float) (i >> 16 & 7L) + 0.5F) / 8.0F - 0.5F) * 0.004F; + float y = (((float) (i >> 20 & 7L) + 0.5F) / 8.0F - 0.5F) * 0.004F + 0.375F; + float z = (((float) (i >> 24 & 7L) + 0.5F) / 8.0F - 0.5F) * 0.004F; + + float pt = Minecraft.getInstance() + .getRenderPartialTicks(); + + double xIn = lerp(pt, cart.lastTickPosX, cart.getX()); + double yIn = lerp(pt, cart.lastTickPosY, cart.getY()); + double zIn = lerp(pt, cart.lastTickPosZ, cart.getZ()); + + float yaw = lerp(pt, cart.prevRotationYaw, cart.rotationYaw); + float pitch = lerp(pt, cart.prevRotationPitch, cart.rotationPitch); + float roll = cart.getRollingAmplitude() - pt; + + float rollAmplifier = cart.getDamage() - pt; + if (rollAmplifier < 0.0F) + rollAmplifier = 0.0F; + roll = roll > 0 ? MathHelper.sin(roll) * roll * rollAmplifier / 10.0F * cart.getRollingDirection() : 0; + + Vec3d positionVec = new Vec3d(xIn, yIn, zIn); + Vec3d frontVec = positionVec.add(VecHelper.rotate(new Vec3d(.5, 0, 0), 180 - yaw, Axis.Y)); + Vec3d backVec = positionVec.add(VecHelper.rotate(new Vec3d(-.5, 0, 0), 180 - yaw, Axis.Y)); + + Vec3d railVecOfPos = cart.getPos(xIn, yIn, zIn); + boolean flip = false; + + if (railVecOfPos != null) { + frontVec = cart.getPosOffset(xIn, yIn, zIn, (double) 0.3F); + backVec = cart.getPosOffset(xIn, yIn, zIn, (double) -0.3F); + if (frontVec == null) + frontVec = railVecOfPos; + if (backVec == null) + backVec = railVecOfPos; + + x += railVecOfPos.x; + y += (frontVec.y + backVec.y) / 2; + z += railVecOfPos.z; + + Vec3d endPointDiff = backVec.add(-frontVec.x, -frontVec.y, -frontVec.z); + if (endPointDiff.length() != 0.0D) { + endPointDiff = endPointDiff.normalize(); + yaw = (float) (Math.atan2(endPointDiff.z, endPointDiff.x) * 180.0D / Math.PI); + pitch = (float) (Math.atan(endPointDiff.y) * 73.0D); + } + } else { + x += xIn; + y += yIn; + z += zIn; + } + + final float offsetMagnitude = 13 / 16f; + boolean isBackFaceCloser = + frontVec.squareDistanceTo(centerOfCoupling) > backVec.squareDistanceTo(centerOfCoupling); + flip = isBackFaceCloser; + float offset = isBackFaceCloser ? -offsetMagnitude : offsetMagnitude; + + return new CartEndpoint(x, y + 2 / 16f, z, 180 - yaw, -pitch, roll, offset, flip); + } + + static class CartEndpoint { + + float x; + float y; + float z; + float yaw; + float pitch; + float roll; + float offset; + boolean flip; + + public CartEndpoint(float x, float y, float z, float yaw, float pitch, float roll, float offset, boolean flip) { + this.x = x; + this.y = y; + this.z = z; + this.yaw = yaw; + this.pitch = pitch; + this.roll = roll; + this.offset = offset; + this.flip = flip; + } + + public Vec3d apply(Vec3d vec) { + vec = vec.add(offset, 0, 0); + vec = VecHelper.rotate(vec, roll, Axis.X); + vec = VecHelper.rotate(vec, pitch, Axis.Z); + vec = VecHelper.rotate(vec, yaw, Axis.Y); + return vec.add(x, y, z); + } + + public void apply(MatrixStack ms) { + ms.translate(x, y, z); + ms.multiply(Vector3f.POSITIVE_Y.getDegreesQuaternion(yaw)); + ms.multiply(Vector3f.POSITIVE_Z.getDegreesQuaternion(pitch)); + ms.multiply(Vector3f.POSITIVE_X.getDegreesQuaternion(roll)); + ms.translate(offset, 0, 0); + if (flip) + ms.multiply(Vector3f.POSITIVE_Y.getDegreesQuaternion(180)); + } + + } + +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCouplingSerializer.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCouplingSerializer.java new file mode 100644 index 000000000..1743a5de8 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCouplingSerializer.java @@ -0,0 +1,65 @@ +package com.simibubi.create.content.contraptions.components.structureMovement.train; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import com.simibubi.create.foundation.utility.NBTHelper; + +import net.minecraft.entity.item.minecart.AbstractMinecartEntity; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.nbt.ListNBT; +import net.minecraft.nbt.NBTUtil; +import net.minecraftforge.common.util.Constants.NBT; + +public class MinecartCouplingSerializer { + + public static void addCouplingToCart(AbstractMinecartEntity minecart, MinecartCoupling coupling) { + CompoundNBT nbt = minecart.getPersistentData(); + ListNBT couplingList = nbt.getList("Couplings", NBT.TAG_COMPOUND); + boolean main = coupling.mainCart.get() == minecart; + couplingList.add(createCouplingTag(main, coupling)); + nbt.put("Couplings", couplingList); + } + + public static void removeCouplingFromCart(AbstractMinecartEntity minecart, MinecartCoupling coupling) { + CompoundNBT nbt = minecart.getPersistentData(); + ListNBT couplingList = nbt.getList("Couplings", NBT.TAG_COMPOUND); + couplingList.removeIf(inbt -> coupling.getId() + .equals(NBTUtil.readUniqueId(((CompoundNBT) inbt).getCompound("Id")))); + nbt.put("Couplings", couplingList); + } + + private static CompoundNBT createCouplingTag(boolean main, MinecartCoupling coupling) { + CompoundNBT nbt = new CompoundNBT(); + nbt.put("Id", NBTUtil.writeUniqueId(coupling.getId())); + nbt.putBoolean("Main", main); + nbt.putDouble("Length", coupling.length); + return nbt; + } + + public static List getCouplingData(AbstractMinecartEntity minecart) { + List list = new ArrayList<>(); + CompoundNBT nbt = minecart.getPersistentData(); + NBTHelper.iterateCompoundList(nbt.getList("Couplings", NBT.TAG_COMPOUND), c -> { + boolean main = c.getBoolean("Main"); + UUID id = NBTUtil.readUniqueId(c.getCompound("Id")); + double length = c.getDouble("Length"); + list.add(new CouplingData(main, id, length)); + }); + return list; + } + + static class CouplingData { + boolean main; + UUID id; + double length; + + public CouplingData(boolean main, UUID id, double length) { + this.main = main; + this.id = id; + this.length = length; + } + } + +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCouplingSyncPacket.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCouplingSyncPacket.java new file mode 100644 index 000000000..293a41003 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartCouplingSyncPacket.java @@ -0,0 +1,31 @@ +package com.simibubi.create.content.contraptions.components.structureMovement.train; + +import java.util.function.Supplier; + +import net.minecraft.client.Minecraft; +import net.minecraft.entity.item.minecart.AbstractMinecartEntity; +import net.minecraft.network.PacketBuffer; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.fml.network.NetworkEvent.Context; + +public class MinecartCouplingSyncPacket extends MinecartCouplingCreationPacket { + + public MinecartCouplingSyncPacket(AbstractMinecartEntity cart1, AbstractMinecartEntity cart2) { + super(cart1, cart2); + } + + public MinecartCouplingSyncPacket(PacketBuffer buffer) { + super(buffer); + } + + @Override + @OnlyIn(Dist.CLIENT) + public void handle(Supplier context) { + context.get() + .enqueueWork(() -> MinecartCouplingHandler.connectCarts(null, Minecraft.getInstance().world, id1, id2)); + context.get() + .setPacketHandled(true); + } + +} \ No newline at end of file diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartSim2020.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartSim2020.java new file mode 100644 index 000000000..6e6e083ac --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartSim2020.java @@ -0,0 +1,224 @@ +package com.simibubi.create.content.contraptions.components.structureMovement.train; + +import static net.minecraft.entity.Entity.horizontalMag; + +import java.util.List; +import java.util.Map; + +import com.google.common.collect.Maps; +import com.mojang.datafixers.util.Pair; +import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionEntity; + +import net.minecraft.block.AbstractRailBlock; +import net.minecraft.block.BlockState; +import net.minecraft.entity.Entity; +import net.minecraft.entity.item.minecart.AbstractMinecartEntity; +import net.minecraft.entity.item.minecart.ContainerMinecartEntity; +import net.minecraft.entity.item.minecart.FurnaceMinecartEntity; +import net.minecraft.inventory.container.Container; +import net.minecraft.state.properties.RailShape; +import net.minecraft.util.Direction; +import net.minecraft.util.Util; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; +import net.minecraft.util.math.Vec3i; + +public class MinecartSim2020 { + + private static final Map> MATRIX = + Util.make(Maps.newEnumMap(RailShape.class), (p_226574_0_) -> { + Vec3i vec3i = Direction.WEST.getDirectionVec(); + Vec3i vec3i1 = Direction.EAST.getDirectionVec(); + Vec3i vec3i2 = Direction.NORTH.getDirectionVec(); + Vec3i vec3i3 = Direction.SOUTH.getDirectionVec(); + Vec3i vec3i4 = vec3i.down(); + Vec3i vec3i5 = vec3i1.down(); + Vec3i vec3i6 = vec3i2.down(); + Vec3i vec3i7 = vec3i3.down(); + p_226574_0_.put(RailShape.NORTH_SOUTH, Pair.of(vec3i2, vec3i3)); + p_226574_0_.put(RailShape.EAST_WEST, Pair.of(vec3i, vec3i1)); + p_226574_0_.put(RailShape.ASCENDING_EAST, Pair.of(vec3i4, vec3i1)); + p_226574_0_.put(RailShape.ASCENDING_WEST, Pair.of(vec3i, vec3i5)); + p_226574_0_.put(RailShape.ASCENDING_NORTH, Pair.of(vec3i2, vec3i7)); + p_226574_0_.put(RailShape.ASCENDING_SOUTH, Pair.of(vec3i6, vec3i3)); + p_226574_0_.put(RailShape.SOUTH_EAST, Pair.of(vec3i3, vec3i1)); + p_226574_0_.put(RailShape.SOUTH_WEST, Pair.of(vec3i3, vec3i)); + p_226574_0_.put(RailShape.NORTH_WEST, Pair.of(vec3i2, vec3i)); + p_226574_0_.put(RailShape.NORTH_EAST, Pair.of(vec3i2, vec3i1)); + }); + + public static Vec3d predictMotionOf(AbstractMinecartEntity cart) { + + if (cart instanceof FurnaceMinecartEntity) { + return cart.getPositionVec() + .subtract(cart.lastTickPosX, cart.lastTickPosY, cart.lastTickPosZ); + } + if (cart instanceof ContainerMinecartEntity) { + ContainerMinecartEntity containerCart = (ContainerMinecartEntity) cart; + float f = 0.98F; + if (containerCart.isEmpty()) + return cart.getMotion() + .mul(f, 0.0D, f); + int i = 15 - Container.calcRedstoneFromInventory(containerCart); + f += (float) i * 0.001F; + return cart.getMotion() + .mul(f, 0.0D, f); + } + return cart.getMotion() + .scale(cart.isBeingRidden() ? 0.997D : 0.96D); + } + + public static boolean canAddMotion(AbstractMinecartEntity c) { + if (c instanceof FurnaceMinecartEntity) + return MathHelper.epsilonEquals(((FurnaceMinecartEntity) c).pushX, 0) + && MathHelper.epsilonEquals(((FurnaceMinecartEntity) c).pushZ, 0); + List passengers = c.getPassengers(); + if (passengers.isEmpty()) + return true; + for (Entity entity : passengers) { + if (entity instanceof ContraptionEntity) { + ContraptionEntity contraptionEntity = (ContraptionEntity) entity; + return !contraptionEntity.isStalled(); + } + } + return true; + } + + public static void moveCartAlongTrack(AbstractMinecartEntity cart, Vec3d forcedMovement, BlockPos cartPos, + BlockState trackState) { + + if (forcedMovement.equals(Vec3d.ZERO)) + return; + + Vec3d previousMotion = cart.getMotion(); + cart.fallDistance = 0.0F; + + double x = cart.getX(); + double y = cart.getY(); + double z = cart.getZ(); + + double actualX = x; + double actualY = y; + double actualZ = z; + + Vec3d actualVec = cart.getPos(actualX, actualY, actualZ); + actualY = cartPos.getY() + 1; + + AbstractRailBlock abstractrailblock = (AbstractRailBlock) trackState.getBlock(); + RailShape railshape = abstractrailblock.getRailDirection(trackState, cart.world, cartPos, cart); + switch (railshape) { + case ASCENDING_EAST: + forcedMovement = forcedMovement.add(-1 * cart.getSlopeAdjustment(), 0.0D, 0.0D); + actualY++; + break; + case ASCENDING_WEST: + forcedMovement = forcedMovement.add(cart.getSlopeAdjustment(), 0.0D, 0.0D); + actualY++; + break; + case ASCENDING_NORTH: + forcedMovement = forcedMovement.add(0.0D, 0.0D, cart.getSlopeAdjustment()); + actualY++; + break; + case ASCENDING_SOUTH: + forcedMovement = forcedMovement.add(0.0D, 0.0D, -1 * cart.getSlopeAdjustment()); + actualY++; + default: + break; + } + + Pair pair = MATRIX.get(railshape); + Vec3i vec3i = pair.getFirst(); + Vec3i vec3i1 = pair.getSecond(); + double d4 = (double) (vec3i1.getX() - vec3i.getX()); + double d5 = (double) (vec3i1.getZ() - vec3i.getZ()); +// double d6 = Math.sqrt(d4 * d4 + d5 * d5); + double d7 = forcedMovement.x * d4 + forcedMovement.z * d5; + if (d7 < 0.0D) { + d4 = -d4; + d5 = -d5; + } + + double d23 = (double) cartPos.getX() + 0.5D + (double) vec3i.getX() * 0.5D; + double d10 = (double) cartPos.getZ() + 0.5D + (double) vec3i.getZ() * 0.5D; + double d12 = (double) cartPos.getX() + 0.5D + (double) vec3i1.getX() * 0.5D; + double d13 = (double) cartPos.getZ() + 0.5D + (double) vec3i1.getZ() * 0.5D; + d4 = d12 - d23; + d5 = d13 - d10; + double d14; + if (d4 == 0.0D) { + d14 = actualZ - (double) cartPos.getZ(); + } else if (d5 == 0.0D) { + d14 = actualX - (double) cartPos.getX(); + } else { + double d15 = actualX - d23; + double d16 = actualZ - d10; + d14 = (d15 * d4 + d16 * d5) * 2.0D; + } + + actualX = d23 + d4 * d14; + actualZ = d10 + d5 * d14; + + cart.setPosition(actualX, actualY, actualZ); + cart.setMotion(forcedMovement); + cart.moveMinecartOnRail(cartPos); + + x = cart.getX(); + y = cart.getY(); + z = cart.getZ(); + + if (vec3i.getY() != 0 && MathHelper.floor(x) - cartPos.getX() == vec3i.getX() + && MathHelper.floor(z) - cartPos.getZ() == vec3i.getZ()) { + cart.setPosition(x, y + (double) vec3i.getY(), z); + } else if (vec3i1.getY() != 0 && MathHelper.floor(x) - cartPos.getX() == vec3i1.getX() + && MathHelper.floor(z) - cartPos.getZ() == vec3i1.getZ()) { + cart.setPosition(x, y + (double) vec3i1.getY(), z); + } + + x = cart.getX(); + y = cart.getY(); + z = cart.getZ(); + + Vec3d vec3d3 = cart.getPos(x, y, z); + if (vec3d3 != null && actualVec != null) { + double d17 = (actualVec.y - vec3d3.y) * 0.05D; + Vec3d vec3d4 = cart.getMotion(); + double d18 = Math.sqrt(horizontalMag(vec3d4)); + if (d18 > 0.0D) { + cart.setMotion(vec3d4.mul((d18 + d17) / d18, 1.0D, (d18 + d17) / d18)); + } + + cart.setPosition(x, vec3d3.y, z); + } + + x = cart.getX(); + y = cart.getY(); + z = cart.getZ(); + + int j = MathHelper.floor(x); + int i = MathHelper.floor(z); + if (j != cartPos.getX() || i != cartPos.getZ()) { + Vec3d vec3d5 = cart.getMotion(); + double d26 = Math.sqrt(horizontalMag(vec3d5)); + cart.setMotion(d26 * (double) (j - cartPos.getX()), vec3d5.y, d26 * (double) (i - cartPos.getZ())); + } + + cart.setMotion(previousMotion); + + if (cart instanceof FurnaceMinecartEntity) { +// FurnaceMinecartEntity furnaceCart = (FurnaceMinecartEntity) cart; +// Vec3d vec3d = cart.getMotion(); +// double d2 = horizontalMag(vec3d); +// double d3 = furnaceCart.pushX * furnaceCart.pushX + furnaceCart.pushZ * furnaceCart.pushZ; +// if (d3 > 1.0E-4D && d2 > 0.001D) { +// double d40 = (double) MathHelper.sqrt(d2); +// double d50 = (double) MathHelper.sqrt(d3); +// furnaceCart.pushX = vec3d.x / d40 * d50; +// furnaceCart.pushZ = vec3d.z / d40 * d50; +// furnaceCart.setMotion(vec3d.mul(0.8D, 0.0D, 0.8D) +// .add(furnaceCart.pushX, 0.0D, furnaceCart.pushZ)); +// } + } + } + +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartTrain.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartTrain.java new file mode 100644 index 000000000..987e73920 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/MinecartTrain.java @@ -0,0 +1,367 @@ +package com.simibubi.create.content.contraptions.components.structureMovement.train; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.UUID; + +import com.simibubi.create.CreateClient; +import com.simibubi.create.foundation.utility.ColorHelper; +import com.simibubi.create.foundation.utility.Couple; +import com.simibubi.create.foundation.utility.Iterate; +import com.simibubi.create.foundation.utility.VecHelper; + +import net.minecraft.block.AbstractRailBlock; +import net.minecraft.block.BlockState; +import net.minecraft.entity.Entity; +import net.minecraft.entity.MoverType; +import net.minecraft.entity.item.minecart.AbstractMinecartEntity; +import net.minecraft.state.properties.RailShape; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; + +public class MinecartTrain { + + protected ArrayList couplings; + protected double momentum; + boolean complete; + + public MinecartTrain(MinecartCoupling coupling) { + couplings = new ArrayList<>(); + couplings.add(coupling); + tickOrder = 1; // start at most stressed + } + + public void flip(World world) { + Collections.reverse(couplings); + couplings.forEach(c -> MinecartCouplingHandler.flipCoupling(world, c)); + } + + public void mergeOnto(World world, MinecartTrain other) { + AbstractMinecartEntity trailingOfOther = other.couplings.get(other.couplings.size() - 1).connectedCart.get(); + AbstractMinecartEntity leadingOfThis = couplings.get(0).mainCart.get(); + + if (trailingOfOther != leadingOfThis) + flip(world); + + other.couplings.addAll(couplings); + } + + public int tickOrder; + + public void tickCouplings(World world) { + + // SOFT collision - modify motion of carts with stressed links @t+1 + double sharedMotion = 0; + int participants = 0; + for (int i = 0; i < couplings.size(); i++) { + MinecartCoupling minecartCoupling = couplings.get(i); + boolean last = i + 1 == couplings.size(); + if (!minecartCoupling.areBothEndsPresent()) + continue; + participants++; + sharedMotion += minecartCoupling.mainCart.get() + .getMotion() + .length(); + + if (last) { + participants++; + sharedMotion += minecartCoupling.connectedCart.get() + .getMotion() + .length(); + } + } + + if (participants == 0) + return; + + sharedMotion /= participants; + + /* + * Tick order testing: 0: start from motion outlier 1: start at most stressed + * coupling 2: start at front 3: start at back + */ + + if (tickOrder == 0) { + // Iterate starting from biggest outlier in motion + double maxDiff = 0; + int argMax = 0; + for (int i = 0; i < couplings.size(); i++) { + MinecartCoupling minecartCoupling = couplings.get(i); + boolean last = i + 1 == couplings.size(); + if (!minecartCoupling.areBothEndsPresent()) + continue; + + double diff = Math.abs(minecartCoupling.mainCart.get() + .getMotion() + .length() - sharedMotion); + if (diff > maxDiff) { + maxDiff = diff; + argMax = i; + } + + if (last) { + diff = Math.abs(minecartCoupling.connectedCart.get() + .getMotion() + .length() - sharedMotion); + if (diff > maxDiff) { + maxDiff = diff; + argMax = i; + } + } + } + + for (boolean hard : Iterate.trueAndFalse) { + for (int i = argMax - 1; i >= 0; i--) + if (couplings.get(i) + .areBothEndsPresent()) + collisionStep(world, couplings.get(i) + .asCouple() + .swap(), couplings.get(i).length, hard); + for (int i = argMax; i < couplings.size(); i++) + if (couplings.get(i) + .areBothEndsPresent()) + collisionStep(world, couplings.get(i) + .asCouple() + .swap(), couplings.get(i).length, hard); + } + return; + } + + if (tickOrder == 1) { + // Iterate starting from biggest stress + double maxStress = 0; + int argMax = 0; + for (int i = 0; i < couplings.size(); i++) { + MinecartCoupling minecartCoupling = couplings.get(i); + if (!minecartCoupling.areBothEndsPresent()) + continue; + double stress = getStressOfCoupling(minecartCoupling); + if (stress > maxStress) { + maxStress = stress; + argMax = i; + } + } + + for (boolean hard : Iterate.trueAndFalse) { + for (int i = argMax - 1; i >= 0; i--) + if (couplings.get(i) + .areBothEndsPresent()) + collisionStep(world, couplings.get(i) + .asCouple() + .swap(), couplings.get(i).length, hard); + for (int i = argMax; i < couplings.size(); i++) + if (couplings.get(i) + .areBothEndsPresent()) + collisionStep(world, couplings.get(i) + .asCouple() + .swap(), couplings.get(i).length, hard); + } + return; + } + + if (momentum >= 0 == (tickOrder == 2)) { + // Iterate front to back + for (boolean hard : Iterate.trueAndFalse) + for (int i = 0; i < couplings.size(); i++) + if (couplings.get(i) + .areBothEndsPresent()) + collisionStep(world, couplings.get(i) + .asCouple() + .swap(), couplings.get(i).length, hard); + + } else { + // Iterate back to front + for (boolean hard : Iterate.trueAndFalse) + for (int i = couplings.size() - 1; i >= 0; i--) + if (couplings.get(i) + .areBothEndsPresent()) + collisionStep(world, couplings.get(i) + .asCouple() + .swap(), couplings.get(i).length, hard); + } + + } + + private float getStressOfCoupling(MinecartCoupling coupling) { + if (!coupling.areBothEndsPresent()) + return 0; + return (float) (coupling.length - coupling.mainCart.get() + .getPositionVec() + .distanceTo(coupling.connectedCart.get() + .getPositionVec())); + } + + public void collisionStep(World world, Couple carts, double couplingLength, boolean hard) { + if (hard) + hardCollisionStep(world, carts, couplingLength); + else + softCollisionStep(world, carts, couplingLength); + } + + public void hardCollisionStep(World world, Couple carts, double couplingLength) { + Couple corrections = Couple.create(null, null); + Couple maxSpeed = carts.map(AbstractMinecartEntity::getMaxCartSpeedOnRail); + boolean firstLoop = true; + for (boolean current : new boolean[] { true, false, true }) { + AbstractMinecartEntity cart = carts.get(current); + AbstractMinecartEntity otherCart = carts.get(!current); + + float stress = (float) (couplingLength - cart.getPositionVec() + .distanceTo(otherCart.getPositionVec())); + + RailShape shape = null; + BlockPos railPosition = cart.getCurrentRailPosition(); + BlockState railState = world.getBlockState(railPosition.up()); + + if (railState.getBlock() instanceof AbstractRailBlock) { + AbstractRailBlock block = (AbstractRailBlock) railState.getBlock(); + shape = block.getRailDirection(railState, world, railPosition, cart); + } + + Vec3d correction = Vec3d.ZERO; + Vec3d pos = cart.getPositionVec(); + Vec3d link = otherCart.getPositionVec() + .subtract(pos); + float correctionMagnitude = firstLoop ? -stress / 2f : -stress; + correction = shape != null ? followLinkOnRail(link, pos, correctionMagnitude, shape).subtract(pos) + : link.normalize() + .scale(correctionMagnitude); + + float maxResolveSpeed = 1.75f; + correction = VecHelper.clamp(correction, Math.min(maxResolveSpeed, maxSpeed.get(current))); + + if (corrections.get(current) == null) + corrections.set(current, correction); + + if (shape != null) + MinecartSim2020.moveCartAlongTrack(cart, correction, railPosition, railState); + else { + cart.move(MoverType.SELF, correction); + cart.setMotion(cart.getMotion() + .scale(0.5f)); + } + firstLoop = false; + } + } + + public void softCollisionStep(World world, Couple carts, double couplingLength) { + + Couple positions = carts.map(Entity::getPositionVector); + Couple maxSpeed = carts.map(AbstractMinecartEntity::getMaxCartSpeedOnRail); + Couple canAddmotion = carts.map(MinecartSim2020::canAddMotion); + + Couple shapes = carts.map(current -> { + BlockPos railPosition = current.getCurrentRailPosition(); + BlockState railState = world.getBlockState(railPosition.up()); + if (!(railState.getBlock() instanceof AbstractRailBlock)) + return null; + AbstractRailBlock block = (AbstractRailBlock) railState.getBlock(); + return block.getRailDirection(railState, world, railPosition, current); + }); + + Couple motions = carts.map(MinecartSim2020::predictMotionOf); + Couple nextPositions = positions.copy(); + nextPositions.replaceWithParams(Vec3d::add, motions); + + float futureStress = (float) (couplingLength - nextPositions.getFirst() + .distanceTo(nextPositions.getSecond())); + if (Math.abs(futureStress) < 1 / 128f) + return; + + for (boolean current : Iterate.trueAndFalse) { + Vec3d correction = Vec3d.ZERO; + Vec3d pos = nextPositions.get(current); + Vec3d link = nextPositions.get(!current) + .subtract(pos); + float correctionMagnitude = -futureStress / 2f; + + if (canAddmotion.get(current) != canAddmotion.get(!current)) + correctionMagnitude = !canAddmotion.get(current) ? 0 : correctionMagnitude * 2; + + RailShape shape = shapes.get(current); + correction = shape != null ? followLinkOnRail(link, pos, correctionMagnitude, shape).subtract(pos) + : link.normalize() + .scale(correctionMagnitude); + correction = VecHelper.clamp(correction, maxSpeed.get(current)); + motions.set(current, motions.get(current) + .add(correction)); + } + + motions.replaceWithParams(VecHelper::clamp, maxSpeed); + carts.forEachWithParams(Entity::setMotion, motions); + } + + public static Vec3d followLinkOnRail(Vec3d link, Vec3d cart, float diffToReduce, RailShape shape) { + Vec3d railAxis = getRailVec(shape); + double dotProduct = railAxis.dotProduct(link); + if (Double.isNaN(dotProduct) || dotProduct == 0 || diffToReduce == 0) + return cart; + + Vec3d axis = railAxis.scale(-Math.signum(dotProduct)); + Vec3d center = cart.add(link); + double radius = link.length() - diffToReduce; + Vec3d intersectSphere = VecHelper.intersectSphere(cart, axis, center, radius); + + // Cannot satisfy on current rail vector + if (intersectSphere == null) + return cart.add(VecHelper.project(link, axis)); + + return intersectSphere; + } + + private static Vec3d getRailVec(RailShape shape) { + switch (shape) { + case ASCENDING_NORTH: + case ASCENDING_SOUTH: + case NORTH_SOUTH: + return new Vec3d(0, 0, 1); + case ASCENDING_EAST: + case ASCENDING_WEST: + case EAST_WEST: + return new Vec3d(1, 0, 0); + case NORTH_EAST: + case SOUTH_WEST: + return new Vec3d(1, 0, 1).normalize(); + case NORTH_WEST: + case SOUTH_EAST: + return new Vec3d(1, 0, -1).normalize(); + default: + return new Vec3d(0, 1, 0); + } + } + + public UUID getId() { + return couplings.get(0) + .getId(); + } + + public static void doDebugRender(World world, MinecartCoupling coupling, int index) { + AbstractMinecartEntity mainCart = coupling.mainCart.get(); + AbstractMinecartEntity connectedCart = coupling.connectedCart.get(); + + if (!coupling.areBothEndsPresent()) + return; + + int yOffset = 1; + Vec3d mainCenter = mainCart.getPositionVec() + .add(0, yOffset, 0); + Vec3d connectedCenter = connectedCart.getPositionVec() + .add(0, yOffset, 0); + + int color = ColorHelper.mixColors(0xabf0e9, 0xee8572, + (float) MathHelper.clamp(Math.abs(coupling.length - connectedCenter.distanceTo(mainCenter)) * 8, 0, 1)); + + CreateClient.outliner.showLine(coupling + "" + index, mainCenter, connectedCenter) + .colored(color) + .lineWidth(1 / 8f); + + Vec3d point = mainCart.getPositionVec() + .add(0, yOffset, 0); + CreateClient.outliner.showLine(coupling.getId() + "" + index, point, point.add(0, 1 / 128f, 0)) + .colored(0xffffff) + .lineWidth(1 / 4f); + } + +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/PersistantDataPacket.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/PersistantDataPacket.java new file mode 100644 index 000000000..1504b6200 --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/PersistantDataPacket.java @@ -0,0 +1,53 @@ +package com.simibubi.create.content.contraptions.components.structureMovement.train; + +import java.util.function.Supplier; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.entity.Entity; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.network.PacketBuffer; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.fml.network.NetworkEvent.Context; + +public class PersistantDataPacket extends PersistantDataPacketRequest { + + CompoundNBT persistentData; + + public PersistantDataPacket(Entity entity) { + super(entity); + persistentData = entity.getPersistentData(); + } + + public PersistantDataPacket(PacketBuffer buffer) { + super(buffer); + persistentData = buffer.readCompoundTag(); + } + + @Override + public void write(PacketBuffer buffer) { + super.write(buffer); + buffer.writeCompoundTag(persistentData); + } + + @Override + @OnlyIn(Dist.CLIENT) + public void handle(Supplier context) { + context.get() + .enqueueWork(() -> { + ClientWorld world = Minecraft.getInstance().world; + if (world == null) + return; + Entity entityByID = world.getEntityByID(entityId); + if (entityByID == null) + return; + CompoundNBT persistentData = entityByID.getPersistentData(); + persistentData.merge(this.persistentData); + MinecartCouplingHandler.queueLoadedMinecart(entityByID, world); + }); + context.get() + .setPacketHandled(true); + } + +} diff --git a/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/PersistantDataPacketRequest.java b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/PersistantDataPacketRequest.java new file mode 100644 index 000000000..b641cee8c --- /dev/null +++ b/src/main/java/com/simibubi/create/content/contraptions/components/structureMovement/train/PersistantDataPacketRequest.java @@ -0,0 +1,49 @@ +package com.simibubi.create.content.contraptions.components.structureMovement.train; + +import java.util.function.Supplier; + +import com.simibubi.create.foundation.networking.AllPackets; +import com.simibubi.create.foundation.networking.SimplePacketBase; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.ServerPlayerEntity; +import net.minecraft.network.PacketBuffer; +import net.minecraftforge.fml.network.NetworkEvent.Context; +import net.minecraftforge.fml.network.PacketDistributor; + +public class PersistantDataPacketRequest extends SimplePacketBase { + + int entityId; + + public PersistantDataPacketRequest(Entity entity) { + entityId = entity.getEntityId(); + } + + public PersistantDataPacketRequest(PacketBuffer buffer) { + entityId = buffer.readInt(); + } + + @Override + public void write(PacketBuffer buffer) { + buffer.writeInt(entityId); + } + + @Override + public void handle(Supplier context) { + context.get() + .enqueueWork(() -> { + ServerPlayerEntity sender = context.get() + .getSender(); + if (sender == null || sender.world == null) + return; + Entity entityByID = sender.world.getEntityByID(entityId); + if (entityByID == null) + return; + AllPackets.channel.send(PacketDistributor.PLAYER.with(() -> sender), + new PersistantDataPacket(entityByID)); + }); + context.get() + .setPacketHandled(true); + } + +} \ No newline at end of file diff --git a/src/main/java/com/simibubi/create/events/ClientEvents.java b/src/main/java/com/simibubi/create/events/ClientEvents.java index e3a3fc2c7..2b1e9f27e 100644 --- a/src/main/java/com/simibubi/create/events/ClientEvents.java +++ b/src/main/java/com/simibubi/create/events/ClientEvents.java @@ -7,15 +7,27 @@ import com.mojang.blaze3d.matrix.MatrixStack; import com.simibubi.create.Create; import com.simibubi.create.CreateClient; import com.simibubi.create.content.contraptions.KineticDebugger; -import com.simibubi.create.content.contraptions.base.KineticTileEntityRenderer; +import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionCollider; +import com.simibubi.create.content.contraptions.components.structureMovement.chassis.ChassisRangeDisplay; +import com.simibubi.create.content.contraptions.components.structureMovement.train.ClientMinecartCouplingHandler; +import com.simibubi.create.content.contraptions.components.structureMovement.train.MinecartCouplingHandler; import com.simibubi.create.content.contraptions.components.turntable.TurntableHandler; +import com.simibubi.create.content.contraptions.relays.belt.item.BeltConnectorHandler; +import com.simibubi.create.content.curiosities.tools.ExtendoGripRenderHandler; +import com.simibubi.create.content.curiosities.zapper.ZapperRenderHandler; +import com.simibubi.create.content.curiosities.zapper.blockzapper.BlockzapperRenderHandler; +import com.simibubi.create.content.curiosities.zapper.terrainzapper.WorldshaperRenderHandler; +import com.simibubi.create.content.logistics.block.mechanicalArm.ArmInteractionPointHandler; import com.simibubi.create.foundation.config.AllConfigs; import com.simibubi.create.foundation.gui.ScreenOpener; import com.simibubi.create.foundation.item.TooltipHelper; import com.simibubi.create.foundation.renderState.SuperRenderTypeBuffer; -import com.simibubi.create.foundation.tileEntity.behaviour.filtering.FilteringHandler; -import com.simibubi.create.foundation.tileEntity.behaviour.scrollvalue.ScrollValueHandler; +import com.simibubi.create.foundation.tileEntity.behaviour.edgeInteraction.EdgeInteractionRenderer; +import com.simibubi.create.foundation.tileEntity.behaviour.filtering.FilteringRenderer; +import com.simibubi.create.foundation.tileEntity.behaviour.linked.LinkRenderer; +import com.simibubi.create.foundation.tileEntity.behaviour.scrollvalue.ScrollValueRenderer; import com.simibubi.create.foundation.utility.AnimationTickHolder; +import com.simibubi.create.foundation.utility.ServerSpeedProvider; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.ActiveRenderInfo; @@ -24,10 +36,8 @@ import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.item.ItemStack; import net.minecraft.util.math.Vec3d; import net.minecraft.util.text.ITextComponent; +import net.minecraft.world.World; import net.minecraftforge.api.distmarker.Dist; -import net.minecraftforge.client.event.InputEvent.KeyInputEvent; -import net.minecraftforge.client.event.InputEvent.MouseInputEvent; -import net.minecraftforge.client.event.InputEvent.MouseScrollEvent; import net.minecraftforge.client.event.RenderGameOverlayEvent; import net.minecraftforge.client.event.RenderGameOverlayEvent.ElementType; import net.minecraftforge.client.event.RenderWorldLastEvent; @@ -35,6 +45,7 @@ import net.minecraftforge.event.TickEvent.ClientTickEvent; import net.minecraftforge.event.TickEvent.Phase; import net.minecraftforge.event.TickEvent.RenderTickEvent; import net.minecraftforge.event.entity.player.ItemTooltipEvent; +import net.minecraftforge.event.world.WorldEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.fml.common.Mod.EventBusSubscriber; @@ -46,6 +57,7 @@ public class ClientEvents { @SubscribeEvent public static void onTick(ClientTickEvent event) { + World world = Minecraft.getInstance().world; if (event.phase == Phase.START) return; @@ -54,13 +66,34 @@ public class ClientEvents { if (!isGameActive()) return; - if (!KineticDebugger.isActive() && KineticTileEntityRenderer.rainbowMode) { - KineticTileEntityRenderer.rainbowMode = false; - CreateClient.bufferCache.invalidate(); - } - + CreateClient.schematicSender.tick(); + CreateClient.schematicAndQuillHandler.tick(); + CreateClient.schematicHandler.tick(); + + ContraptionCollider.runCollisions(world); + MinecartCouplingHandler.tick(world); ScreenOpener.tick(); - CreateClient.gameTick(); + ServerSpeedProvider.clientTick(); + BeltConnectorHandler.tick(); + FilteringRenderer.tick(); + LinkRenderer.tick(); + ScrollValueRenderer.tick(); + ChassisRangeDisplay.tick(); + EdgeInteractionRenderer.tick(); + WorldshaperRenderHandler.tick(); + BlockzapperRenderHandler.tick(); + ClientMinecartCouplingHandler.tick(); + KineticDebugger.tick(); + ZapperRenderHandler.tick(); + ExtendoGripRenderHandler.tick(); +// CollisionDebugger.tick(); + ArmInteractionPointHandler.tick(); + CreateClient.outliner.tickOutlines(); + } + + @SubscribeEvent + public static void onLoadWorld(WorldEvent.Load event) { + CreateClient.bufferCache.invalidate(); } @SubscribeEvent @@ -68,11 +101,11 @@ public class ClientEvents { MatrixStack ms = event.getMatrixStack(); ActiveRenderInfo info = Minecraft.getInstance().gameRenderer.getActiveRenderInfo(); Vec3d view = info.getProjectedView(); - ms.push(); ms.translate(-view.getX(), -view.getY(), -view.getZ()); - SuperRenderTypeBuffer buffer = SuperRenderTypeBuffer.getInstance(); + + MinecartCouplingHandler.render(ms, buffer); CreateClient.schematicHandler.render(ms, buffer); CreateClient.outliner.renderOutlines(ms, buffer); // CollisionDebugger.render(ms, buffer); @@ -95,42 +128,6 @@ public class ClientEvents { CreateClient.schematicHandler.renderOverlay(ms, buffer, light, overlay); } - @SubscribeEvent - public static void onKeyInput(KeyInputEvent event) { - int key = event.getKey(); - boolean pressed = !(event.getAction() == 0); - - if (Minecraft.getInstance().currentScreen != null) - return; - - CreateClient.schematicHandler.onKeyInput(key, pressed); - } - - @SubscribeEvent - public static void onMouseScrolled(MouseScrollEvent event) { - if (Minecraft.getInstance().currentScreen != null) - return; - - double delta = event.getScrollDelta(); -// CollisionDebugger.onScroll(delta); - boolean cancelled = CreateClient.schematicHandler.mouseScrolled(delta) - || CreateClient.schematicAndQuillHandler.mouseScrolled(delta) || FilteringHandler.onScroll(delta) - || ScrollValueHandler.onScroll(delta); - event.setCanceled(cancelled); - } - - @SubscribeEvent - public static void onMouseInput(MouseInputEvent event) { - if (Minecraft.getInstance().currentScreen != null) - return; - - int button = event.getButton(); - boolean pressed = !(event.getAction() == 0); - - CreateClient.schematicHandler.onMouseInput(button, pressed); - CreateClient.schematicAndQuillHandler.onMouseInput(button, pressed); - } - @SubscribeEvent public static void addToItemTooltip(ItemTooltipEvent event) { if (!AllConfigs.CLIENT.tooltips.get()) @@ -159,7 +156,6 @@ public class ClientEvents { public static void onRenderTick(RenderTickEvent event) { if (!isGameActive()) return; - TurntableHandler.gameRenderTick(); } diff --git a/src/main/java/com/simibubi/create/events/CommonEvents.java b/src/main/java/com/simibubi/create/events/CommonEvents.java index 12cd8d21b..4273c650e 100644 --- a/src/main/java/com/simibubi/create/events/CommonEvents.java +++ b/src/main/java/com/simibubi/create/events/CommonEvents.java @@ -1,16 +1,25 @@ package com.simibubi.create.events; import com.simibubi.create.Create; -import com.simibubi.create.CreateClient; -import com.simibubi.create.foundation.command.CreateCommand; +import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionCollider; +import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionHandler; +import com.simibubi.create.content.contraptions.components.structureMovement.train.MinecartCouplingHandler; +import com.simibubi.create.content.schematics.ServerSchematicLoader; +import com.simibubi.create.foundation.command.AllCommands; +import com.simibubi.create.foundation.utility.ServerSpeedProvider; +import com.simibubi.create.foundation.utility.WorldAttached; +import net.minecraft.entity.Entity; +import net.minecraft.entity.LivingEntity; import net.minecraft.world.IWorld; -import net.minecraftforge.api.distmarker.Dist; +import net.minecraft.world.World; import net.minecraftforge.event.TickEvent.Phase; import net.minecraftforge.event.TickEvent.ServerTickEvent; +import net.minecraftforge.event.TickEvent.WorldTickEvent; +import net.minecraftforge.event.entity.EntityJoinWorldEvent; +import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent; import net.minecraftforge.event.world.WorldEvent; import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.DistExecutor; import net.minecraftforge.fml.common.Mod.EventBusSubscriber; import net.minecraftforge.fml.event.server.FMLServerStartingEvent; import net.minecraftforge.fml.event.server.FMLServerStoppingEvent; @@ -19,20 +28,50 @@ import net.minecraftforge.fml.event.server.FMLServerStoppingEvent; public class CommonEvents { @SubscribeEvent - public static void onTick(ServerTickEvent event) { - if (event.phase == Phase.END) + public static void onServerTick(ServerTickEvent event) { + if (event.phase == Phase.START) return; - Create.tick(); + if (Create.schematicReceiver == null) + Create.schematicReceiver = new ServerSchematicLoader(); + Create.schematicReceiver.tick(); + Create.lagger.tick(); + ServerSpeedProvider.serverTick(); } @SubscribeEvent - public static void onClose(FMLServerStoppingEvent event) { - Create.shutdown(); + public static void onWorldTick(WorldTickEvent event) { + if (event.phase == Phase.START) + return; + World world = event.world; + ContraptionCollider.runCollisions(world); + MinecartCouplingHandler.tick(world); + } + + @SubscribeEvent + public static void onUpdateLivingEntity(LivingUpdateEvent event) { + LivingEntity entityLiving = event.getEntityLiving(); + World world = entityLiving.world; + if (world == null) + return; + ContraptionHandler.entitiesWhoJustDismountedGetSentToTheRightLocation(entityLiving, world); } @SubscribeEvent - public static void serverStarting(FMLServerStartingEvent event) { - new CreateCommand(event.getCommandDispatcher()); + public static void onEntityAdded(EntityJoinWorldEvent event) { + Entity entity = event.getEntity(); + World world = event.getWorld(); + ContraptionHandler.addSpawnedContraptionsToCollisionList(entity, world); + MinecartCouplingHandler.handleAddedMinecart(entity, world); + } + + @SubscribeEvent + public static void serverStarted(FMLServerStartingEvent event) { + AllCommands.register(event.getCommandDispatcher()); + } + + @SubscribeEvent + public static void serverStopped(FMLServerStoppingEvent event) { + Create.schematicReceiver.shutdown(); } @SubscribeEvent @@ -40,8 +79,6 @@ public class CommonEvents { IWorld world = event.getWorld(); Create.redstoneLinkNetworkHandler.onLoadWorld(world); Create.torquePropagator.onLoadWorld(world); - if (event.getWorld().isRemote()) - DistExecutor.runWhenOn(Dist.CLIENT, () -> CreateClient.bufferCache::invalidate); } @SubscribeEvent @@ -49,6 +86,7 @@ public class CommonEvents { IWorld world = event.getWorld(); Create.redstoneLinkNetworkHandler.onUnloadWorld(world); Create.torquePropagator.onUnloadWorld(world); + WorldAttached.invalidateWorld(world); } } diff --git a/src/main/java/com/simibubi/create/events/InputEvents.java b/src/main/java/com/simibubi/create/events/InputEvents.java new file mode 100644 index 000000000..260c86182 --- /dev/null +++ b/src/main/java/com/simibubi/create/events/InputEvents.java @@ -0,0 +1,54 @@ +package com.simibubi.create.events; + +import com.simibubi.create.CreateClient; +import com.simibubi.create.foundation.tileEntity.behaviour.filtering.FilteringHandler; +import com.simibubi.create.foundation.tileEntity.behaviour.scrollvalue.ScrollValueHandler; + +import net.minecraft.client.Minecraft; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.client.event.InputEvent.KeyInputEvent; +import net.minecraftforge.client.event.InputEvent.MouseInputEvent; +import net.minecraftforge.client.event.InputEvent.MouseScrollEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod.EventBusSubscriber; + +@EventBusSubscriber(value = Dist.CLIENT) +public class InputEvents { + + @SubscribeEvent + public static void onKeyInput(KeyInputEvent event) { + int key = event.getKey(); + boolean pressed = !(event.getAction() == 0); + + if (Minecraft.getInstance().currentScreen != null) + return; + + CreateClient.schematicHandler.onKeyInput(key, pressed); + } + + @SubscribeEvent + public static void onMouseScrolled(MouseScrollEvent event) { + if (Minecraft.getInstance().currentScreen != null) + return; + + double delta = event.getScrollDelta(); +// CollisionDebugger.onScroll(delta); + boolean cancelled = CreateClient.schematicHandler.mouseScrolled(delta) + || CreateClient.schematicAndQuillHandler.mouseScrolled(delta) || FilteringHandler.onScroll(delta) + || ScrollValueHandler.onScroll(delta); + event.setCanceled(cancelled); + } + + @SubscribeEvent + public static void onMouseInput(MouseInputEvent event) { + if (Minecraft.getInstance().currentScreen != null) + return; + + int button = event.getButton(); + boolean pressed = !(event.getAction() == 0); + + CreateClient.schematicHandler.onMouseInput(button, pressed); + CreateClient.schematicAndQuillHandler.onMouseInput(button, pressed); + } + +} diff --git a/src/main/java/com/simibubi/create/foundation/command/AllCommands.java b/src/main/java/com/simibubi/create/foundation/command/AllCommands.java new file mode 100644 index 000000000..ec5f2fca8 --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/command/AllCommands.java @@ -0,0 +1,18 @@ +package com.simibubi.create.foundation.command; + +import com.mojang.brigadier.CommandDispatcher; + +import net.minecraft.command.CommandSource; +import net.minecraft.command.Commands; + +public class AllCommands { + + public static void register(CommandDispatcher dispatcher) { + dispatcher.register(Commands.literal("create") + .then(ToggleDebugCommand.register()) + .then(OverlayConfigCommand.register()) + .then(ClearBufferCacheCommand.register()) + // .then(KillTPSCommand.register()) //Commented out for release + ); + } +} diff --git a/src/main/java/com/simibubi/create/foundation/command/CreateCommand.java b/src/main/java/com/simibubi/create/foundation/command/CreateCommand.java deleted file mode 100644 index 9126dedba..000000000 --- a/src/main/java/com/simibubi/create/foundation/command/CreateCommand.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.simibubi.create.foundation.command; - -import com.mojang.brigadier.CommandDispatcher; - -import net.minecraft.command.CommandSource; -import net.minecraft.command.Commands; - -public class CreateCommand { - - public CreateCommand(CommandDispatcher dispatcher) { - dispatcher.register(Commands.literal("create") - .then(ToggleDebugCommand.register()) - .then(OverlayConfigCommand.register()) - .then(ClearBufferCacheCommand.register()) - //.then(KillTPSCommand.register()) //Commented out for release - ); - } -} diff --git a/src/main/java/com/simibubi/create/foundation/config/CKinetics.java b/src/main/java/com/simibubi/create/foundation/config/CKinetics.java index 0b5436ac2..3f83c62f2 100644 --- a/src/main/java/com/simibubi/create/foundation/config/CKinetics.java +++ b/src/main/java/com/simibubi/create/foundation/config/CKinetics.java @@ -26,6 +26,7 @@ public class CKinetics extends ConfigBase { public ConfigInt maxChassisRange = i(16, 1, "maxChassisRange", Comments.maxChassisRange); public ConfigInt maxPistonPoles = i(64, 1, "maxPistonPoles", Comments.maxPistonPoles); public ConfigInt maxRopeLength = i(128, 1, "maxRopeLength", Comments.maxRopeLength); + public ConfigInt maxCartCouplingLength = i(32, 1, "maxCartCouplingLength", Comments.maxCartCouplingLength); public ConfigGroup state = group(0, "stats", Comments.stats); public ConfigFloat mediumSpeed = f(30, 0, 4096, "mediumSpeed", Comments.rpm, Comments.mediumSpeed); @@ -58,6 +59,7 @@ public class CKinetics extends ConfigBase { static String maxChassisRange = "Maximum value of a chassis attachment range."; static String maxPistonPoles = "Maximum amount of extension poles behind a Mechanical Piston."; static String maxRopeLength = "Max length of rope available off a Rope Pulley."; + static String maxCartCouplingLength = "Maximum allowed distance of two coupled minecarts."; static String stats = "Configure speed/capacity levels for requirements and indicators."; static String rpm = "[in Revolutions per Minute]"; static String su = "[in Stress Units]"; diff --git a/src/main/java/com/simibubi/create/foundation/networking/AllPackets.java b/src/main/java/com/simibubi/create/foundation/networking/AllPackets.java index 57a6b3a82..5200f3255 100644 --- a/src/main/java/com/simibubi/create/foundation/networking/AllPackets.java +++ b/src/main/java/com/simibubi/create/foundation/networking/AllPackets.java @@ -10,6 +10,10 @@ import com.simibubi.create.content.contraptions.components.structureMovement.glu import com.simibubi.create.content.contraptions.components.structureMovement.sync.ClientMotionPacket; import com.simibubi.create.content.contraptions.components.structureMovement.sync.ContraptionInteractionPacket; import com.simibubi.create.content.contraptions.components.structureMovement.sync.ContraptionSeatMappingPacket; +import com.simibubi.create.content.contraptions.components.structureMovement.train.MinecartCouplingCreationPacket; +import com.simibubi.create.content.contraptions.components.structureMovement.train.MinecartCouplingSyncPacket; +import com.simibubi.create.content.contraptions.components.structureMovement.train.PersistantDataPacket; +import com.simibubi.create.content.contraptions.components.structureMovement.train.PersistantDataPacketRequest; import com.simibubi.create.content.contraptions.relays.advanced.sequencer.ConfigureSequencedGearshiftPacket; import com.simibubi.create.content.curiosities.symmetry.SymmetryEffectPacket; import com.simibubi.create.content.curiosities.tools.ExtendoGripInteractionPacket; @@ -49,6 +53,8 @@ public enum AllPackets { CONTRAPTION_INTERACT(ContraptionInteractionPacket.class, ContraptionInteractionPacket::new), CLIENT_MOTION(ClientMotionPacket.class, ClientMotionPacket::new), PLACE_ARM(ArmPlacementPacket.class, ArmPlacementPacket::new), + MINECART_COUPLING_CREATION(MinecartCouplingCreationPacket.class, MinecartCouplingCreationPacket::new), + PERSISTANT_DATA_REQUEST(PersistantDataPacketRequest.class, PersistantDataPacketRequest::new), // Server to Client SYMMETRY_EFFECT(SymmetryEffectPacket.class, SymmetryEffectPacket::new), @@ -57,7 +63,9 @@ public enum AllPackets { CONFIGURE_CONFIG(ConfigureConfigPacket.class, ConfigureConfigPacket::new), CONTRAPTION_STALL(ContraptionStallPacket.class, ContraptionStallPacket::new), GLUE_EFFECT(GlueEffectPacket.class, GlueEffectPacket::new), + MINECART_COUPLING_SYNC(MinecartCouplingSyncPacket.class, MinecartCouplingSyncPacket::new), CONTRAPTION_SEAT_MAPPING(ContraptionSeatMappingPacket.class, ContraptionSeatMappingPacket::new), + PERSISTANT_DATA(PersistantDataPacket.class, PersistantDataPacket::new), ; diff --git a/src/main/java/com/simibubi/create/foundation/utility/Couple.java b/src/main/java/com/simibubi/create/foundation/utility/Couple.java new file mode 100644 index 000000000..5645c30e6 --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/utility/Couple.java @@ -0,0 +1,72 @@ +package com.simibubi.create.foundation.utility; + +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.Consumer; +import java.util.function.Function; + +public class Couple extends Pair { + + private static Couple TRUE_AND_FALSE = Couple.create(true, false); + + protected Couple(T first, T second) { + super(first, second); + } + + public static Couple create(T first, T second) { + return new Couple<>(first, second); + } + + public T get(boolean first) { + return first ? getFirst() : getSecond(); + } + + public void set(boolean first, T value) { + if (first) + setFirst(value); + else + setSecond(value); + } + + @Override + public Couple copy() { + return create(first, second); + } + + public Couple map(Function function) { + return Couple.create(function.apply(first), function.apply(second)); + } + + public void replace(Function function) { + setFirst(function.apply(getFirst())); + setSecond(function.apply(getSecond())); + } + + public void replaceWithContext(BiFunction function) { + replaceWithParams(function, TRUE_AND_FALSE); + } + + public void replaceWithParams(BiFunction function, Couple values) { + setFirst(function.apply(getFirst(), values.getFirst())); + setSecond(function.apply(getSecond(), values.getSecond())); + } + + public void forEach(Consumer consumer) { + consumer.accept(getFirst()); + consumer.accept(getSecond()); + } + + public void forEachWithContext(BiConsumer consumer) { + forEachWithParams(consumer, TRUE_AND_FALSE); + } + + public void forEachWithParams(BiConsumer function, Couple values) { + function.accept(getFirst(), values.getFirst()); + function.accept(getSecond(), values.getSecond()); + } + + public Couple swap() { + return Couple.create(second, first); + } + +} diff --git a/src/main/java/com/simibubi/create/foundation/utility/Pair.java b/src/main/java/com/simibubi/create/foundation/utility/Pair.java new file mode 100644 index 000000000..dbd1810a2 --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/utility/Pair.java @@ -0,0 +1,64 @@ +package com.simibubi.create.foundation.utility; + +import java.util.Objects; + +public class Pair { + + F first; + S second; + + protected Pair(F first, S second) { + this.first = first; + this.second = second; + } + + public static Pair of(F first, S second) { + return new Pair<>(first, second); + } + + public F getFirst() { + return first; + } + + public S getSecond() { + return second; + } + + public void setFirst(F first) { + this.first = first; + } + + public void setSecond(S second) { + this.second = second; + } + + public Pair copy() { + return Pair.of(first, second); + } + + @Override + public boolean equals(final Object obj) { + if (obj == this) + return true; + if (obj instanceof Pair) { + final Pair other = (Pair) obj; + return Objects.equals(first, other.first) && Objects.equals(second, other.second); + } + return false; + } + + @Override + public int hashCode() { + return (first == null ? 0 : first.hashCode()) ^ (second == null ? 0 : second.hashCode()); + } + + @Override + public String toString() { + return "(" + first + ", " + second + ")"; + } + + public Pair swap() { + return Pair.of(second, first); + } + +} diff --git a/src/main/java/com/simibubi/create/foundation/utility/ServerSpeedProvider.java b/src/main/java/com/simibubi/create/foundation/utility/ServerSpeedProvider.java index 2e3599132..3c6fad18a 100644 --- a/src/main/java/com/simibubi/create/foundation/utility/ServerSpeedProvider.java +++ b/src/main/java/com/simibubi/create/foundation/utility/ServerSpeedProvider.java @@ -11,14 +11,9 @@ import net.minecraft.client.Minecraft; import net.minecraft.network.PacketBuffer; import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.OnlyIn; -import net.minecraftforge.event.TickEvent; -import net.minecraftforge.event.TickEvent.Phase; -import net.minecraftforge.eventbus.api.SubscribeEvent; -import net.minecraftforge.fml.common.Mod.EventBusSubscriber; import net.minecraftforge.fml.network.NetworkEvent.Context; import net.minecraftforge.fml.network.PacketDistributor; -@EventBusSubscriber public class ServerSpeedProvider { static int clientTimer = 0; @@ -26,33 +21,26 @@ public class ServerSpeedProvider { static boolean initialized = false; static InterpolatedChasingValue modifier = new InterpolatedChasingValue().withSpeed(.25f); - @SubscribeEvent - public static void onServerTick(TickEvent.ServerTickEvent event) { - if (event.phase == Phase.START) - return; + public static void serverTick() { serverTimer++; if (serverTimer > getSyncInterval()) { AllPackets.channel.send(PacketDistributor.ALL.noArg(), new Packet()); serverTimer = 0; } } + + @OnlyIn(Dist.CLIENT) + public static void clientTick() { + if (Minecraft.getInstance().isSingleplayer() && Minecraft.getInstance().isGamePaused()) + return; + modifier.tick(); + clientTimer++; + } public static Integer getSyncInterval() { return AllConfigs.SERVER.tickrateSyncTimer.get(); } - @OnlyIn(Dist.CLIENT) - @SubscribeEvent - public static void onClientTick(TickEvent.ClientTickEvent event) { - if (event.phase == Phase.START) - return; - if (Minecraft.getInstance().isSingleplayer() && Minecraft.getInstance().isGamePaused()) - return; - - modifier.tick(); - clientTimer++; - } - public static float get() { return modifier.value; } diff --git a/src/main/java/com/simibubi/create/foundation/utility/SuperByteBufferCache.java b/src/main/java/com/simibubi/create/foundation/utility/SuperByteBufferCache.java index adb1a16cd..1e55fa6af 100644 --- a/src/main/java/com/simibubi/create/foundation/utility/SuperByteBufferCache.java +++ b/src/main/java/com/simibubi/create/foundation/utility/SuperByteBufferCache.java @@ -52,17 +52,22 @@ public class SuperByteBufferCache { public SuperByteBuffer renderPartial(AllBlockPartials partial, BlockState referenceState) { return get(PARTIAL, partial, () -> standardModelRender(partial.get(), referenceState)); } - - public SuperByteBuffer renderPartial(AllBlockPartials partial, BlockState referenceState, MatrixStack modelTransform) { + + public SuperByteBuffer renderPartial(AllBlockPartials partial, BlockState referenceState, + MatrixStack modelTransform) { return get(PARTIAL, partial, () -> standardModelRender(partial.get(), referenceState, modelTransform)); } - - public SuperByteBuffer renderDirectionalPartial(AllBlockPartials partial, BlockState referenceState, Direction dir) { - return get(DIRECTIONAL_PARTIAL, Pair.of(dir, partial), () -> standardModelRender(partial.get(), referenceState)); + + public SuperByteBuffer renderDirectionalPartial(AllBlockPartials partial, BlockState referenceState, + Direction dir) { + return get(DIRECTIONAL_PARTIAL, Pair.of(dir, partial), + () -> standardModelRender(partial.get(), referenceState)); } - - public SuperByteBuffer renderDirectionalPartial(AllBlockPartials partial, BlockState referenceState, Direction dir, MatrixStack modelTransform) { - return get(DIRECTIONAL_PARTIAL, Pair.of(dir, partial), () -> standardModelRender(partial.get(), referenceState, modelTransform)); + + public SuperByteBuffer renderDirectionalPartial(AllBlockPartials partial, BlockState referenceState, Direction dir, + MatrixStack modelTransform) { + return get(DIRECTIONAL_PARTIAL, Pair.of(dir, partial), + () -> standardModelRender(partial.get(), referenceState, modelTransform)); } public SuperByteBuffer renderBlockIn(Compartment compartment, BlockState toRender) { @@ -84,16 +89,19 @@ public class SuperByteBufferCache { } public void registerCompartment(Compartment instance) { - cache.put(instance, CacheBuilder.newBuilder().build()); + cache.put(instance, CacheBuilder.newBuilder() + .build()); } - public void registerCompartment(Compartment instance, long ticksTillExpired) { - cache.put(instance, - CacheBuilder.newBuilder().expireAfterAccess(ticksTillExpired * 50, TimeUnit.MILLISECONDS).build()); + public void registerCompartment(Compartment instance, long ticksUntilExpired) { + cache.put(instance, CacheBuilder.newBuilder() + .expireAfterAccess(ticksUntilExpired * 50, TimeUnit.MILLISECONDS) + .build()); } private SuperByteBuffer standardBlockRender(BlockState renderedState) { - BlockRendererDispatcher dispatcher = Minecraft.getInstance().getBlockRendererDispatcher(); + BlockRendererDispatcher dispatcher = Minecraft.getInstance() + .getBlockRendererDispatcher(); return standardModelRender(dispatcher.getModelForState(renderedState), renderedState); } @@ -102,22 +110,21 @@ public class SuperByteBufferCache { } private SuperByteBuffer standardModelRender(IBakedModel model, BlockState referenceState, MatrixStack ms) { - BlockRendererDispatcher dispatcher = Minecraft.getInstance().getBlockRendererDispatcher(); + BlockRendererDispatcher dispatcher = Minecraft.getInstance() + .getBlockRendererDispatcher(); BlockModelRenderer blockRenderer = dispatcher.getBlockModelRenderer(); BufferBuilder builder = new BufferBuilder(DefaultVertexFormats.BLOCK.getIntegerSize()); Random random = new Random(); builder.begin(GL11.GL_QUADS, DefaultVertexFormats.BLOCK); blockRenderer.renderModelFlat(Minecraft.getInstance().world, model, referenceState, BlockPos.ZERO.up(255), ms, - builder, true, random, 42, OverlayTexture.DEFAULT_UV, EmptyModelData.INSTANCE); + builder, true, random, 42, OverlayTexture.DEFAULT_UV, EmptyModelData.INSTANCE); builder.finishDrawing(); return new SuperByteBuffer(builder); } public void invalidate() { - cache.forEach((comp, cache) -> { - cache.invalidateAll(); - }); + cache.forEach((comp, cache) -> cache.invalidateAll()); } } diff --git a/src/main/java/com/simibubi/create/foundation/utility/VecHelper.java b/src/main/java/com/simibubi/create/foundation/utility/VecHelper.java index 30eeb44c0..99fbffc69 100644 --- a/src/main/java/com/simibubi/create/foundation/utility/VecHelper.java +++ b/src/main/java/com/simibubi/create/foundation/utility/VecHelper.java @@ -2,6 +2,8 @@ package com.simibubi.create.foundation.utility; import java.util.Random; +import javax.annotation.Nullable; + import net.minecraft.nbt.DoubleNBT; import net.minecraft.nbt.ListNBT; import net.minecraft.util.Direction; @@ -80,6 +82,8 @@ public class VecHelper { } public static Vec3d readNBT(ListNBT list) { + if (list.isEmpty()) + return Vec3d.ZERO; return new Vec3d(list.getDouble(0), list.getDouble(1), list.getDouble(2)); } @@ -105,4 +109,31 @@ public class VecHelper { return true; } + public static Vec3d clamp(Vec3d vec, float maxLength) { + return vec.length() > maxLength ? vec.normalize() + .scale(maxLength) : vec; + } + + public static Vec3d project(Vec3d vec, Vec3d ontoVec) { + if (ontoVec.equals(Vec3d.ZERO)) + return Vec3d.ZERO; + return ontoVec.scale(vec.dotProduct(ontoVec) / ontoVec.lengthSquared()); + } + + @Nullable + public static Vec3d intersectSphere(Vec3d origin, Vec3d lineDirection, Vec3d sphereCenter, double radius) { + if (lineDirection.equals(Vec3d.ZERO)) + return null; + if (lineDirection.length() != 1) + lineDirection = lineDirection.normalize(); + + Vec3d diff = origin.subtract(sphereCenter); + double lineDotDiff = lineDirection.dotProduct(diff); + double delta = lineDotDiff * lineDotDiff - (diff.lengthSquared() - radius * radius); + if (delta < 0) + return null; + double t = -lineDotDiff + MathHelper.sqrt(delta); + return origin.add(lineDirection.scale(t)); + } + } diff --git a/src/main/java/com/simibubi/create/foundation/utility/WorldAttached.java b/src/main/java/com/simibubi/create/foundation/utility/WorldAttached.java new file mode 100644 index 000000000..99a84db53 --- /dev/null +++ b/src/main/java/com/simibubi/create/foundation/utility/WorldAttached.java @@ -0,0 +1,43 @@ +package com.simibubi.create.foundation.utility; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Supplier; + +import javax.annotation.Nullable; + +import net.minecraft.world.IWorld; + +public class WorldAttached { + + static List> allMaps = new ArrayList<>(); + Map attached; + private Supplier factory; + + public WorldAttached(Supplier factory) { + this.factory = factory; + attached = new HashMap<>(); + allMaps.add(attached); + } + + public static void invalidateWorld(IWorld world) { + allMaps.forEach(m -> m.remove(world)); + } + + @Nullable + public T get(IWorld world) { + T t = attached.get(world); + if (t != null) + return t; + T entry = factory.get(); + put(world, entry); + return entry; + } + + public void put(IWorld world, T entry) { + attached.put(world, entry); + } + +} diff --git a/src/main/java/com/simibubi/create/foundation/utility/outliner/LineOutline.java b/src/main/java/com/simibubi/create/foundation/utility/outliner/LineOutline.java index 0c97d254f..3f3a307ab 100644 --- a/src/main/java/com/simibubi/create/foundation/utility/outliner/LineOutline.java +++ b/src/main/java/com/simibubi/create/foundation/utility/outliner/LineOutline.java @@ -20,7 +20,7 @@ public class LineOutline extends Outline { @Override public void render(MatrixStack ms, SuperRenderTypeBuffer buffer) { - renderAACuboidLine(ms, buffer, start, end); + renderCuboidLine(ms, buffer, start, end); } public static class EndChasingLineOutline extends LineOutline { @@ -52,7 +52,7 @@ public class LineOutline extends Outline { float distanceToTarget = 1 - MathHelper.lerp(pt, prevProgress, progress); Vec3d start = end.add(this.start.subtract(end) .scale(distanceToTarget)); - renderAACuboidLine(ms, buffer, start, end); + renderCuboidLine(ms, buffer, start, end); } } diff --git a/src/main/java/com/simibubi/create/foundation/utility/outliner/Outline.java b/src/main/java/com/simibubi/create/foundation/utility/outliner/Outline.java index 4329d97fd..a8dcb297a 100644 --- a/src/main/java/com/simibubi/create/foundation/utility/outliner/Outline.java +++ b/src/main/java/com/simibubi/create/foundation/utility/outliner/Outline.java @@ -10,13 +10,16 @@ import com.mojang.blaze3d.vertex.IVertexBuilder; import com.simibubi.create.AllSpecialTextures; import com.simibubi.create.foundation.renderState.RenderTypes; import com.simibubi.create.foundation.renderState.SuperRenderTypeBuffer; +import com.simibubi.create.foundation.utility.AngleHelper; import com.simibubi.create.foundation.utility.ColorHelper; +import com.simibubi.create.foundation.utility.MatrixStacker; import com.simibubi.create.foundation.utility.VecHelper; import net.minecraft.client.renderer.Matrix3f; import net.minecraft.client.renderer.texture.OverlayTexture; import net.minecraft.util.Direction; import net.minecraft.util.Direction.Axis; +import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; public abstract class Outline { @@ -30,6 +33,20 @@ public abstract class Outline { public abstract void render(MatrixStack ms, SuperRenderTypeBuffer buffer); + public void renderCuboidLine(MatrixStack ms, SuperRenderTypeBuffer buffer, Vec3d start, Vec3d end) { + Vec3d diff = end.subtract(start); + float hAngle = AngleHelper.deg(MathHelper.atan2(diff.x, diff.z)); + float hDistance = (float) diff.mul(1, 0, 1) + .length(); + float vAngle = AngleHelper.deg(MathHelper.atan2(hDistance, diff.y)) - 90; + ms.push(); + MatrixStacker.of(ms) + .translate(start) + .rotateY(hAngle).rotateX(vAngle); + renderAACuboidLine(ms, buffer, Vec3d.ZERO, new Vec3d(0, 0, diff.length())); + ms.pop(); + } + public void renderAACuboidLine(MatrixStack ms, SuperRenderTypeBuffer buffer, Vec3d start, Vec3d end) { IVertexBuilder builder = buffer.getBuffer(RenderTypes.getOutlineSolid()); diff --git a/src/main/resources/assets/create/models/entity/minecart_coupling/attachment.json b/src/main/resources/assets/create/models/entity/minecart_coupling/attachment.json new file mode 100644 index 000000000..29d9735c7 --- /dev/null +++ b/src/main/resources/assets/create/models/entity/minecart_coupling/attachment.json @@ -0,0 +1,56 @@ +{ + "credit": "Made with Blockbench", + "textures": { + "0": "create:entity/coupling", + "particle": "create:entity/coupling" + }, + "elements": [ + { + "from": [-2.5, -2.5, -2.5], + "to": [2.5, -1.5, 2.5], + "rotation": {"angle": 0, "axis": "y", "origin": [2.5, -2.5, 0]}, + "faces": { + "north": {"uv": [1, 5, 0, 10], "texture": "#0"}, + "east": {"uv": [5, 5, 0, 6], "texture": "#0"}, + "south": {"uv": [5, 5, 4, 10], "texture": "#0"}, + "west": {"uv": [5, 5, 0, 6], "texture": "#0"}, + "up": {"uv": [0, 10, 5, 5], "rotation": 90, "texture": "#0"}, + "down": {"uv": [0, 10, 5, 5], "rotation": 90, "texture": "#0"} + } + }, + { + "from": [-3.5, -2.5, 0.5], + "to": [2.5, -1.5, 1.5], + "rotation": {"angle": 22.5, "axis": "z", "origin": [2.5, -2.5, 1.5]}, + "faces": { + "north": {"uv": [2, 5, 1, 10], "texture": "#0"}, + "south": {"uv": [2, 5, 1, 10], "texture": "#0"}, + "up": {"uv": [2, 5, 1, 10], "texture": "#0"}, + "down": {"uv": [2, 5, 1, 10], "texture": "#0"} + } + }, + { + "from": [-3.5, -2.5, -1.5], + "to": [2.5, -1.5, -0.5], + "rotation": {"angle": 22.5, "axis": "z", "origin": [2.5, -2.5, 1.5]}, + "faces": { + "north": {"uv": [2, 5, 1, 10], "texture": "#0"}, + "south": {"uv": [2, 5, 1, 10], "texture": "#0"}, + "up": {"uv": [2, 5, 1, 10], "texture": "#0"}, + "down": {"uv": [2, 5, 1, 10], "texture": "#0"} + } + }, + { + "from": [-1, -1.5, -1], + "to": [1, 2.5, 1], + "rotation": {"angle": 0, "axis": "y", "origin": [0.5, 0, -0.5]}, + "faces": { + "north": {"uv": [1, 5, 0, 9], "texture": "#0"}, + "east": {"uv": [1, 5, 0, 9], "texture": "#0"}, + "south": {"uv": [1, 5, 0, 9], "texture": "#0"}, + "west": {"uv": [1, 5, 0, 9], "texture": "#0"}, + "up": {"uv": [1, 5, 0, 6], "texture": "#0"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/create/models/entity/minecart_coupling/cart_coupling.bbmodel b/src/main/resources/assets/create/models/entity/minecart_coupling/cart_coupling.bbmodel new file mode 100644 index 000000000..e3f3ab67d --- /dev/null +++ b/src/main/resources/assets/create/models/entity/minecart_coupling/cart_coupling.bbmodel @@ -0,0 +1 @@ +{"meta":{"format_version":"3.2","model_format":"java_block","box_uv":false},"name":"","ambientocclusion":true,"front_gui_light":false,"resolution":{"width":16,"height":16},"elements":[{"name":"cube","from":[-2.5,-2.5,-2.5],"to":[2.5,-1.5,2.5],"autouv":0,"color":1,"locked":false,"origin":[2.5,-2.5,0],"faces":{"north":{"uv":[1,5,0,10],"texture":0},"east":{"uv":[5,5,0,6],"texture":0},"south":{"uv":[5,5,4,10],"texture":0},"west":{"uv":[5,5,0,6],"texture":0},"up":{"uv":[0,10,5,5],"rotation":90,"texture":0},"down":{"uv":[0,10,5,5],"rotation":90,"texture":0}},"uuid":"2ab28291-d8a0-1c59-0932-fd688d885b36"},{"name":"cube","from":[-3.5,-2.5,0.5],"to":[2.5,-1.5,1.5],"autouv":0,"color":1,"locked":false,"rotation":[0,0,22.5],"origin":[2.5,-2.5,1.5],"faces":{"north":{"uv":[2,5,1,10],"texture":0},"east":{"uv":[0,0,0,0],"texture":null},"south":{"uv":[2,5,1,10],"texture":0},"west":{"uv":[0,0,0,0],"texture":null},"up":{"uv":[2,5,1,10],"texture":0},"down":{"uv":[2,5,1,10],"texture":0}},"uuid":"161c9d0c-ea0d-7643-ea35-2a2b60015c66"},{"name":"cube","from":[-1,-1.5,-1],"to":[1,2.5,1],"autouv":0,"color":0,"locked":false,"origin":[0.5,0,-0.5],"faces":{"north":{"uv":[1,5,0,9],"texture":0},"east":{"uv":[1,5,0,9],"texture":0},"south":{"uv":[1,5,0,9],"texture":0},"west":{"uv":[1,5,0,9],"texture":0},"up":{"uv":[1,5,0,6],"texture":0},"down":{"uv":[0,0,0,0],"texture":null}},"uuid":"df623d06-f1ee-4e7a-d324-cca888af8748"},{"name":"cube","from":[-3.5,-2.5,-1.5],"to":[2.5,-1.5,-0.5],"autouv":0,"color":1,"locked":false,"rotation":[0,0,22.5],"origin":[2.5,-2.5,1.5],"faces":{"north":{"uv":[2,5,1,10],"texture":0},"east":{"uv":[0,0,0,0],"texture":null},"south":{"uv":[2,5,1,10],"texture":0},"west":{"uv":[0,0,0,0],"texture":null},"up":{"uv":[2,5,1,10],"texture":0},"down":{"uv":[2,5,1,10],"texture":0}},"uuid":"3cc3b4bd-1a33-b861-6e08-b88cb413b6a3"},{"name":"cube","from":[-2.5,-1,-2.5],"to":[2.5,1,2.5],"autouv":0,"color":2,"locked":false,"origin":[-5.5,7,5.5],"faces":{"north":{"uv":[10,0,5,2],"texture":0},"east":{"uv":[10,0,5,2],"texture":0},"south":{"uv":[10,0,5,2],"texture":0},"west":{"uv":[10,0,5,2],"texture":0},"up":{"uv":[5,0,0,5],"texture":0},"down":{"uv":[5,0,0,5],"texture":0}},"uuid":"1d3fae57-f8fb-8ae5-6b58-a9545e59439a"}],"outliner":["2ab28291-d8a0-1c59-0932-fd688d885b36","161c9d0c-ea0d-7643-ea35-2a2b60015c66","3cc3b4bd-1a33-b861-6e08-b88cb413b6a3","df623d06-f1ee-4e7a-d324-cca888af8748","1d3fae57-f8fb-8ae5-6b58-a9545e59439a"],"textures":[{"path":"C:\\Users\\simon\\Desktop\\Forgespace 1.15\\Create\\src\\main\\resources\\assets\\create\\textures\\entity\\coupling.png","name":"coupling.png","folder":"entity","namespace":"create","id":"0","particle":true,"mode":"bitmap","saved":true,"uuid":"b6862380-7179-b672-5fd5-f1caa05b56cf","source":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAm0lEQVQ4T2PMK638Ly4ixAADL9+8AzPRxSZ1tzPCFSExGFs7u/+DND15cJdBRkEZrPHsmdMM/KIScGUfX79gWLd6FW4DQBrevn3LICwszGBlbcPw8+dPhuryUqwa0F2B1QV3Hj5mmD9tEnEGwMKAnZ0dbDPIO7j8izUMgkLD/oP8C/InjMbl31EDsIUAAwNRUYVdK0R01AAqhAEAGbtwESadp4gAAAAASUVORK5CYII="}]} \ No newline at end of file diff --git a/src/main/resources/assets/create/models/entity/minecart_coupling/connector.json b/src/main/resources/assets/create/models/entity/minecart_coupling/connector.json new file mode 100644 index 000000000..bd660976d --- /dev/null +++ b/src/main/resources/assets/create/models/entity/minecart_coupling/connector.json @@ -0,0 +1,22 @@ +{ + "credit": "Made with Blockbench", + "textures": { + "0": "create:entity/coupling", + "particle": "create:entity/coupling" + }, + "elements": [ + { + "from": [0, -0.75, -1], + "to": [16, 0.75, 1], + "rotation": {"angle": 0, "axis": "y", "origin": [21, 7, 7]}, + "faces": { + "north": {"uv": [5, 2, 6, 4], "texture": "#0"}, + "east": {"uv": [5, 2, 6, 4], "texture": "#0"}, + "south": {"uv": [5, 2, 6, 4], "texture": "#0"}, + "west": {"uv": [5, 2, 6, 4], "texture": "#0"}, + "up": {"uv": [5, 2, 6, 3], "texture": "#0"}, + "down": {"uv": [5, 3, 6, 4], "texture": "#0"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/create/models/entity/minecart_coupling/ring.json b/src/main/resources/assets/create/models/entity/minecart_coupling/ring.json new file mode 100644 index 000000000..b94fbc4b3 --- /dev/null +++ b/src/main/resources/assets/create/models/entity/minecart_coupling/ring.json @@ -0,0 +1,22 @@ +{ + "credit": "Made with Blockbench", + "textures": { + "0": "create:entity/coupling", + "particle": "create:entity/coupling" + }, + "elements": [ + { + "from": [-2.5, -1, -2.5], + "to": [2.5, 1, 2.5], + "rotation": {"angle": 0, "axis": "y", "origin": [-5.5, 7, 5.5]}, + "faces": { + "north": {"uv": [10, 0, 5, 2], "texture": "#0"}, + "east": {"uv": [10, 0, 5, 2], "texture": "#0"}, + "south": {"uv": [10, 0, 5, 2], "texture": "#0"}, + "west": {"uv": [10, 0, 5, 2], "texture": "#0"}, + "up": {"uv": [5, 0, 0, 5], "texture": "#0"}, + "down": {"uv": [5, 0, 0, 5], "texture": "#0"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/create/textures/entity/coupling.png b/src/main/resources/assets/create/textures/entity/coupling.png new file mode 100644 index 0000000000000000000000000000000000000000..650b1610709ed66572de67f0ca457402a63f1141 GIT binary patch literal 471 zcmV;|0Vw{7P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vG?A^-p`A_1!6-I4$R0c=S`K~y+TW6Ue9 z{4Xvd!~g>?-@F6U=wcJQ+8Ci+6u<;h2vTtN%xN$U;y-zCm*LLC$8ZedZ`iyAss$A= zfe4U-w{PEqX?AvY24fRbhIvybG8*e@F>0%+Fe>XAGd_L#6s!RqFt&Df{|DI&(k!E( z40Zv?R*(T81@o6KXZZH{6T^?6zZf{!*%*|Rlo;;ayT`!A%g4aY#mT?~bGDVW4VVV; zVVXhU@UasN58r%bc=`D|R0IOvy?Mj%_1kv_7Ld)KzkX%-`1%C{zo-P5hJmE)d@%jy zImG$$BK%+)1VF9<0dN=)1CR{{c?SfZ1H*-&0U(XYnqdHB2tOY$eglv-f?^37{QUV7 zYzR?az@`~SGyeboAI!l99GqRCEC>h<4FyNz`3o1pd{B(xHK2FmWUwuubO8b?^0MHB z28w}?pFV@d@OlBH8I;6#Zd?z>AU4eJAcH^{uM3C)M5hB>fIjN4bydY@s2@z$+hCG2W{9Vz&Dhm+0LR(Aup!>spr>W^@k zGl&_u_s>r}vP_W0K`KP#>472zvko?ChR^r!R`W|-%e~pZXyRn|hKHY?dM6yc!LWF0 z>&N;D`~Bwb?GUoKzrUv8b`!sWmiCE%$Hgp;WUw)A5O?_J#4oeL#*u;F<;wv@u?M|w iNem||8AS~Y7#OZGPU8+;y=w>1*9@MnelF{r5}E*0Wq%j| literal 0 HcmV?d00001