Couple things, Part I

- Added Foundation and POC of minecart couplings and carriage contraptions. highly unstable
This commit is contained in:
simibubi 2020-08-05 22:10:05 +02:00
parent ffe3cf4f71
commit 7e167f3b29
58 changed files with 2426 additions and 377 deletions

View file

@ -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

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -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": "プロペラ",

View file

@ -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": "프로펠러",

View file

@ -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",

View file

@ -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",

View file

@ -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": "Пропеллер",

View file

@ -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": "扇叶",

View file

@ -0,0 +1,6 @@
{
"parent": "item/generated",
"textures": {
"layer0": "create:item/minecart_coupling"
}
}

View file

@ -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<Direction, AllBlockPartials> PIPE_RIMS = map();
public static final Map<HeatLevel, AllBlockPartials> 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);

View file

@ -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.<VerticalGearboxItem>customItemModel("gearbox", "item_vertical"))
.register();
public static final ItemEntry<BlazeBurnerBlockItem> EMPTY_BLAZE_BURNER =
REGISTRATE.item("empty_blaze_burner", BlazeBurnerBlockItem::empty)
.model(AssetLookup.<BlazeBurnerBlockItem>customItemModel("blaze_burner", "block"))
.register();
.model(AssetLookup.<BlazeBurnerBlockItem>customItemModel("blaze_burner", "block"))
.register();
public static final ItemEntry<SuperGlueItem> SUPER_GLUE = REGISTRATE.item("super_glue", SuperGlueItem::new)
.register();
public static final ItemEntry<MinecartCouplingItem> MINECART_COUPLING =
REGISTRATE.item("minecart_coupling", MinecartCouplingItem::new)
.register();
public static final ItemEntry<SandPaperItem> SAND_PAPER = REGISTRATE.item("sand_paper", SandPaperItem::new)
.transform(CreateRegistrate.customRenderedItem(() -> SandPaperModel::new))
.register();

View file

@ -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();
}

View file

@ -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<ResourceLocation, IBakedModel> 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<ModelResourceLocation> getAllBlockStateModelLocations(Block block) {
List<ModelResourceLocation> 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 <T extends IBakedModel> void swapModels(Map<ResourceLocation, IBakedModel> modelRegistry,
List<ModelResourceLocation> locations, Function<IBakedModel, T> factory) {
locations.forEach(location -> {
swapModels(modelRegistry, location, factory);
});
}
@OnlyIn(Dist.CLIENT)
protected static <T extends IBakedModel> void swapModels(Map<ResourceLocation, IBakedModel> modelRegistry,
ModelResourceLocation location, Function<IBakedModel, T> factory) {
modelRegistry.put(location, factory.apply(modelRegistry.get(location)));

View file

@ -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;

View file

@ -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);
}

View file

@ -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<World, List<WeakReference<ContraptionEntity>>> 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<WeakReference<ContraptionEntity>> 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<WeakReference<ContraptionEntity>> list = activeContraptions.getIfPresent(world);
public static void runCollisions(World world) {
List<WeakReference<ContraptionEntity>> list = ContraptionHandler.activeContraptions.getIfPresent(world);
if (list == null)
return;
for (Iterator<WeakReference<ContraptionEntity>> 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<VoxelShape> getPotentiallyCollidedShapes(World world, Contraption contraption,
private static ReuseableStream<VoxelShape> getPotentiallyCollidedShapes(World world, Contraption contraption,
AxisAlignedBB localBB) {
double height = localBB.getYSize();

View file

@ -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<Entity> 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<Entity> 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)

View file

@ -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<World, List<WeakReference<ContraptionEntity>>> activeContraptions = CacheBuilder.newBuilder()
.expireAfterAccess(400, SECONDS)
.build();
public static void addSpawnedContraptionsToCollisionList(Entity entity, World world) {
if (!(entity instanceof ContraptionEntity))
return;
try {
List<WeakReference<ContraptionEntity>> 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");
}
}

View file

@ -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);

View file

@ -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");

View file

@ -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<BlockInfo, TileEntity> capture(World world, BlockPos pos) {
Pair<BlockInfo, TileEntity> 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);

View file

@ -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);
}
}
}

View file

@ -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<AbstractMinecartEntity> mainCart;
WeakReference<AbstractMinecartEntity> 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<MinecartCoupling> loadAllAttached(World world, AbstractMinecartEntity minecart) {
List<MinecartCoupling> loaded = new ArrayList<>(2);
List<AbstractMinecartEntity> otherCartsInRange =
world.getEntitiesWithinAABB(AbstractMinecartEntity.class, minecart.getBoundingBox()
.grow(MinecartCouplingHandler.maxDistance()), c -> c != minecart);
List<CouplingData> couplingData = MinecartCouplingSerializer.getCouplingData(minecart);
Connections: for (CouplingData connection : couplingData) {
boolean cartIsMain = connection.main;
UUID cartCouplingUUID = connection.id;
for (AbstractMinecartEntity otherCart : otherCartsInRange) {
List<CouplingData> 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.<br>
* Changes are written to the carts' nbt data!<br>
* Id of this coupling will change!
*/
public void flip() {
MinecartCouplingSerializer.removeCouplingFromCart(mainCart.get(), this);
MinecartCouplingSerializer.removeCouplingFromCart(connectedCart.get(), this);
WeakReference<AbstractMinecartEntity> 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<AbstractMinecartEntity> asCouple() {
return Couple.create(mainCart.get(), connectedCart.get());
}
}

View file

@ -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) {
context.get()
.enqueueWork(() -> {
ServerPlayerEntity sender = context.get()
.getSender();
if (sender != null)
MinecartCouplingHandler.connectCarts(sender, sender.world, id1, id2);
});
context.get()
.setPacketHandled(true);
}
}

View file

@ -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.
*
* <HARD Resolve>
* 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
* }
* </HARD Resolve>
*
* 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 {
* <Soft Resolve>
* 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)
* </Soft 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<Map<UUID, MinecartCoupling>> loadedCouplings = new WorldAttached<>(HashMap::new);
static WorldAttached<Map<UUID, MinecartTrain>> loadedTrains = new WorldAttached<>(HashMap::new);
static WorldAttached<List<AbstractMinecartEntity>> 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<AbstractMinecartEntity> 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<UUID> toRemove = new ArrayList<>();
Map<UUID, MinecartCoupling> couplings = loadedCouplings.get(world);
if (couplings == null)
return;
for (Entry<UUID, MinecartCoupling> 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<UUID, Boolean> getTrainIfComplete(World world, AbstractMinecartEntity minecart,
@Nullable UUID ignore) {
AbstractMinecartEntity current = minecart;
UUID trainId = current.getUniqueID();
for (int i = 0; i < 100; i++) {
List<CouplingData> 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<UUID, Boolean> trainOf = getTrainIfComplete(world, cart1, null);
Pair<UUID, Boolean> 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<UUID, Boolean> trainIdOfMain = getTrainIfComplete(world, coupling.mainCart.get(), null);
Pair<UUID, Boolean> 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<UUID, MinecartTrain> 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<UUID, MinecartTrain> 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<UUID, MinecartCoupling> map = loadedCouplings.get(world);
return map.get(id);
}
public static void flipCoupling(World world, MinecartCoupling coupling) {
Map<UUID, MinecartCoupling> map = loadedCouplings.get(world);
map.remove(coupling.getId());
coupling.flip();
map.put(coupling.getId(), coupling);
}
}

View file

@ -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);
}
}

View file

@ -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<AbstractMinecartEntity> carts = coupling.asCouple();
Couple<Integer> lightValues =
carts.map(c -> WorldRenderer.getLightmapCoordinates(world, new BlockPos(c.getBoundingBox()
.getCenter())));
Vec3d center = carts.getFirst()
.getPositionVec()
.add(carts.getSecond()
.getPositionVec())
.scale(.5f);
Couple<CartEndpoint> 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));
}
}
}

View file

@ -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<CouplingData> getCouplingData(AbstractMinecartEntity minecart) {
List<CouplingData> 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;
}
}
}

View file

@ -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) {
context.get()
.enqueueWork(() -> MinecartCouplingHandler.connectCarts(null, Minecraft.getInstance().world, id1, id2));
context.get()
.setPacketHandled(true);
}
}

View file

@ -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<RailShape, Pair<Vec3i, Vec3i>> 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<Entity> 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<Vec3i, Vec3i> 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));
// }
}
}
}

View file

@ -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<MinecartCoupling> 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<AbstractMinecartEntity> carts, double couplingLength, boolean hard) {
if (hard)
hardCollisionStep(world, carts, couplingLength);
else
softCollisionStep(world, carts, couplingLength);
}
public void hardCollisionStep(World world, Couple<AbstractMinecartEntity> carts, double couplingLength) {
Couple<Vec3d> corrections = Couple.create(null, null);
Couple<Float> 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<AbstractMinecartEntity> carts, double couplingLength) {
Couple<Vec3d> positions = carts.map(Entity::getPositionVector);
Couple<Float> maxSpeed = carts.map(AbstractMinecartEntity::getMaxCartSpeedOnRail);
Couple<Boolean> canAddmotion = carts.map(MinecartSim2020::canAddMotion);
Couple<RailShape> 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<Vec3d> motions = carts.map(MinecartSim2020::predictMotionOf);
Couple<Vec3d> 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);
}
}

View file

@ -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) {
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);
}
}

View file

@ -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) {
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);
}
}

View file

@ -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();
}

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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<CommandSource> dispatcher) {
dispatcher.register(Commands.literal("create")
.then(ToggleDebugCommand.register())
.then(OverlayConfigCommand.register())
.then(ClearBufferCacheCommand.register())
// .then(KillTPSCommand.register()) //Commented out for release
);
}
}

View file

@ -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<CommandSource> dispatcher) {
dispatcher.register(Commands.literal("create")
.then(ToggleDebugCommand.register())
.then(OverlayConfigCommand.register())
.then(ClearBufferCacheCommand.register())
//.then(KillTPSCommand.register()) //Commented out for release
);
}
}

View file

@ -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]";

View file

@ -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),
;

View file

@ -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<T> extends Pair<T, T> {
private static Couple<Boolean> TRUE_AND_FALSE = Couple.create(true, false);
protected Couple(T first, T second) {
super(first, second);
}
public static <T> Couple<T> 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<T> copy() {
return create(first, second);
}
public <S> Couple<S> map(Function<T, S> function) {
return Couple.create(function.apply(first), function.apply(second));
}
public void replace(Function<T, T> function) {
setFirst(function.apply(getFirst()));
setSecond(function.apply(getSecond()));
}
public void replaceWithContext(BiFunction<T, Boolean, T> function) {
replaceWithParams(function, TRUE_AND_FALSE);
}
public <S> void replaceWithParams(BiFunction<T, S, T> function, Couple<S> values) {
setFirst(function.apply(getFirst(), values.getFirst()));
setSecond(function.apply(getSecond(), values.getSecond()));
}
public void forEach(Consumer<T> consumer) {
consumer.accept(getFirst());
consumer.accept(getSecond());
}
public void forEachWithContext(BiConsumer<T, Boolean> consumer) {
forEachWithParams(consumer, TRUE_AND_FALSE);
}
public <S> void forEachWithParams(BiConsumer<T, S> function, Couple<S> values) {
function.accept(getFirst(), values.getFirst());
function.accept(getSecond(), values.getSecond());
}
public Couple<T> swap() {
return Couple.create(second, first);
}
}

View file

@ -0,0 +1,64 @@
package com.simibubi.create.foundation.utility;
import java.util.Objects;
public class Pair<F, S> {
F first;
S second;
protected Pair(F first, S second) {
this.first = first;
this.second = second;
}
public static <F, S> Pair<F, S> 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<F, S> 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<S, F> swap() {
return Pair.of(second, first);
}
}

View file

@ -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;
}

View file

@ -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<BlockState> 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());
}
}

View file

@ -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));
}
}

View file

@ -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<T> {
static List<Map<IWorld, ?>> allMaps = new ArrayList<>();
Map<IWorld, T> attached;
private Supplier<T> factory;
public WorldAttached(Supplier<T> 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);
}
}

View file

@ -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);
}
}

View file

@ -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());

View file

@ -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"}
}
}
]
}

View file

@ -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="}]}

View file

@ -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"}
}
}
]
}

View file

@ -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"}
}
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 471 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 B