mirror of
https://github.com/Creators-of-Create/Create.git
synced 2024-11-11 04:22:00 +01:00
Merge branch 'mc1.18/dev' into mc1.18/0.5.1
This commit is contained in:
commit
ab221b9d5b
141 changed files with 4206 additions and 292 deletions
23
.github/workflows/gametest.yml
vendored
Normal file
23
.github/workflows/gametest.yml
vendored
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
name: gametest
|
||||||
|
on: [ pull_request, push, workflow_dispatch ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
|
||||||
|
- name: checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: setup Java
|
||||||
|
uses: actions/setup-java@v3
|
||||||
|
with:
|
||||||
|
distribution: temurin
|
||||||
|
java-version: 17
|
||||||
|
cache: gradle
|
||||||
|
|
||||||
|
- name: make gradle wrapper executable
|
||||||
|
run: chmod +x ./gradlew
|
||||||
|
|
||||||
|
- name: run gametests
|
||||||
|
run: ./gradlew prepareRunGameTestServer runGameTestServer --no-daemon
|
31
build.gradle
31
build.gradle
|
@ -90,6 +90,18 @@ minecraft {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gameTestServer {
|
||||||
|
workingDirectory project.file('run/gametest')
|
||||||
|
arg '-mixin.config=create.mixins.json'
|
||||||
|
property 'forge.logging.console.level', 'info'
|
||||||
|
mods {
|
||||||
|
create {
|
||||||
|
source sourceSets.main
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setForceExit false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -138,6 +150,14 @@ repositories {
|
||||||
flatDir {
|
flatDir {
|
||||||
dirs 'libs'
|
dirs 'libs'
|
||||||
}
|
}
|
||||||
|
maven {
|
||||||
|
// Location of maven for CC: Tweaked
|
||||||
|
name = "squiddev"
|
||||||
|
url = "https://squiddev.cc/maven/"
|
||||||
|
content {
|
||||||
|
includeGroup "org.squiddev"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
@ -165,6 +185,11 @@ dependencies {
|
||||||
compileOnly fg.deobf("top.theillusivec4.curios:curios-forge:${curios_minecraft_version}-${curios_version}:api")
|
compileOnly fg.deobf("top.theillusivec4.curios:curios-forge:${curios_minecraft_version}-${curios_version}:api")
|
||||||
runtimeOnly fg.deobf("top.theillusivec4.curios:curios-forge:${curios_minecraft_version}-${curios_version}")
|
runtimeOnly fg.deobf("top.theillusivec4.curios:curios-forge:${curios_minecraft_version}-${curios_version}")
|
||||||
|
|
||||||
|
if (cc_tweaked_enable.toBoolean()) {
|
||||||
|
compileOnly fg.deobf("org.squiddev:cc-tweaked-${cc_tweaked_minecraft_version}:${cc_tweaked_version}:api")
|
||||||
|
runtimeOnly fg.deobf("org.squiddev:cc-tweaked-${cc_tweaked_minecraft_version}:${cc_tweaked_version}")
|
||||||
|
}
|
||||||
|
|
||||||
// implementation fg.deobf("curse.maven:druidcraft-340991:3101903")
|
// implementation fg.deobf("curse.maven:druidcraft-340991:3101903")
|
||||||
// implementation fg.deobf("com.ferreusveritas.dynamictrees:DynamicTrees-1.16.5:0.10.0-Beta25")
|
// implementation fg.deobf("com.ferreusveritas.dynamictrees:DynamicTrees-1.16.5:0.10.0-Beta25")
|
||||||
// runtimeOnly fg.deobf("vazkii.arl:AutoRegLib:1.4-35.69")
|
// runtimeOnly fg.deobf("vazkii.arl:AutoRegLib:1.4-35.69")
|
||||||
|
@ -182,6 +207,12 @@ dependencies {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sourceSets.main.java {
|
||||||
|
if (!cc_tweaked_enable.toBoolean()) {
|
||||||
|
exclude 'com/simibubi/create/compat/computercraft/implementation/**'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sourceSets.main.resources {
|
sourceSets.main.resources {
|
||||||
srcDir 'src/generated/resources'
|
srcDir 'src/generated/resources'
|
||||||
exclude '.cache/'
|
exclude '.cache/'
|
||||||
|
|
|
@ -27,6 +27,10 @@ jei_version = 9.7.0.209
|
||||||
curios_minecraft_version = 1.18.2
|
curios_minecraft_version = 1.18.2
|
||||||
curios_version = 5.0.7.0
|
curios_version = 5.0.7.0
|
||||||
|
|
||||||
|
cc_tweaked_enable = true
|
||||||
|
cc_tweaked_minecraft_version = 1.18.2
|
||||||
|
cc_tweaked_version = 1.100.10
|
||||||
|
|
||||||
# curseforge information
|
# curseforge information
|
||||||
projectId = 328085
|
projectId = 328085
|
||||||
curse_type = beta
|
curse_type = beta
|
||||||
|
|
|
@ -582,7 +582,7 @@ bf2b0310500213ff853c748c236eb5d01f61658e assets/create/blockstates/yellow_toolbo
|
||||||
7f39521b211441f5c3e06d60c5978cebe16cacfb assets/create/blockstates/zinc_block.json
|
7f39521b211441f5c3e06d60c5978cebe16cacfb assets/create/blockstates/zinc_block.json
|
||||||
b7181bcd8182b2f17088e5aa881f374c9c65470c assets/create/blockstates/zinc_ore.json
|
b7181bcd8182b2f17088e5aa881f374c9c65470c assets/create/blockstates/zinc_ore.json
|
||||||
fcaad84ac4ebdb1e6d9301b77245ce855dbde503 assets/create/lang/en_ud.json
|
fcaad84ac4ebdb1e6d9301b77245ce855dbde503 assets/create/lang/en_ud.json
|
||||||
a0e7d027d022330c5358d75165a76383d86ba122 assets/create/lang/en_us.json
|
5bca3a0eafdf9c29f5d6b692fd395741e4a815e6 assets/create/lang/en_us.json
|
||||||
487a511a01b2a4531fb672f917922312db78f958 assets/create/models/block/acacia_window.json
|
487a511a01b2a4531fb672f917922312db78f958 assets/create/models/block/acacia_window.json
|
||||||
b48060cba1a382f373a05bf0039054053eccf076 assets/create/models/block/acacia_window_pane_noside.json
|
b48060cba1a382f373a05bf0039054053eccf076 assets/create/models/block/acacia_window_pane_noside.json
|
||||||
3066db1bf03cffa1a9c7fbacf47ae586632f4eb3 assets/create/models/block/acacia_window_pane_noside_alt.json
|
3066db1bf03cffa1a9c7fbacf47ae586632f4eb3 assets/create/models/block/acacia_window_pane_noside_alt.json
|
||||||
|
|
|
@ -1188,6 +1188,8 @@
|
||||||
"create.schematicAndQuill.convert": "Save and Upload Immediately",
|
"create.schematicAndQuill.convert": "Save and Upload Immediately",
|
||||||
"create.schematicAndQuill.fallbackName": "My Schematic",
|
"create.schematicAndQuill.fallbackName": "My Schematic",
|
||||||
"create.schematicAndQuill.saved": "Saved as %1$s",
|
"create.schematicAndQuill.saved": "Saved as %1$s",
|
||||||
|
"create.schematicAndQuill.failed": "Failed to save schematic, check logs for details",
|
||||||
|
"create.schematicAndQuill.instant_failed": "Schematic instant-upload failed, check logs for details",
|
||||||
|
|
||||||
"create.schematic.invalid": "[!] Invalid Item - Use the Schematic Table instead",
|
"create.schematic.invalid": "[!] Invalid Item - Use the Schematic Table instead",
|
||||||
"create.schematic.error": "Schematic failed to Load - Check Game Logs",
|
"create.schematic.error": "Schematic failed to Load - Check Game Logs",
|
||||||
|
|
|
@ -11,6 +11,7 @@ import com.google.gson.GsonBuilder;
|
||||||
import com.mojang.logging.LogUtils;
|
import com.mojang.logging.LogUtils;
|
||||||
import com.simibubi.create.api.behaviour.BlockSpoutingBehaviour;
|
import com.simibubi.create.api.behaviour.BlockSpoutingBehaviour;
|
||||||
import com.simibubi.create.compat.Mods;
|
import com.simibubi.create.compat.Mods;
|
||||||
|
import com.simibubi.create.compat.computercraft.ComputerCraftProxy;
|
||||||
import com.simibubi.create.compat.curios.Curios;
|
import com.simibubi.create.compat.curios.Curios;
|
||||||
import com.simibubi.create.content.contraptions.TorquePropagator;
|
import com.simibubi.create.content.contraptions.TorquePropagator;
|
||||||
import com.simibubi.create.content.contraptions.fluids.tank.BoilerHeaters;
|
import com.simibubi.create.content.contraptions.fluids.tank.BoilerHeaters;
|
||||||
|
@ -140,6 +141,7 @@ public class Create {
|
||||||
ContraptionMovementSetting.registerDefaults();
|
ContraptionMovementSetting.registerDefaults();
|
||||||
AllArmInteractionPointTypes.register();
|
AllArmInteractionPointTypes.register();
|
||||||
BlockSpoutingBehaviour.registerDefaults();
|
BlockSpoutingBehaviour.registerDefaults();
|
||||||
|
ComputerCraftProxy.register();
|
||||||
|
|
||||||
ForgeMod.enableMilkFluid();
|
ForgeMod.enableMilkFluid();
|
||||||
CopperRegistries.inject();
|
CopperRegistries.inject();
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
package com.simibubi.create.api.event;
|
||||||
|
|
||||||
|
import com.simibubi.create.content.logistics.trains.TrackGraph;
|
||||||
|
|
||||||
|
import net.minecraftforge.eventbus.api.Event;
|
||||||
|
|
||||||
|
public class TrackGraphMergeEvent extends Event{
|
||||||
|
private TrackGraph mergedInto, mergedFrom;
|
||||||
|
|
||||||
|
public TrackGraphMergeEvent(TrackGraph from, TrackGraph into) {
|
||||||
|
mergedInto = into;
|
||||||
|
mergedFrom = from;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TrackGraph getGraphMergedInto() {
|
||||||
|
return mergedInto;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TrackGraph getGraphMergedFrom() {
|
||||||
|
return mergedFrom;
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,7 +5,10 @@ import java.util.function.Supplier;
|
||||||
|
|
||||||
import com.simibubi.create.foundation.utility.Lang;
|
import com.simibubi.create.foundation.utility.Lang;
|
||||||
|
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
import net.minecraftforge.fml.ModList;
|
import net.minecraftforge.fml.ModList;
|
||||||
|
import net.minecraftforge.registries.ForgeRegistries;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For compatibility with and without another mod present, we have to define load conditions of the specific code
|
* For compatibility with and without another mod present, we have to define load conditions of the specific code
|
||||||
|
@ -14,6 +17,8 @@ public enum Mods {
|
||||||
DYNAMICTREES,
|
DYNAMICTREES,
|
||||||
TCONSTRUCT,
|
TCONSTRUCT,
|
||||||
CURIOS,
|
CURIOS,
|
||||||
|
|
||||||
|
COMPUTERCRAFT,
|
||||||
STORAGEDRAWERS,
|
STORAGEDRAWERS,
|
||||||
XLPACKETS;
|
XLPACKETS;
|
||||||
|
|
||||||
|
@ -51,4 +56,8 @@ public enum Mods {
|
||||||
toExecute.get().run();
|
toExecute.get().run();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Block getBlock(String id) {
|
||||||
|
return ForgeRegistries.BLOCKS.getValue(new ResourceLocation(asId(), id));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
package com.simibubi.create.compat.computercraft;
|
||||||
|
|
||||||
|
import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
|
||||||
|
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
|
||||||
|
import com.simibubi.create.foundation.tileEntity.behaviour.BehaviourType;
|
||||||
|
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraftforge.common.capabilities.Capability;
|
||||||
|
import net.minecraftforge.common.util.LazyOptional;
|
||||||
|
|
||||||
|
public class AbstractComputerBehaviour extends TileEntityBehaviour {
|
||||||
|
|
||||||
|
public static final BehaviourType<AbstractComputerBehaviour> TYPE = new BehaviourType<>();
|
||||||
|
|
||||||
|
boolean hasAttachedComputer;
|
||||||
|
|
||||||
|
public AbstractComputerBehaviour(SmartTileEntity te) {
|
||||||
|
super(te);
|
||||||
|
this.hasAttachedComputer = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(CompoundTag nbt, boolean clientPacket) {
|
||||||
|
hasAttachedComputer = nbt.getBoolean("HasAttachedComputer");
|
||||||
|
super.read(nbt, clientPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(CompoundTag nbt, boolean clientPacket) {
|
||||||
|
nbt.putBoolean("HasAttachedComputer", hasAttachedComputer);
|
||||||
|
super.write(nbt, clientPacket);
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> boolean isPeripheralCap(Capability<T> cap) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public <T> LazyOptional<T> getPeripheralCapability() {
|
||||||
|
return LazyOptional.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removePeripheral() {}
|
||||||
|
|
||||||
|
public void setHasAttachedComputer(boolean hasAttachedComputer) {
|
||||||
|
this.hasAttachedComputer = hasAttachedComputer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean hasAttachedComputer() {
|
||||||
|
return hasAttachedComputer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BehaviourType<?> getType() {
|
||||||
|
return TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
package com.simibubi.create.compat.computercraft;
|
||||||
|
|
||||||
|
import com.simibubi.create.foundation.networking.TileEntityDataPacket;
|
||||||
|
import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
|
||||||
|
import com.simibubi.create.foundation.tileEntity.SyncedTileEntity;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
|
|
||||||
|
public class AttachedComputerPacket extends TileEntityDataPacket<SyncedTileEntity> {
|
||||||
|
|
||||||
|
private final boolean hasAttachedComputer;
|
||||||
|
|
||||||
|
public AttachedComputerPacket(BlockPos tilePos, boolean hasAttachedComputer) {
|
||||||
|
super(tilePos);
|
||||||
|
this.hasAttachedComputer = hasAttachedComputer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public AttachedComputerPacket(FriendlyByteBuf buffer) {
|
||||||
|
super(buffer);
|
||||||
|
this.hasAttachedComputer = buffer.readBoolean();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void writeData(FriendlyByteBuf buffer) {
|
||||||
|
buffer.writeBoolean(hasAttachedComputer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void handlePacket(SyncedTileEntity tile) {
|
||||||
|
if (tile instanceof SmartTileEntity smartTile) {
|
||||||
|
smartTile.getBehaviour(AbstractComputerBehaviour.TYPE)
|
||||||
|
.setHasAttachedComputer(hasAttachedComputer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package com.simibubi.create.compat.computercraft;
|
||||||
|
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import com.simibubi.create.compat.Mods;
|
||||||
|
import com.simibubi.create.compat.computercraft.implementation.ComputerBehaviour;
|
||||||
|
import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
|
||||||
|
|
||||||
|
public class ComputerCraftProxy {
|
||||||
|
|
||||||
|
public static void register() {
|
||||||
|
fallbackFactory = FallbackComputerBehaviour::new;
|
||||||
|
Mods.COMPUTERCRAFT.executeIfInstalled(() -> ComputerCraftProxy::registerWithDependency);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void registerWithDependency() {
|
||||||
|
/* Comment if computercraft.implementation is not in the source set */
|
||||||
|
computerFactory = ComputerBehaviour::new;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Function<SmartTileEntity, ? extends AbstractComputerBehaviour> fallbackFactory;
|
||||||
|
private static Function<SmartTileEntity, ? extends AbstractComputerBehaviour> computerFactory;
|
||||||
|
|
||||||
|
public static AbstractComputerBehaviour behaviour(SmartTileEntity ste) {
|
||||||
|
if (computerFactory == null)
|
||||||
|
return fallbackFactory.apply(ste);
|
||||||
|
return computerFactory.apply(ste);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,96 @@
|
||||||
|
package com.simibubi.create.compat.computercraft;
|
||||||
|
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
|
import com.simibubi.create.compat.Mods;
|
||||||
|
import com.simibubi.create.foundation.gui.AbstractSimiScreen;
|
||||||
|
import com.simibubi.create.foundation.gui.AllGuiTextures;
|
||||||
|
import com.simibubi.create.foundation.gui.AllIcons;
|
||||||
|
import com.simibubi.create.foundation.gui.element.GuiGameElement;
|
||||||
|
import com.simibubi.create.foundation.gui.widget.AbstractSimiWidget;
|
||||||
|
import com.simibubi.create.foundation.gui.widget.ElementWidget;
|
||||||
|
import com.simibubi.create.foundation.gui.widget.IconButton;
|
||||||
|
import com.simibubi.create.foundation.utility.Lang;
|
||||||
|
|
||||||
|
import net.minecraft.client.gui.screens.Screen;
|
||||||
|
import net.minecraft.network.chat.Component;
|
||||||
|
|
||||||
|
public class ComputerScreen extends AbstractSimiScreen {
|
||||||
|
|
||||||
|
private final AllGuiTextures background = AllGuiTextures.COMPUTER;
|
||||||
|
|
||||||
|
private final Supplier<Component> displayTitle;
|
||||||
|
private final RenderWindowFunction additional;
|
||||||
|
private final Screen previousScreen;
|
||||||
|
private final Supplier<Boolean> hasAttachedComputer;
|
||||||
|
|
||||||
|
private AbstractSimiWidget computerWidget;
|
||||||
|
private IconButton confirmButton;
|
||||||
|
|
||||||
|
public ComputerScreen(Component title, @Nullable RenderWindowFunction additional, Screen previousScreen, Supplier<Boolean> hasAttachedComputer) {
|
||||||
|
this(title, () -> title, additional, previousScreen, hasAttachedComputer);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ComputerScreen(Component title, Supplier<Component> displayTitle, @Nullable RenderWindowFunction additional, Screen previousScreen, Supplier<Boolean> hasAttachedComputer) {
|
||||||
|
super(title);
|
||||||
|
this.displayTitle = displayTitle;
|
||||||
|
this.additional = additional;
|
||||||
|
this.previousScreen = previousScreen;
|
||||||
|
this.hasAttachedComputer = hasAttachedComputer;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tick() {
|
||||||
|
if (!hasAttachedComputer.get())
|
||||||
|
minecraft.setScreen(previousScreen);
|
||||||
|
|
||||||
|
super.tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void init() {
|
||||||
|
setWindowSize(background.width, background.height);
|
||||||
|
super.init();
|
||||||
|
|
||||||
|
int x = guiLeft;
|
||||||
|
int y = guiTop;
|
||||||
|
|
||||||
|
Mods.COMPUTERCRAFT.executeIfInstalled(() -> () -> {
|
||||||
|
computerWidget = new ElementWidget(x + 33, y + 38)
|
||||||
|
.showingElement(GuiGameElement.of(Mods.COMPUTERCRAFT.getBlock("computer_advanced")));
|
||||||
|
computerWidget.getToolTip().add(Lang.translate("gui.attached_computer.hint").component());
|
||||||
|
addRenderableWidget(computerWidget);
|
||||||
|
});
|
||||||
|
|
||||||
|
confirmButton = new IconButton(x + background.width - 33, y + background.height - 24, AllIcons.I_CONFIRM);
|
||||||
|
confirmButton.withCallback(this::onClose);
|
||||||
|
addRenderableWidget(confirmButton);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void renderWindow(PoseStack ms, int mouseX, int mouseY, float partialTicks) {
|
||||||
|
int x = guiLeft;
|
||||||
|
int y = guiTop;
|
||||||
|
|
||||||
|
background.render(ms, x, y, this);
|
||||||
|
|
||||||
|
font.draw(ms, displayTitle.get(), x + background.width / 2.0F - font.width(displayTitle.get()) / 2.0F, y + 4, 0x442000);
|
||||||
|
font.drawWordWrap(Lang.translate("gui.attached_computer.controlled").component(), x + 55, y + 32, 111, 0x7A7A7A);
|
||||||
|
|
||||||
|
if (additional != null)
|
||||||
|
additional.render(ms, mouseX, mouseY, partialTicks, x, y, background);
|
||||||
|
}
|
||||||
|
|
||||||
|
@FunctionalInterface
|
||||||
|
public interface RenderWindowFunction {
|
||||||
|
|
||||||
|
void render(PoseStack ms, int mouseX, int mouseY, float partialTicks, int guiLeft, int guiTop, AllGuiTextures background);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
package com.simibubi.create.compat.computercraft;
|
||||||
|
|
||||||
|
import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
|
||||||
|
|
||||||
|
public class FallbackComputerBehaviour extends AbstractComputerBehaviour {
|
||||||
|
|
||||||
|
public FallbackComputerBehaviour(SmartTileEntity te) {
|
||||||
|
super(te);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasAttachedComputer() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
package com.simibubi.create.compat.computercraft.implementation;
|
||||||
|
|
||||||
|
import com.simibubi.create.compat.computercraft.AbstractComputerBehaviour;
|
||||||
|
import com.simibubi.create.compat.computercraft.implementation.peripherals.DisplayLinkPeripheral;
|
||||||
|
import com.simibubi.create.compat.computercraft.implementation.peripherals.SequencedGearshiftPeripheral;
|
||||||
|
import com.simibubi.create.compat.computercraft.implementation.peripherals.SpeedControllerPeripheral;
|
||||||
|
import com.simibubi.create.compat.computercraft.implementation.peripherals.SpeedGaugePeripheral;
|
||||||
|
import com.simibubi.create.compat.computercraft.implementation.peripherals.StationPeripheral;
|
||||||
|
import com.simibubi.create.compat.computercraft.implementation.peripherals.StressGaugePeripheral;
|
||||||
|
import com.simibubi.create.content.contraptions.relays.advanced.SpeedControllerTileEntity;
|
||||||
|
import com.simibubi.create.content.contraptions.relays.advanced.sequencer.SequencedGearshiftTileEntity;
|
||||||
|
import com.simibubi.create.content.contraptions.relays.gauge.SpeedGaugeTileEntity;
|
||||||
|
import com.simibubi.create.content.contraptions.relays.gauge.StressGaugeTileEntity;
|
||||||
|
import com.simibubi.create.content.logistics.block.display.DisplayLinkTileEntity;
|
||||||
|
import com.simibubi.create.content.logistics.trains.management.edgePoint.station.StationTileEntity;
|
||||||
|
import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
|
import net.minecraftforge.common.capabilities.Capability;
|
||||||
|
import net.minecraftforge.common.capabilities.CapabilityManager;
|
||||||
|
import net.minecraftforge.common.capabilities.CapabilityToken;
|
||||||
|
import net.minecraftforge.common.util.LazyOptional;
|
||||||
|
import net.minecraftforge.common.util.NonNullSupplier;
|
||||||
|
|
||||||
|
public class ComputerBehaviour extends AbstractComputerBehaviour {
|
||||||
|
|
||||||
|
protected static final Capability<IPeripheral> PERIPHERAL_CAPABILITY =
|
||||||
|
CapabilityManager.get(new CapabilityToken<>() {
|
||||||
|
});
|
||||||
|
LazyOptional<IPeripheral> peripheral;
|
||||||
|
NonNullSupplier<IPeripheral> peripheralSupplier;
|
||||||
|
|
||||||
|
public ComputerBehaviour(SmartTileEntity te) {
|
||||||
|
super(te);
|
||||||
|
this.peripheralSupplier = getPeripheralFor(te);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static NonNullSupplier<IPeripheral> getPeripheralFor(SmartTileEntity te) {
|
||||||
|
if (te instanceof SpeedControllerTileEntity scte)
|
||||||
|
return () -> new SpeedControllerPeripheral(scte, scte.targetSpeed);
|
||||||
|
if (te instanceof DisplayLinkTileEntity dlte)
|
||||||
|
return () -> new DisplayLinkPeripheral(dlte);
|
||||||
|
if (te instanceof SequencedGearshiftTileEntity sgte)
|
||||||
|
return () -> new SequencedGearshiftPeripheral(sgte);
|
||||||
|
if (te instanceof SpeedGaugeTileEntity sgte)
|
||||||
|
return () -> new SpeedGaugePeripheral(sgte);
|
||||||
|
if (te instanceof StressGaugeTileEntity sgte)
|
||||||
|
return () -> new StressGaugePeripheral(sgte);
|
||||||
|
if (te instanceof StationTileEntity ste)
|
||||||
|
return () -> new StationPeripheral(ste);
|
||||||
|
|
||||||
|
throw new IllegalArgumentException("No peripheral available for " + te.getType()
|
||||||
|
.getRegistryName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> boolean isPeripheralCap(Capability<T> cap) {
|
||||||
|
return cap == PERIPHERAL_CAPABILITY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> LazyOptional<T> getPeripheralCapability() {
|
||||||
|
if (peripheral == null || !peripheral.isPresent())
|
||||||
|
peripheral = LazyOptional.of(peripheralSupplier);
|
||||||
|
return peripheral.cast();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removePeripheral() {
|
||||||
|
if (peripheral != null)
|
||||||
|
peripheral.invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,172 @@
|
||||||
|
package com.simibubi.create.compat.computercraft.implementation;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.lua.LuaException;
|
||||||
|
import dan200.computercraft.api.lua.LuaTable;
|
||||||
|
import dan200.computercraft.api.lua.LuaValues;
|
||||||
|
|
||||||
|
public class CreateLuaTable implements LuaTable<Object, Object> {
|
||||||
|
|
||||||
|
private final Map<Object, Object> map;
|
||||||
|
|
||||||
|
public CreateLuaTable() {
|
||||||
|
this.map = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public CreateLuaTable(Map<?, ?> map) {
|
||||||
|
this.map = new HashMap<>(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean getBoolean(String key) throws LuaException {
|
||||||
|
Object value = get(key);
|
||||||
|
|
||||||
|
if (!(value instanceof Boolean))
|
||||||
|
throw LuaValues.badField(key, "boolean", LuaValues.getType(value));
|
||||||
|
|
||||||
|
return (Boolean) value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getString(String key) throws LuaException {
|
||||||
|
Object value = get(key);
|
||||||
|
|
||||||
|
if (!(value instanceof String))
|
||||||
|
throw LuaValues.badField(key, "string", LuaValues.getType(value));
|
||||||
|
|
||||||
|
return (String) value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public CreateLuaTable getTable(String key) throws LuaException {
|
||||||
|
Object value = get(key);
|
||||||
|
|
||||||
|
if (!(value instanceof Map<?, ?>))
|
||||||
|
throw LuaValues.badField(key, "table", LuaValues.getType(value));
|
||||||
|
|
||||||
|
return new CreateLuaTable((Map<?, ?>) value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<Boolean> getOptBoolean(String key) throws LuaException {
|
||||||
|
Object value = get(key);
|
||||||
|
|
||||||
|
if (value == null)
|
||||||
|
return Optional.empty();
|
||||||
|
|
||||||
|
if (!(value instanceof Boolean))
|
||||||
|
throw LuaValues.badField(key, "boolean", LuaValues.getType(value));
|
||||||
|
|
||||||
|
return Optional.of((Boolean) value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<String> stringKeySet() throws LuaException {
|
||||||
|
Set<String> stringSet = new HashSet<>();
|
||||||
|
|
||||||
|
for (Object key : keySet()) {
|
||||||
|
if (!(key instanceof String))
|
||||||
|
throw new LuaException("key " + key + " is not string (got " + LuaValues.getType(key) + ")");
|
||||||
|
|
||||||
|
stringSet.add((String) key);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Collections.unmodifiableSet(stringSet);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Collection<CreateLuaTable> tableValues() throws LuaException {
|
||||||
|
List<CreateLuaTable> tables = new ArrayList<>();
|
||||||
|
|
||||||
|
for (int i = 1; i <= size(); i++) {
|
||||||
|
Object value = get((double) i);
|
||||||
|
|
||||||
|
if (!(value instanceof Map<?, ?>))
|
||||||
|
throw new LuaException("value " + value + " is not table (got " + LuaValues.getType(value) + ")");
|
||||||
|
|
||||||
|
tables.add(new CreateLuaTable((Map<?, ?>) value));
|
||||||
|
}
|
||||||
|
|
||||||
|
return Collections.unmodifiableList(tables);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Object, Object> getMap() {
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public Object put(Object key, Object value) {
|
||||||
|
return map.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putBoolean(String key, boolean value) {
|
||||||
|
map.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putDouble(String key, double value) {
|
||||||
|
map.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putString(String key, String value) {
|
||||||
|
map.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putTable(String key, CreateLuaTable value) {
|
||||||
|
map.put(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void putTable(int i, CreateLuaTable value) {
|
||||||
|
map.put(i, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int size() {
|
||||||
|
return map.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return map.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsKey(Object o) {
|
||||||
|
return map.containsKey(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean containsValue(Object o) {
|
||||||
|
return map.containsValue(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object get(Object o) {
|
||||||
|
return map.get(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Set<Object> keySet() {
|
||||||
|
return map.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Collection<Object> values() {
|
||||||
|
return map.values();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public Set<Entry<Object, Object>> entrySet() {
|
||||||
|
return map.entrySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,108 @@
|
||||||
|
package com.simibubi.create.compat.computercraft.implementation.peripherals;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import com.simibubi.create.content.logistics.block.display.DisplayLinkContext;
|
||||||
|
import com.simibubi.create.content.logistics.block.display.DisplayLinkTileEntity;
|
||||||
|
import com.simibubi.create.content.logistics.block.display.target.DisplayTargetStats;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.lua.LuaFunction;
|
||||||
|
import net.minecraft.nbt.ListTag;
|
||||||
|
import net.minecraft.nbt.StringTag;
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
|
|
||||||
|
public class DisplayLinkPeripheral extends SyncedPeripheral<DisplayLinkTileEntity> {
|
||||||
|
|
||||||
|
public static final String TAG_KEY = "ComputerSourceList";
|
||||||
|
private final AtomicInteger cursorX = new AtomicInteger();
|
||||||
|
private final AtomicInteger cursorY = new AtomicInteger();
|
||||||
|
|
||||||
|
public DisplayLinkPeripheral(DisplayLinkTileEntity tile) {
|
||||||
|
super(tile);
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaFunction
|
||||||
|
public final void setCursorPos(int x, int y) {
|
||||||
|
cursorX.set(x - 1);
|
||||||
|
cursorY.set(y - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaFunction
|
||||||
|
public final Object[] getCursorPos() {
|
||||||
|
return new Object[] {cursorX.get() + 1, cursorY.get() + 1};
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaFunction(mainThread = true)
|
||||||
|
public final Object[] getSize() {
|
||||||
|
DisplayTargetStats stats = tile.activeTarget.provideStats(new DisplayLinkContext(tile.getLevel(), tile));
|
||||||
|
return new Object[]{stats.maxRows(), stats.maxColumns()};
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaFunction
|
||||||
|
public final boolean isColor() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaFunction
|
||||||
|
public final boolean isColour() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaFunction
|
||||||
|
public final void write(String text) {
|
||||||
|
ListTag tag = tile.getSourceConfig().getList(TAG_KEY, Tag.TAG_STRING);
|
||||||
|
|
||||||
|
int x = cursorX.get();
|
||||||
|
int y = cursorY.get();
|
||||||
|
|
||||||
|
for (int i = tag.size(); i <= y; i++) {
|
||||||
|
tag.add(StringTag.valueOf(""));
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder builder = new StringBuilder(tag.getString(y));
|
||||||
|
|
||||||
|
builder.append(" ".repeat(Math.max(0, x - builder.length())));
|
||||||
|
builder.replace(x, x + text.length(), text);
|
||||||
|
|
||||||
|
tag.set(y, StringTag.valueOf(builder.toString()));
|
||||||
|
|
||||||
|
synchronized (tile) {
|
||||||
|
tile.getSourceConfig().put(TAG_KEY, tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
cursorX.set(x + text.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaFunction
|
||||||
|
public final void clearLine() {
|
||||||
|
ListTag tag = tile.getSourceConfig().getList(TAG_KEY, Tag.TAG_STRING);
|
||||||
|
|
||||||
|
if (tag.size() > cursorY.get())
|
||||||
|
tag.set(cursorY.get(), StringTag.valueOf(""));
|
||||||
|
|
||||||
|
synchronized (tile) {
|
||||||
|
tile.getSourceConfig().put(TAG_KEY, tag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaFunction
|
||||||
|
public final void clear() {
|
||||||
|
synchronized (tile) {
|
||||||
|
tile.getSourceConfig().put(TAG_KEY, new ListTag());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaFunction(mainThread = true)
|
||||||
|
public final void update() {
|
||||||
|
tile.tickSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String getType() {
|
||||||
|
return "Create_DisplayLink";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,54 @@
|
||||||
|
package com.simibubi.create.compat.computercraft.implementation.peripherals;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import com.simibubi.create.content.contraptions.relays.advanced.sequencer.Instruction;
|
||||||
|
import com.simibubi.create.content.contraptions.relays.advanced.sequencer.InstructionSpeedModifiers;
|
||||||
|
import com.simibubi.create.content.contraptions.relays.advanced.sequencer.SequencedGearshiftTileEntity;
|
||||||
|
import com.simibubi.create.content.contraptions.relays.advanced.sequencer.SequencerInstructions;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.lua.IArguments;
|
||||||
|
import dan200.computercraft.api.lua.LuaException;
|
||||||
|
import dan200.computercraft.api.lua.LuaFunction;
|
||||||
|
|
||||||
|
public class SequencedGearshiftPeripheral extends SyncedPeripheral<SequencedGearshiftTileEntity> {
|
||||||
|
|
||||||
|
public SequencedGearshiftPeripheral(SequencedGearshiftTileEntity tile) {
|
||||||
|
super(tile);
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaFunction(mainThread = true)
|
||||||
|
public final void rotate(IArguments arguments) throws LuaException {
|
||||||
|
runInstruction(arguments, SequencerInstructions.TURN_ANGLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaFunction(mainThread = true)
|
||||||
|
public final void move(IArguments arguments) throws LuaException {
|
||||||
|
runInstruction(arguments, SequencerInstructions.TURN_DISTANCE);
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaFunction
|
||||||
|
public final boolean isRunning() {
|
||||||
|
return !this.tile.isIdle();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void runInstruction(IArguments arguments, SequencerInstructions instructionType) throws LuaException {
|
||||||
|
int speedModifier = arguments.count() > 1 ? arguments.getInt(1) : 1;
|
||||||
|
this.tile.getInstructions().clear();
|
||||||
|
|
||||||
|
this.tile.getInstructions().add(new Instruction(
|
||||||
|
instructionType,
|
||||||
|
InstructionSpeedModifiers.getByModifier(speedModifier),
|
||||||
|
Math.abs(arguments.getInt(0))));
|
||||||
|
this.tile.getInstructions().add(new Instruction(SequencerInstructions.END));
|
||||||
|
|
||||||
|
this.tile.run(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String getType() {
|
||||||
|
return "Create_SequencedGearshift";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
package com.simibubi.create.compat.computercraft.implementation.peripherals;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import com.simibubi.create.content.contraptions.relays.advanced.SpeedControllerTileEntity;
|
||||||
|
import com.simibubi.create.foundation.tileEntity.behaviour.scrollvalue.ScrollValueBehaviour;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.lua.LuaFunction;
|
||||||
|
|
||||||
|
public class SpeedControllerPeripheral extends SyncedPeripheral<SpeedControllerTileEntity> {
|
||||||
|
|
||||||
|
private final ScrollValueBehaviour targetSpeed;
|
||||||
|
|
||||||
|
public SpeedControllerPeripheral(SpeedControllerTileEntity tile, ScrollValueBehaviour targetSpeed) {
|
||||||
|
super(tile);
|
||||||
|
this.targetSpeed = targetSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaFunction(mainThread = true)
|
||||||
|
public final void setTargetSpeed(int speed) {
|
||||||
|
this.targetSpeed.setValue(speed);
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaFunction
|
||||||
|
public final float getTargetSpeed() {
|
||||||
|
return this.targetSpeed.getValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String getType() {
|
||||||
|
return "Create_RotationSpeedController";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
package com.simibubi.create.compat.computercraft.implementation.peripherals;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import com.simibubi.create.content.contraptions.relays.gauge.SpeedGaugeTileEntity;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.lua.LuaFunction;
|
||||||
|
|
||||||
|
public class SpeedGaugePeripheral extends SyncedPeripheral<SpeedGaugeTileEntity> {
|
||||||
|
|
||||||
|
public SpeedGaugePeripheral(SpeedGaugeTileEntity tile) {
|
||||||
|
super(tile);
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaFunction
|
||||||
|
public final float getSpeed() {
|
||||||
|
return this.tile.getSpeed();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String getType() {
|
||||||
|
return "Create_Speedometer";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,269 @@
|
||||||
|
package com.simibubi.create.compat.computercraft.implementation.peripherals;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import com.simibubi.create.compat.computercraft.implementation.CreateLuaTable;
|
||||||
|
import com.simibubi.create.content.logistics.trains.entity.Train;
|
||||||
|
import com.simibubi.create.content.logistics.trains.management.edgePoint.station.GlobalStation;
|
||||||
|
import com.simibubi.create.content.logistics.trains.management.edgePoint.station.StationTileEntity;
|
||||||
|
import com.simibubi.create.content.logistics.trains.management.edgePoint.station.TrainEditPacket;
|
||||||
|
import com.simibubi.create.content.logistics.trains.management.schedule.Schedule;
|
||||||
|
import com.simibubi.create.foundation.networking.AllPackets;
|
||||||
|
import com.simibubi.create.foundation.utility.Components;
|
||||||
|
import com.simibubi.create.foundation.utility.StringHelper;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.lua.IArguments;
|
||||||
|
import dan200.computercraft.api.lua.LuaException;
|
||||||
|
import dan200.computercraft.api.lua.LuaFunction;
|
||||||
|
import net.minecraft.nbt.ByteTag;
|
||||||
|
import net.minecraft.nbt.CollectionTag;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.nbt.DoubleTag;
|
||||||
|
import net.minecraft.nbt.IntTag;
|
||||||
|
import net.minecraft.nbt.ListTag;
|
||||||
|
import net.minecraft.nbt.NumericTag;
|
||||||
|
import net.minecraft.nbt.StringTag;
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
|
import net.minecraftforge.network.PacketDistributor;
|
||||||
|
|
||||||
|
public class StationPeripheral extends SyncedPeripheral<StationTileEntity> {
|
||||||
|
|
||||||
|
public StationPeripheral(StationTileEntity tile) {
|
||||||
|
super(tile);
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaFunction(mainThread = true)
|
||||||
|
public final void assemble() throws LuaException {
|
||||||
|
if (!tile.isAssembling())
|
||||||
|
throw new LuaException("station must be in assembly mode");
|
||||||
|
|
||||||
|
tile.assemble(null);
|
||||||
|
|
||||||
|
if (tile.getStation() == null || tile.getStation().getPresentTrain() == null)
|
||||||
|
throw new LuaException("failed to assemble train");
|
||||||
|
|
||||||
|
if (!tile.exitAssemblyMode())
|
||||||
|
throw new LuaException("failed to exit assembly mode");
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaFunction(mainThread = true)
|
||||||
|
public final void disassemble() throws LuaException {
|
||||||
|
if (tile.isAssembling())
|
||||||
|
throw new LuaException("station must not be in assembly mode");
|
||||||
|
|
||||||
|
getTrainOrThrow();
|
||||||
|
|
||||||
|
if (!tile.enterAssemblyMode(null))
|
||||||
|
throw new LuaException("could not disassemble train");
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaFunction(mainThread = true)
|
||||||
|
public final void setAssemblyMode(boolean assemblyMode) throws LuaException {
|
||||||
|
if (assemblyMode) {
|
||||||
|
if (!tile.enterAssemblyMode(null))
|
||||||
|
throw new LuaException("failed to enter assembly mode");
|
||||||
|
} else {
|
||||||
|
if (!tile.exitAssemblyMode())
|
||||||
|
throw new LuaException("failed to exit assembly mode");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaFunction
|
||||||
|
public final boolean isInAssemblyMode() {
|
||||||
|
return tile.isAssembling();
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaFunction
|
||||||
|
public final String getStationName() throws LuaException {
|
||||||
|
GlobalStation station = tile.getStation();
|
||||||
|
if (station == null)
|
||||||
|
throw new LuaException("station is not connected to a track");
|
||||||
|
|
||||||
|
return station.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaFunction(mainThread = true)
|
||||||
|
public final void setStationName(String name) throws LuaException {
|
||||||
|
if (!tile.updateName(name))
|
||||||
|
throw new LuaException("could not set station name");
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaFunction
|
||||||
|
public final boolean isTrainPresent() throws LuaException {
|
||||||
|
GlobalStation station = tile.getStation();
|
||||||
|
if (station == null)
|
||||||
|
throw new LuaException("station is not connected to a track");
|
||||||
|
|
||||||
|
return station.getPresentTrain() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaFunction
|
||||||
|
public final boolean isTrainImminent() throws LuaException {
|
||||||
|
GlobalStation station = tile.getStation();
|
||||||
|
if (station == null)
|
||||||
|
throw new LuaException("station is not connected to a track");
|
||||||
|
|
||||||
|
return station.getImminentTrain() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaFunction
|
||||||
|
public final boolean isTrainEnroute() throws LuaException {
|
||||||
|
GlobalStation station = tile.getStation();
|
||||||
|
if (station == null)
|
||||||
|
throw new LuaException("station is not connected to a track");
|
||||||
|
|
||||||
|
return station.getNearestTrain() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaFunction
|
||||||
|
public final String getTrainName() throws LuaException {
|
||||||
|
Train train = getTrainOrThrow();
|
||||||
|
return train.name.getString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaFunction(mainThread = true)
|
||||||
|
public final void setTrainName(String name) throws LuaException {
|
||||||
|
Train train = getTrainOrThrow();
|
||||||
|
train.name = Components.literal(name);
|
||||||
|
AllPackets.channel.send(PacketDistributor.ALL.noArg(), new TrainEditPacket.TrainEditReturnPacket(train.id, name, train.icon.getId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaFunction
|
||||||
|
public final boolean hasSchedule() throws LuaException {
|
||||||
|
Train train = getTrainOrThrow();
|
||||||
|
return train.runtime.getSchedule() != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaFunction
|
||||||
|
public final CreateLuaTable getSchedule() throws LuaException {
|
||||||
|
Train train = getTrainOrThrow();
|
||||||
|
|
||||||
|
Schedule schedule = train.runtime.getSchedule();
|
||||||
|
if (schedule == null)
|
||||||
|
throw new LuaException("train doesn't have a schedule");
|
||||||
|
|
||||||
|
return fromCompoundTag(schedule.write());
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaFunction(mainThread = true)
|
||||||
|
public final void setSchedule(IArguments arguments) throws LuaException {
|
||||||
|
Train train = getTrainOrThrow();
|
||||||
|
Schedule schedule = Schedule.fromTag(toCompoundTag(new CreateLuaTable(arguments.getTable(0))));
|
||||||
|
boolean autoSchedule = train.runtime.getSchedule() == null || train.runtime.isAutoSchedule;
|
||||||
|
train.runtime.setSchedule(schedule, autoSchedule);
|
||||||
|
}
|
||||||
|
|
||||||
|
private @NotNull Train getTrainOrThrow() throws LuaException {
|
||||||
|
GlobalStation station = tile.getStation();
|
||||||
|
if (station == null)
|
||||||
|
throw new LuaException("station is not connected to a track");
|
||||||
|
|
||||||
|
Train train = station.getPresentTrain();
|
||||||
|
if (train == null)
|
||||||
|
throw new LuaException("there is no train present");
|
||||||
|
|
||||||
|
return train;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @NotNull CreateLuaTable fromCompoundTag(CompoundTag tag) throws LuaException {
|
||||||
|
return (CreateLuaTable) fromNBTTag(null, tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @NotNull Object fromNBTTag(@Nullable String key, Tag tag) throws LuaException {
|
||||||
|
byte type = tag.getId();
|
||||||
|
|
||||||
|
if (type == Tag.TAG_BYTE && key != null && key.equals("Count"))
|
||||||
|
return ((NumericTag) tag).getAsByte();
|
||||||
|
else if (type == Tag.TAG_BYTE)
|
||||||
|
return ((NumericTag) tag).getAsByte() != 0;
|
||||||
|
else if (type == Tag.TAG_SHORT || type == Tag.TAG_INT || type == Tag.TAG_LONG)
|
||||||
|
return ((NumericTag) tag).getAsLong();
|
||||||
|
else if (type == Tag.TAG_FLOAT || type == Tag.TAG_DOUBLE)
|
||||||
|
return ((NumericTag) tag).getAsDouble();
|
||||||
|
else if (type == Tag.TAG_STRING)
|
||||||
|
return tag.getAsString();
|
||||||
|
else if (type == Tag.TAG_LIST || type == Tag.TAG_BYTE_ARRAY || type == Tag.TAG_INT_ARRAY || type == Tag.TAG_LONG_ARRAY) {
|
||||||
|
CreateLuaTable list = new CreateLuaTable();
|
||||||
|
CollectionTag<?> listTag = (CollectionTag<?>) tag;
|
||||||
|
|
||||||
|
for (int i = 0; i < listTag.size(); i++) {
|
||||||
|
list.put(i + 1, fromNBTTag(null, listTag.get(i)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
|
||||||
|
} else if (type == Tag.TAG_COMPOUND) {
|
||||||
|
CreateLuaTable table = new CreateLuaTable();
|
||||||
|
CompoundTag compoundTag = (CompoundTag) tag;
|
||||||
|
|
||||||
|
for (String compoundKey : compoundTag.getAllKeys()) {
|
||||||
|
table.put(
|
||||||
|
StringHelper.camelCaseToSnakeCase(compoundKey),
|
||||||
|
fromNBTTag(compoundKey, compoundTag.get(compoundKey))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return table;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new LuaException("unknown tag type " + tag.getType().getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @NotNull CompoundTag toCompoundTag(CreateLuaTable table) throws LuaException {
|
||||||
|
return (CompoundTag) toNBTTag(null, table.getMap());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static @NotNull Tag toNBTTag(@Nullable String key, Object value) throws LuaException {
|
||||||
|
if (value instanceof Boolean v)
|
||||||
|
return ByteTag.valueOf(v);
|
||||||
|
else if (value instanceof Byte || (key != null && key.equals("count")))
|
||||||
|
return ByteTag.valueOf(((Number) value).byteValue());
|
||||||
|
else if (value instanceof Number v) {
|
||||||
|
// If number is numerical integer
|
||||||
|
if (v.intValue() == v.doubleValue())
|
||||||
|
return IntTag.valueOf(v.intValue());
|
||||||
|
else
|
||||||
|
return DoubleTag.valueOf(v.doubleValue());
|
||||||
|
|
||||||
|
} else if (value instanceof String v)
|
||||||
|
return StringTag.valueOf(v);
|
||||||
|
else if (value instanceof Map<?, ?> v && v.containsKey(1.0)) { // List
|
||||||
|
ListTag list = new ListTag();
|
||||||
|
for (Object o : v.values()) {
|
||||||
|
list.add(toNBTTag(null, o));
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
|
||||||
|
} else if (value instanceof Map<?, ?> v) { // Table/Map
|
||||||
|
CompoundTag compound = new CompoundTag();
|
||||||
|
for (Object objectKey : v.keySet()) {
|
||||||
|
if (!(objectKey instanceof String compoundKey))
|
||||||
|
throw new LuaException("table key is not of type string");
|
||||||
|
|
||||||
|
compound.put(
|
||||||
|
// Items serialize their resource location as "id" and not as "Id".
|
||||||
|
// This check is needed to see if the 'i' should be left lowercase or not.
|
||||||
|
// Items store "count" in the same compound tag, so we can check for its presence to see if this is a serialized item
|
||||||
|
compoundKey.equals("id") && v.containsKey("count") ? "id" : StringHelper.snakeCaseToCamelCase(compoundKey),
|
||||||
|
toNBTTag(compoundKey, v.get(compoundKey))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return compound;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new LuaException("unknown object type " + value.getClass().getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String getType() {
|
||||||
|
return "Create_Station";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package com.simibubi.create.compat.computercraft.implementation.peripherals;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import com.simibubi.create.content.contraptions.relays.gauge.StressGaugeTileEntity;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.lua.LuaFunction;
|
||||||
|
|
||||||
|
public class StressGaugePeripheral extends SyncedPeripheral<StressGaugeTileEntity> {
|
||||||
|
|
||||||
|
public StressGaugePeripheral(StressGaugeTileEntity tile) {
|
||||||
|
super(tile);
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaFunction
|
||||||
|
public final float getStress() {
|
||||||
|
return this.tile.getNetworkStress();
|
||||||
|
}
|
||||||
|
|
||||||
|
@LuaFunction
|
||||||
|
public final float getStressCapacity() {
|
||||||
|
return this.tile.getNetworkCapacity();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public String getType() {
|
||||||
|
return "Create_Stressometer";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,50 @@
|
||||||
|
package com.simibubi.create.compat.computercraft.implementation.peripherals;
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.simibubi.create.compat.computercraft.AttachedComputerPacket;
|
||||||
|
import com.simibubi.create.compat.computercraft.implementation.ComputerBehaviour;
|
||||||
|
import com.simibubi.create.foundation.networking.AllPackets;
|
||||||
|
import com.simibubi.create.foundation.tileEntity.SmartTileEntity;
|
||||||
|
|
||||||
|
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||||
|
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||||
|
import net.minecraftforge.network.PacketDistributor;
|
||||||
|
|
||||||
|
public abstract class SyncedPeripheral<T extends SmartTileEntity> implements IPeripheral {
|
||||||
|
|
||||||
|
protected final T tile;
|
||||||
|
private final AtomicInteger computers = new AtomicInteger();
|
||||||
|
|
||||||
|
public SyncedPeripheral(T tile) {
|
||||||
|
this.tile = tile;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void attach(@NotNull IComputerAccess computer) {
|
||||||
|
computers.incrementAndGet();
|
||||||
|
updateTile();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void detach(@NotNull IComputerAccess computer) {
|
||||||
|
computers.decrementAndGet();
|
||||||
|
updateTile();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateTile() {
|
||||||
|
boolean hasAttachedComputer = computers.get() > 0;
|
||||||
|
|
||||||
|
tile.getBehaviour(ComputerBehaviour.TYPE).setHasAttachedComputer(hasAttachedComputer);
|
||||||
|
AllPackets.channel.send(PacketDistributor.ALL.noArg(), new AttachedComputerPacket(tile.getBlockPos(), hasAttachedComputer));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(@Nullable IPeripheral other) {
|
||||||
|
return this == other;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -202,6 +202,7 @@ public class GantryContraptionEntity extends AbstractContraptionEntity {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@OnlyIn(Dist.CLIENT)
|
||||||
public void applyLocalTransforms(PoseStack matrixStack, float partialTicks) {}
|
public void applyLocalTransforms(PoseStack matrixStack, float partialTicks) {}
|
||||||
|
|
||||||
public void updateClientMotion() {
|
public void updateClientMotion() {
|
||||||
|
|
|
@ -25,6 +25,7 @@ import net.minecraft.tags.BlockTags;
|
||||||
import net.minecraft.tags.FluidTags;
|
import net.minecraft.tags.FluidTags;
|
||||||
import net.minecraft.world.effect.MobEffect;
|
import net.minecraft.world.effect.MobEffect;
|
||||||
import net.minecraft.world.effect.MobEffectInstance;
|
import net.minecraft.world.effect.MobEffectInstance;
|
||||||
|
import net.minecraft.world.effect.MobEffects;
|
||||||
import net.minecraft.world.entity.Entity;
|
import net.minecraft.world.entity.Entity;
|
||||||
import net.minecraft.world.entity.LivingEntity;
|
import net.minecraft.world.entity.LivingEntity;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
@ -39,6 +40,7 @@ import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||||
import net.minecraft.world.level.material.FluidState;
|
import net.minecraft.world.level.material.FluidState;
|
||||||
import net.minecraft.world.level.material.Fluids;
|
import net.minecraft.world.level.material.Fluids;
|
||||||
import net.minecraft.world.phys.AABB;
|
import net.minecraft.world.phys.AABB;
|
||||||
|
import net.minecraftforge.common.ForgeConfig;
|
||||||
import net.minecraftforge.common.Tags;
|
import net.minecraftforge.common.Tags;
|
||||||
import net.minecraftforge.common.util.LazyOptional;
|
import net.minecraftforge.common.util.LazyOptional;
|
||||||
import net.minecraftforge.fluids.FluidStack;
|
import net.minecraftforge.fluids.FluidStack;
|
||||||
|
@ -54,6 +56,7 @@ public class OpenEndedPipe extends FlowSource {
|
||||||
registerEffectHandler(new MilkEffectHandler());
|
registerEffectHandler(new MilkEffectHandler());
|
||||||
registerEffectHandler(new WaterEffectHandler());
|
registerEffectHandler(new WaterEffectHandler());
|
||||||
registerEffectHandler(new LavaEffectHandler());
|
registerEffectHandler(new LavaEffectHandler());
|
||||||
|
registerEffectHandler(new TeaEffectHandler());
|
||||||
}
|
}
|
||||||
|
|
||||||
private Level world;
|
private Level world;
|
||||||
|
@ -443,4 +446,23 @@ public class OpenEndedPipe extends FlowSource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class TeaEffectHandler implements IEffectHandler {
|
||||||
|
@Override
|
||||||
|
public boolean canApplyEffects(OpenEndedPipe pipe, FluidStack fluid) {
|
||||||
|
return fluid.getFluid().isSame(AllFluids.TEA.get());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void applyEffects(OpenEndedPipe pipe, FluidStack fluid) {
|
||||||
|
Level world = pipe.getWorld();
|
||||||
|
if (world.getGameTime() % 5 != 0)
|
||||||
|
return;
|
||||||
|
List<LivingEntity> entities = world
|
||||||
|
.getEntitiesOfClass(LivingEntity.class, pipe.getAOE(), LivingEntity::isAffectedByPotions);
|
||||||
|
for (LivingEntity entity : entities) {
|
||||||
|
entity.addEffect(new MobEffectInstance(MobEffects.DIG_SPEED, 21, 0, false, false, false));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -125,4 +125,8 @@ public class HosePulleyFluidHandler implements IFluidHandler {
|
||||||
return internalTank.isFluidValid(tank, stack);
|
return internalTank.isFluidValid(tank, stack);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SmartFluidTank getInternalTank() {
|
||||||
|
return internalTank;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,14 +38,24 @@ public class SpoutRenderer extends SafeBlockEntityRenderer<SpoutBlockEntity> {
|
||||||
.getValue(partialTicks);
|
.getValue(partialTicks);
|
||||||
|
|
||||||
if (!fluidStack.isEmpty() && level != 0) {
|
if (!fluidStack.isEmpty() && level != 0) {
|
||||||
|
boolean top = fluidStack.getFluid()
|
||||||
|
.getAttributes()
|
||||||
|
.isLighterThanAir();
|
||||||
|
|
||||||
level = Math.max(level, 0.175f);
|
level = Math.max(level, 0.175f);
|
||||||
float min = 2.5f / 16f;
|
float min = 2.5f / 16f;
|
||||||
float max = min + (11 / 16f);
|
float max = min + (11 / 16f);
|
||||||
float yOffset = (11 / 16f) * level;
|
float yOffset = (11 / 16f) * level;
|
||||||
|
|
||||||
ms.pushPose();
|
ms.pushPose();
|
||||||
ms.translate(0, yOffset, 0);
|
if (!top) ms.translate(0, yOffset, 0);
|
||||||
FluidRenderer.renderFluidBox(fluidStack, min, min - yOffset, min, max, min, max, buffer, ms, light,
|
else ms.translate(0, max - min, 0);
|
||||||
false);
|
|
||||||
|
FluidRenderer.renderFluidBox(fluidStack,
|
||||||
|
min, min - yOffset, min,
|
||||||
|
max, min, max,
|
||||||
|
buffer, ms, light, false);
|
||||||
|
|
||||||
ms.popPose();
|
ms.popPose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,8 @@ import java.util.function.Supplier;
|
||||||
|
|
||||||
import com.simibubi.create.AllBlockEntityTypes;
|
import com.simibubi.create.AllBlockEntityTypes;
|
||||||
import com.simibubi.create.AllBlocks;
|
import com.simibubi.create.AllBlocks;
|
||||||
|
import com.simibubi.create.content.contraptions.components.structureMovement.ITransformableBlock;
|
||||||
|
import com.simibubi.create.content.contraptions.components.structureMovement.StructureTransform;
|
||||||
import com.simibubi.create.content.contraptions.fluids.FluidPropagator;
|
import com.simibubi.create.content.contraptions.fluids.FluidPropagator;
|
||||||
import com.simibubi.create.content.contraptions.fluids.FluidTransportBehaviour;
|
import com.simibubi.create.content.contraptions.fluids.FluidTransportBehaviour;
|
||||||
import com.simibubi.create.content.contraptions.relays.elementary.EncasedBlock;
|
import com.simibubi.create.content.contraptions.relays.elementary.EncasedBlock;
|
||||||
|
@ -36,7 +38,9 @@ import net.minecraft.world.item.context.UseOnContext;
|
||||||
import net.minecraft.world.level.BlockGetter;
|
import net.minecraft.world.level.BlockGetter;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.Mirror;
|
||||||
import net.minecraft.world.level.block.PipeBlock;
|
import net.minecraft.world.level.block.PipeBlock;
|
||||||
|
import net.minecraft.world.level.block.Rotation;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
@ -46,7 +50,8 @@ import net.minecraft.world.phys.BlockHitResult;
|
||||||
import net.minecraft.world.phys.HitResult;
|
import net.minecraft.world.phys.HitResult;
|
||||||
import net.minecraft.world.ticks.TickPriority;
|
import net.minecraft.world.ticks.TickPriority;
|
||||||
|
|
||||||
public class EncasedPipeBlock extends Block implements IWrenchable, ISpecialBlockItemRequirement, IBE<FluidPipeBlockEntity>, EncasedBlock {
|
public class EncasedPipeBlock extends Block
|
||||||
|
implements IWrenchable, ISpecialBlockItemRequirement, IBE<FluidPipeBlockEntity>, EncasedBlock, ITransformableBlock {
|
||||||
public static final Map<Direction, BooleanProperty> FACING_TO_PROPERTY_MAP = PipeBlock.PROPERTY_BY_DIRECTION;
|
public static final Map<Direction, BooleanProperty> FACING_TO_PROPERTY_MAP = PipeBlock.PROPERTY_BY_DIRECTION;
|
||||||
|
|
||||||
private final Supplier<Block> casing;
|
private final Supplier<Block> casing;
|
||||||
|
@ -173,4 +178,20 @@ public class EncasedPipeBlock extends Block implements IWrenchable, ISpecialBloc
|
||||||
EncasedPipeBlock.transferSixWayProperties(state, defaultBlockState()));
|
EncasedPipeBlock.transferSixWayProperties(state, defaultBlockState()));
|
||||||
FluidTransportBehaviour.loadFlows(level, pos);
|
FluidTransportBehaviour.loadFlows(level, pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockState rotate(BlockState pState, Rotation pRotation) {
|
||||||
|
return FluidPipeBlockRotation.rotate(pState, pRotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockState mirror(BlockState pState, Mirror pMirror) {
|
||||||
|
return FluidPipeBlockRotation.mirror(pState, pMirror);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockState transform(BlockState state, StructureTransform transform) {
|
||||||
|
return FluidPipeBlockRotation.transform(state, transform);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,8 @@ import javax.annotation.Nullable;
|
||||||
|
|
||||||
import com.simibubi.create.AllBlockEntityTypes;
|
import com.simibubi.create.AllBlockEntityTypes;
|
||||||
import com.simibubi.create.AllBlocks;
|
import com.simibubi.create.AllBlocks;
|
||||||
|
import com.simibubi.create.content.contraptions.components.structureMovement.ITransformableBlock;
|
||||||
|
import com.simibubi.create.content.contraptions.components.structureMovement.StructureTransform;
|
||||||
import com.simibubi.create.content.contraptions.fluids.FluidPropagator;
|
import com.simibubi.create.content.contraptions.fluids.FluidPropagator;
|
||||||
import com.simibubi.create.content.contraptions.fluids.FluidTransportBehaviour;
|
import com.simibubi.create.content.contraptions.fluids.FluidTransportBehaviour;
|
||||||
import com.simibubi.create.content.contraptions.relays.elementary.BracketedBlockEntityBehaviour;
|
import com.simibubi.create.content.contraptions.relays.elementary.BracketedBlockEntityBehaviour;
|
||||||
|
@ -36,7 +38,9 @@ import net.minecraft.world.level.BlockGetter;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.level.LevelAccessor;
|
import net.minecraft.world.level.LevelAccessor;
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
|
import net.minecraft.world.level.block.Mirror;
|
||||||
import net.minecraft.world.level.block.PipeBlock;
|
import net.minecraft.world.level.block.PipeBlock;
|
||||||
|
import net.minecraft.world.level.block.Rotation;
|
||||||
import net.minecraft.world.level.block.SimpleWaterloggedBlock;
|
import net.minecraft.world.level.block.SimpleWaterloggedBlock;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
@ -50,8 +54,8 @@ import net.minecraft.world.phys.Vec3;
|
||||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||||
import net.minecraft.world.ticks.TickPriority;
|
import net.minecraft.world.ticks.TickPriority;
|
||||||
|
|
||||||
public class FluidPipeBlock extends PipeBlock
|
public class FluidPipeBlock extends PipeBlock implements SimpleWaterloggedBlock, IWrenchableWithBracket,
|
||||||
implements SimpleWaterloggedBlock, IWrenchableWithBracket, IBE<FluidPipeBlockEntity>, EncasableBlock {
|
IBE<FluidPipeBlockEntity>, EncasableBlock, ITransformableBlock {
|
||||||
|
|
||||||
private static final VoxelShape OCCLUSION_BOX = Block.box(4, 4, 4, 12, 12, 12);
|
private static final VoxelShape OCCLUSION_BOX = Block.box(4, 4, 4, 12, 12, 12);
|
||||||
|
|
||||||
|
@ -337,4 +341,20 @@ public class FluidPipeBlock extends PipeBlock
|
||||||
public VoxelShape getOcclusionShape(BlockState pState, BlockGetter pLevel, BlockPos pPos) {
|
public VoxelShape getOcclusionShape(BlockState pState, BlockGetter pLevel, BlockPos pPos) {
|
||||||
return OCCLUSION_BOX;
|
return OCCLUSION_BOX;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockState rotate(BlockState pState, Rotation pRotation) {
|
||||||
|
return FluidPipeBlockRotation.rotate(pState, pRotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockState mirror(BlockState pState, Mirror pMirror) {
|
||||||
|
return FluidPipeBlockRotation.mirror(pState, pMirror);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlockState transform(BlockState state, StructureTransform transform) {
|
||||||
|
return FluidPipeBlockRotation.transform(state, transform);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
package com.simibubi.create.content.contraptions.fluids.pipes;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.simibubi.create.content.contraptions.components.structureMovement.StructureTransform;
|
||||||
|
import com.simibubi.create.foundation.utility.Iterate;
|
||||||
|
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.world.level.block.Mirror;
|
||||||
|
import net.minecraft.world.level.block.PipeBlock;
|
||||||
|
import net.minecraft.world.level.block.Rotation;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.block.state.properties.BooleanProperty;
|
||||||
|
|
||||||
|
public class FluidPipeBlockRotation {
|
||||||
|
|
||||||
|
public static final Map<Direction, BooleanProperty> FACING_TO_PROPERTY_MAP = PipeBlock.PROPERTY_BY_DIRECTION;
|
||||||
|
|
||||||
|
public static BlockState rotate(BlockState state, Rotation rotation) {
|
||||||
|
BlockState rotated = state;
|
||||||
|
for (Direction direction : Iterate.horizontalDirections)
|
||||||
|
rotated = rotated.setValue(FACING_TO_PROPERTY_MAP.get(rotation.rotate(direction)),
|
||||||
|
state.getValue(FACING_TO_PROPERTY_MAP.get(direction)));
|
||||||
|
return rotated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BlockState mirror(BlockState state, Mirror mirror) {
|
||||||
|
BlockState mirrored = state;
|
||||||
|
for (Direction direction : Iterate.horizontalDirections)
|
||||||
|
mirrored = mirrored.setValue(FACING_TO_PROPERTY_MAP.get(mirror.mirror(direction)),
|
||||||
|
state.getValue(FACING_TO_PROPERTY_MAP.get(direction)));
|
||||||
|
return mirrored;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BlockState transform(BlockState state, StructureTransform transform) {
|
||||||
|
if (transform.mirror != null)
|
||||||
|
state = mirror(state, transform.mirror);
|
||||||
|
|
||||||
|
if (transform.rotationAxis == Direction.Axis.Y)
|
||||||
|
return rotate(state, transform.rotation);
|
||||||
|
|
||||||
|
BlockState rotated = state;
|
||||||
|
for (Direction direction : Iterate.directions)
|
||||||
|
rotated = rotated.setValue(FACING_TO_PROPERTY_MAP.get(transform.rotateFacing(direction)),
|
||||||
|
state.getValue(FACING_TO_PROPERTY_MAP.get(direction)));
|
||||||
|
return rotated;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -44,7 +44,8 @@ public class SequencedAssemblyRecipe implements Recipe<RecipeWrapper> {
|
||||||
protected List<SequencedRecipe<?>> sequence;
|
protected List<SequencedRecipe<?>> sequence;
|
||||||
protected int loops;
|
protected int loops;
|
||||||
protected ProcessingOutput transitionalItem;
|
protected ProcessingOutput transitionalItem;
|
||||||
protected List<ProcessingOutput> resultPool;
|
|
||||||
|
public final List<ProcessingOutput> resultPool;
|
||||||
|
|
||||||
public SequencedAssemblyRecipe(ResourceLocation recipeId, SequencedAssemblyRecipeSerializer serializer) {
|
public SequencedAssemblyRecipe(ResourceLocation recipeId, SequencedAssemblyRecipeSerializer serializer) {
|
||||||
this.id = recipeId;
|
this.id = recipeId;
|
||||||
|
|
|
@ -2,6 +2,11 @@ package com.simibubi.create.content.contraptions.relays.advanced;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.simibubi.create.compat.computercraft.AbstractComputerBehaviour;
|
||||||
|
import com.simibubi.create.compat.computercraft.ComputerCraftProxy;
|
||||||
import com.simibubi.create.content.contraptions.RotationPropagator;
|
import com.simibubi.create.content.contraptions.RotationPropagator;
|
||||||
import com.simibubi.create.content.contraptions.base.KineticBlockEntity;
|
import com.simibubi.create.content.contraptions.base.KineticBlockEntity;
|
||||||
import com.simibubi.create.content.contraptions.components.motor.KineticScrollValueBehaviour;
|
import com.simibubi.create.content.contraptions.components.motor.KineticScrollValueBehaviour;
|
||||||
|
@ -20,11 +25,14 @@ import net.minecraft.core.Direction;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
import net.minecraftforge.common.capabilities.Capability;
|
||||||
|
import net.minecraftforge.common.util.LazyOptional;
|
||||||
|
|
||||||
public class SpeedControllerBlockEntity extends KineticBlockEntity {
|
public class SpeedControllerBlockEntity extends KineticBlockEntity {
|
||||||
|
|
||||||
public static final int DEFAULT_SPEED = 16;
|
public static final int DEFAULT_SPEED = 16;
|
||||||
protected ScrollValueBehaviour targetSpeed;
|
public ScrollValueBehaviour targetSpeed;
|
||||||
|
public AbstractComputerBehaviour computerBehaviour;
|
||||||
|
|
||||||
boolean hasBracket;
|
boolean hasBracket;
|
||||||
|
|
||||||
|
@ -50,6 +58,7 @@ public class SpeedControllerBlockEntity extends KineticBlockEntity {
|
||||||
targetSpeed.value = DEFAULT_SPEED;
|
targetSpeed.value = DEFAULT_SPEED;
|
||||||
targetSpeed.withCallback(i -> this.updateTargetRotation());
|
targetSpeed.withCallback(i -> this.updateTargetRotation());
|
||||||
behaviours.add(targetSpeed);
|
behaviours.add(targetSpeed);
|
||||||
|
behaviours.add(computerBehaviour = ComputerCraftProxy.behaviour(this));
|
||||||
|
|
||||||
registerAwardables(behaviours, AllAdvancements.SPEED_CONTROLLER);
|
registerAwardables(behaviours, AllAdvancements.SPEED_CONTROLLER);
|
||||||
}
|
}
|
||||||
|
@ -125,6 +134,20 @@ public class SpeedControllerBlockEntity extends KineticBlockEntity {
|
||||||
.isHorizontal();
|
.isHorizontal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public <T> LazyOptional<T> getCapability(@NotNull Capability<T> cap, @Nullable Direction side) {
|
||||||
|
if (computerBehaviour.isPeripheralCap(cap))
|
||||||
|
return computerBehaviour.getPeripheralCapability();
|
||||||
|
return super.getCapability(cap, side);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidateCaps() {
|
||||||
|
super.invalidateCaps();
|
||||||
|
computerBehaviour.removePeripheral();
|
||||||
|
}
|
||||||
|
|
||||||
private class ControllerValueBoxTransform extends ValueBoxTransform.Sided {
|
private class ControllerValueBoxTransform extends ValueBoxTransform.Sided {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -35,6 +35,9 @@ public class ConfigureSequencedGearshiftPacket extends BlockEntityConfigurationP
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void applySettings(SequencedGearshiftBlockEntity be) {
|
protected void applySettings(SequencedGearshiftBlockEntity be) {
|
||||||
|
if (be.computerBehaviour.hasAttachedComputer())
|
||||||
|
return;
|
||||||
|
|
||||||
be.run(-1);
|
be.run(-1);
|
||||||
be.instructions = Instruction.deserializeAll(instructions);
|
be.instructions = Instruction.deserializeAll(instructions);
|
||||||
be.sendData();
|
be.sendData();
|
||||||
|
|
|
@ -19,8 +19,12 @@ public class Instruction {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Instruction(SequencerInstructions instruction, int value) {
|
public Instruction(SequencerInstructions instruction, int value) {
|
||||||
|
this(instruction, InstructionSpeedModifiers.FORWARD, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Instruction(SequencerInstructions instruction, InstructionSpeedModifiers speedModifier, int value) {
|
||||||
this.instruction = instruction;
|
this.instruction = instruction;
|
||||||
speedModifier = InstructionSpeedModifiers.FORWARD;
|
this.speedModifier = speedModifier;
|
||||||
this.value = value;
|
this.value = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package com.simibubi.create.content.contraptions.relays.advanced.sequencer;
|
package com.simibubi.create.content.contraptions.relays.advanced.sequencer;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import com.simibubi.create.foundation.utility.Components;
|
import com.simibubi.create.foundation.utility.Components;
|
||||||
|
@ -36,4 +37,11 @@ public enum InstructionSpeedModifiers {
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static InstructionSpeedModifiers getByModifier(int modifier) {
|
||||||
|
return Arrays.stream(InstructionSpeedModifiers.values())
|
||||||
|
.filter(speedModifier -> speedModifier.value == modifier)
|
||||||
|
.findAny()
|
||||||
|
.orElse(InstructionSpeedModifiers.FORWARD);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,17 @@
|
||||||
package com.simibubi.create.content.contraptions.relays.advanced.sequencer;
|
package com.simibubi.create.content.contraptions.relays.advanced.sequencer;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
import java.util.Vector;
|
import java.util.Vector;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import com.simibubi.create.compat.computercraft.AbstractComputerBehaviour;
|
||||||
|
import com.simibubi.create.compat.computercraft.ComputerCraftProxy;
|
||||||
import com.simibubi.create.content.contraptions.base.KineticBlockEntity;
|
import com.simibubi.create.content.contraptions.base.KineticBlockEntity;
|
||||||
import com.simibubi.create.content.contraptions.relays.encased.SplitShaftBlockEntity;
|
import com.simibubi.create.content.contraptions.relays.encased.SplitShaftBlockEntity;
|
||||||
|
import com.simibubi.create.foundation.blockEntity.BlockEntityBehaviour;
|
||||||
import com.simibubi.create.foundation.utility.NBTHelper;
|
import com.simibubi.create.foundation.utility.NBTHelper;
|
||||||
|
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
|
@ -12,6 +20,8 @@ import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.nbt.Tag;
|
import net.minecraft.nbt.Tag;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraftforge.common.capabilities.Capability;
|
||||||
|
import net.minecraftforge.common.util.LazyOptional;
|
||||||
|
|
||||||
public class SequencedGearshiftBlockEntity extends SplitShaftBlockEntity {
|
public class SequencedGearshiftBlockEntity extends SplitShaftBlockEntity {
|
||||||
|
|
||||||
|
@ -22,6 +32,8 @@ public class SequencedGearshiftBlockEntity extends SplitShaftBlockEntity {
|
||||||
int timer;
|
int timer;
|
||||||
boolean poweredPreviously;
|
boolean poweredPreviously;
|
||||||
|
|
||||||
|
public AbstractComputerBehaviour computerBehaviour;
|
||||||
|
|
||||||
public record SequenceContext(SequencerInstructions instruction, double relativeValue) {
|
public record SequenceContext(SequencerInstructions instruction, double relativeValue) {
|
||||||
|
|
||||||
public static SequenceContext fromGearshift(SequencerInstructions instruction, double kineticSpeed,
|
public static SequenceContext fromGearshift(SequencerInstructions instruction, double kineticSpeed,
|
||||||
|
@ -61,6 +73,12 @@ public class SequencedGearshiftBlockEntity extends SplitShaftBlockEntity {
|
||||||
poweredPreviously = false;
|
poweredPreviously = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addBehaviours(List<BlockEntityBehaviour> behaviours) {
|
||||||
|
super.addBehaviours(behaviours);
|
||||||
|
behaviours.add(computerBehaviour = ComputerCraftProxy.behaviour(this));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void tick() {
|
public void tick() {
|
||||||
super.tick();
|
super.tick();
|
||||||
|
@ -103,6 +121,8 @@ public class SequencedGearshiftBlockEntity extends SplitShaftBlockEntity {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onRedstoneUpdate(boolean isPowered, boolean isRunning) {
|
public void onRedstoneUpdate(boolean isPowered, boolean isRunning) {
|
||||||
|
if (computerBehaviour.hasAttachedComputer())
|
||||||
|
return;
|
||||||
if (!poweredPreviously && isPowered)
|
if (!poweredPreviously && isPowered)
|
||||||
risingFlank();
|
risingFlank();
|
||||||
poweredPreviously = isPowered;
|
poweredPreviously = isPowered;
|
||||||
|
@ -136,7 +156,7 @@ public class SequencedGearshiftBlockEntity extends SplitShaftBlockEntity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void run(int instructionIndex) {
|
public void run(int instructionIndex) {
|
||||||
Instruction instruction = getInstruction(instructionIndex);
|
Instruction instruction = getInstruction(instructionIndex);
|
||||||
if (instruction == null || instruction.instruction == SequencerInstructions.END) {
|
if (instruction == null || instruction.instruction == SequencerInstructions.END) {
|
||||||
if (getModifier() != 0)
|
if (getModifier() != 0)
|
||||||
|
@ -193,6 +213,20 @@ public class SequencedGearshiftBlockEntity extends SplitShaftBlockEntity {
|
||||||
super.read(compound, clientPacket);
|
super.read(compound, clientPacket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public <T> LazyOptional<T> getCapability(@NotNull Capability<T> cap, @Nullable Direction side) {
|
||||||
|
if (computerBehaviour.isPeripheralCap(cap))
|
||||||
|
return computerBehaviour.getPeripheralCapability();
|
||||||
|
return super.getCapability(cap, side);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidateCaps() {
|
||||||
|
super.invalidateCaps();
|
||||||
|
computerBehaviour.removePeripheral();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public float getRotationSpeedModifier(Direction face) {
|
public float getRotationSpeedModifier(Direction face) {
|
||||||
if (isVirtual())
|
if (isVirtual())
|
||||||
|
@ -208,4 +242,8 @@ public class SequencedGearshiftBlockEntity extends SplitShaftBlockEntity {
|
||||||
.getSpeedModifier();
|
.getSpeedModifier();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Vector<Instruction> getInstructions() {
|
||||||
|
return this.instructions;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import java.util.Vector;
|
||||||
|
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
import com.simibubi.create.AllBlocks;
|
import com.simibubi.create.AllBlocks;
|
||||||
|
import com.simibubi.create.compat.computercraft.ComputerScreen;
|
||||||
import com.simibubi.create.foundation.gui.AbstractSimiScreen;
|
import com.simibubi.create.foundation.gui.AbstractSimiScreen;
|
||||||
import com.simibubi.create.foundation.gui.AllGuiTextures;
|
import com.simibubi.create.foundation.gui.AllGuiTextures;
|
||||||
import com.simibubi.create.foundation.gui.AllIcons;
|
import com.simibubi.create.foundation.gui.AllIcons;
|
||||||
|
@ -15,7 +16,6 @@ import com.simibubi.create.foundation.networking.AllPackets;
|
||||||
import com.simibubi.create.foundation.utility.Components;
|
import com.simibubi.create.foundation.utility.Components;
|
||||||
import com.simibubi.create.foundation.utility.Lang;
|
import com.simibubi.create.foundation.utility.Lang;
|
||||||
|
|
||||||
import net.minecraft.core.BlockPos;
|
|
||||||
import net.minecraft.nbt.ListTag;
|
import net.minecraft.nbt.ListTag;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
@ -25,22 +25,26 @@ public class SequencedGearshiftScreen extends AbstractSimiScreen {
|
||||||
private final ItemStack renderedItem = AllBlocks.SEQUENCED_GEARSHIFT.asStack();
|
private final ItemStack renderedItem = AllBlocks.SEQUENCED_GEARSHIFT.asStack();
|
||||||
private final AllGuiTextures background = AllGuiTextures.SEQUENCER;
|
private final AllGuiTextures background = AllGuiTextures.SEQUENCER;
|
||||||
private IconButton confirmButton;
|
private IconButton confirmButton;
|
||||||
|
private SequencedGearshiftBlockEntity be;
|
||||||
|
|
||||||
private ListTag compareTag;
|
private ListTag compareTag;
|
||||||
private Vector<Instruction> instructions;
|
private Vector<Instruction> instructions;
|
||||||
private BlockPos pos;
|
|
||||||
|
|
||||||
private Vector<Vector<ScrollInput>> inputs;
|
private Vector<Vector<ScrollInput>> inputs;
|
||||||
|
|
||||||
public SequencedGearshiftScreen(SequencedGearshiftBlockEntity be) {
|
public SequencedGearshiftScreen(SequencedGearshiftBlockEntity be) {
|
||||||
super(Lang.translateDirect("gui.sequenced_gearshift.title"));
|
super(Lang.translateDirect("gui.sequenced_gearshift.title"));
|
||||||
this.instructions = be.instructions;
|
this.instructions = be.instructions;
|
||||||
this.pos = be.getBlockPos();
|
this.be = be;
|
||||||
compareTag = Instruction.serializeAll(instructions);
|
compareTag = Instruction.serializeAll(instructions);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void init() {
|
protected void init() {
|
||||||
|
if (be.computerBehaviour.hasAttachedComputer())
|
||||||
|
minecraft.setScreen(
|
||||||
|
new ComputerScreen(title, this::renderAdditional, this, be.computerBehaviour::hasAttachedComputer));
|
||||||
|
|
||||||
setWindowSize(background.width, background.height);
|
setWindowSize(background.width, background.height);
|
||||||
setWindowOffset(-20, 0);
|
setWindowOffset(-20, 0);
|
||||||
super.init();
|
super.init();
|
||||||
|
@ -55,8 +59,7 @@ public class SequencedGearshiftScreen extends AbstractSimiScreen {
|
||||||
for (int row = 0; row < instructions.size(); row++)
|
for (int row = 0; row < instructions.size(); row++)
|
||||||
initInputsOfRow(row, x, y);
|
initInputsOfRow(row, x, y);
|
||||||
|
|
||||||
confirmButton =
|
confirmButton = new IconButton(x + background.width - 33, y + background.height - 24, AllIcons.I_CONFIRM);
|
||||||
new IconButton(x + background.width - 33, y + background.height - 24, AllIcons.I_CONFIRM);
|
|
||||||
confirmButton.withCallback(() -> {
|
confirmButton.withCallback(() -> {
|
||||||
onClose();
|
onClose();
|
||||||
});
|
});
|
||||||
|
@ -127,6 +130,15 @@ public class SequencedGearshiftScreen extends AbstractSimiScreen {
|
||||||
modifier.setState(instruction.speedModifier.ordinal());
|
modifier.setState(instruction.speedModifier.ordinal());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tick() {
|
||||||
|
super.tick();
|
||||||
|
|
||||||
|
if (be.computerBehaviour.hasAttachedComputer())
|
||||||
|
minecraft.setScreen(
|
||||||
|
new ComputerScreen(title, this::renderAdditional, this, be.computerBehaviour::hasAttachedComputer));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderWindow(PoseStack ms, int mouseX, int mouseY, float partialTicks) {
|
protected void renderWindow(PoseStack ms, int mouseX, int mouseY, float partialTicks) {
|
||||||
int x = guiLeft;
|
int x = guiLeft;
|
||||||
|
@ -134,6 +146,13 @@ public class SequencedGearshiftScreen extends AbstractSimiScreen {
|
||||||
|
|
||||||
background.render(ms, x, y, this);
|
background.render(ms, x, y, this);
|
||||||
|
|
||||||
|
for (int row = 0; row < instructions.capacity(); row++) {
|
||||||
|
AllGuiTextures toDraw = AllGuiTextures.SEQUENCER_EMPTY;
|
||||||
|
int yOffset = toDraw.height * row;
|
||||||
|
|
||||||
|
toDraw.render(ms, x, y + 14 + yOffset, this);
|
||||||
|
}
|
||||||
|
|
||||||
for (int row = 0; row < instructions.capacity(); row++) {
|
for (int row = 0; row < instructions.capacity(); row++) {
|
||||||
AllGuiTextures toDraw = AllGuiTextures.SEQUENCER_EMPTY;
|
AllGuiTextures toDraw = AllGuiTextures.SEQUENCER_EMPTY;
|
||||||
int yOffset = toDraw.height * row;
|
int yOffset = toDraw.height * row;
|
||||||
|
@ -157,9 +176,13 @@ public class SequencedGearshiftScreen extends AbstractSimiScreen {
|
||||||
}
|
}
|
||||||
|
|
||||||
font.draw(ms, title, x + (background.width - 8) / 2 - font.width(title) / 2, y + 4, 0x592424);
|
font.draw(ms, title, x + (background.width - 8) / 2 - font.width(title) / 2, y + 4, 0x592424);
|
||||||
|
renderAdditional(ms, mouseX, mouseY, partialTicks, x, y, background);
|
||||||
|
}
|
||||||
|
|
||||||
GuiGameElement.of(renderedItem)
|
private void renderAdditional(PoseStack ms, int mouseX, int mouseY, float partialTicks, int guiLeft, int guiTop,
|
||||||
.<GuiGameElement.GuiRenderBuilder>at(x + background.width + 6, y + background.height - 56, -200)
|
AllGuiTextures background) {
|
||||||
|
GuiGameElement.of(renderedItem).<GuiGameElement
|
||||||
|
.GuiRenderBuilder>at(guiLeft + background.width + 6, guiTop + background.height - 56, 100)
|
||||||
.scale(5)
|
.scale(5)
|
||||||
.render(ms);
|
.render(ms);
|
||||||
}
|
}
|
||||||
|
@ -172,7 +195,8 @@ public class SequencedGearshiftScreen extends AbstractSimiScreen {
|
||||||
ListTag serialized = Instruction.serializeAll(instructions);
|
ListTag serialized = Instruction.serializeAll(instructions);
|
||||||
if (serialized.equals(compareTag))
|
if (serialized.equals(compareTag))
|
||||||
return;
|
return;
|
||||||
AllPackets.getChannel().sendToServer(new ConfigureSequencedGearshiftPacket(pos, serialized));
|
AllPackets.getChannel()
|
||||||
|
.sendToServer(new ConfigureSequencedGearshiftPacket(be.getBlockPos(), serialized));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -12,7 +12,7 @@ import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
|
||||||
public class GaugeBlockEntity extends KineticBlockEntity implements IHaveGoggleInformation {
|
public abstract class GaugeBlockEntity extends KineticBlockEntity implements IHaveGoggleInformation {
|
||||||
|
|
||||||
public float dialTarget;
|
public float dialTarget;
|
||||||
public float dialState;
|
public float dialState;
|
||||||
|
@ -52,4 +52,5 @@ public class GaugeBlockEntity extends KineticBlockEntity implements IHaveGoggleI
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,24 +2,41 @@ package com.simibubi.create.content.contraptions.relays.gauge;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.simibubi.create.compat.computercraft.AbstractComputerBehaviour;
|
||||||
|
import com.simibubi.create.compat.computercraft.ComputerCraftProxy;
|
||||||
import com.simibubi.create.content.contraptions.base.IRotate.SpeedLevel;
|
import com.simibubi.create.content.contraptions.base.IRotate.SpeedLevel;
|
||||||
|
import com.simibubi.create.foundation.blockEntity.BlockEntityBehaviour;
|
||||||
import com.simibubi.create.foundation.config.AllConfigs;
|
import com.simibubi.create.foundation.config.AllConfigs;
|
||||||
import com.simibubi.create.foundation.utility.Color;
|
import com.simibubi.create.foundation.utility.Color;
|
||||||
import com.simibubi.create.foundation.utility.Lang;
|
import com.simibubi.create.foundation.utility.Lang;
|
||||||
|
|
||||||
import net.minecraft.ChatFormatting;
|
import net.minecraft.ChatFormatting;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.util.Mth;
|
import net.minecraft.util.Mth;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraftforge.common.capabilities.Capability;
|
||||||
|
import net.minecraftforge.common.util.LazyOptional;
|
||||||
|
|
||||||
public class SpeedGaugeBlockEntity extends GaugeBlockEntity {
|
public class SpeedGaugeBlockEntity extends GaugeBlockEntity {
|
||||||
|
|
||||||
|
public AbstractComputerBehaviour computerBehaviour;
|
||||||
|
|
||||||
public SpeedGaugeBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
|
public SpeedGaugeBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
|
||||||
super(type, pos, state);
|
super(type, pos, state);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addBehaviours(List<BlockEntityBehaviour> behaviours) {
|
||||||
|
super.addBehaviours(behaviours);
|
||||||
|
behaviours.add(computerBehaviour = ComputerCraftProxy.behaviour(this));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSpeedChanged(float prevSpeed) {
|
public void onSpeedChanged(float prevSpeed) {
|
||||||
super.onSpeedChanged(prevSpeed);
|
super.onSpeedChanged(prevSpeed);
|
||||||
|
@ -62,4 +79,19 @@ public class SpeedGaugeBlockEntity extends GaugeBlockEntity {
|
||||||
.forGoggles(tooltip);
|
.forGoggles(tooltip);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public <T> LazyOptional<T> getCapability(@NotNull Capability<T> cap, @Nullable Direction side) {
|
||||||
|
if (computerBehaviour.isPeripheralCap(cap))
|
||||||
|
return computerBehaviour.getPeripheralCapability();
|
||||||
|
return super.getCapability(cap, side);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidateCaps() {
|
||||||
|
super.invalidateCaps();
|
||||||
|
computerBehaviour.removePeripheral();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,11 @@ package com.simibubi.create.content.contraptions.relays.gauge;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.simibubi.create.compat.computercraft.AbstractComputerBehaviour;
|
||||||
|
import com.simibubi.create.compat.computercraft.ComputerCraftProxy;
|
||||||
import com.simibubi.create.content.contraptions.base.IRotate.StressImpact;
|
import com.simibubi.create.content.contraptions.base.IRotate.StressImpact;
|
||||||
import com.simibubi.create.foundation.advancement.AllAdvancements;
|
import com.simibubi.create.foundation.advancement.AllAdvancements;
|
||||||
import com.simibubi.create.foundation.blockEntity.BlockEntityBehaviour;
|
import com.simibubi.create.foundation.blockEntity.BlockEntityBehaviour;
|
||||||
|
@ -13,14 +18,19 @@ import com.simibubi.create.foundation.utility.LangBuilder;
|
||||||
|
|
||||||
import net.minecraft.ChatFormatting;
|
import net.minecraft.ChatFormatting;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.util.Mth;
|
import net.minecraft.util.Mth;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraftforge.common.capabilities.Capability;
|
||||||
|
import net.minecraftforge.common.util.LazyOptional;
|
||||||
|
|
||||||
public class StressGaugeBlockEntity extends GaugeBlockEntity {
|
public class StressGaugeBlockEntity extends GaugeBlockEntity {
|
||||||
|
|
||||||
|
public AbstractComputerBehaviour computerBehaviour;
|
||||||
|
|
||||||
static BlockPos lastSent;
|
static BlockPos lastSent;
|
||||||
|
|
||||||
public StressGaugeBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
|
public StressGaugeBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
|
||||||
|
@ -30,6 +40,7 @@ public class StressGaugeBlockEntity extends GaugeBlockEntity {
|
||||||
@Override
|
@Override
|
||||||
public void addBehaviours(List<BlockEntityBehaviour> behaviours) {
|
public void addBehaviours(List<BlockEntityBehaviour> behaviours) {
|
||||||
super.addBehaviours(behaviours);
|
super.addBehaviours(behaviours);
|
||||||
|
behaviours.add(computerBehaviour = ComputerCraftProxy.behaviour(this));
|
||||||
registerAwardables(behaviours, AllAdvancements.STRESSOMETER, AllAdvancements.STRESSOMETER_MAXED);
|
registerAwardables(behaviours, AllAdvancements.STRESSOMETER, AllAdvancements.STRESSOMETER_MAXED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -141,4 +152,18 @@ public class StressGaugeBlockEntity extends GaugeBlockEntity {
|
||||||
award(AllAdvancements.STRESSOMETER_MAXED);
|
award(AllAdvancements.STRESSOMETER_MAXED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public <T> LazyOptional<T> getCapability(@NotNull Capability<T> cap, @Nullable Direction side) {
|
||||||
|
if (computerBehaviour.isPeripheralCap(cap))
|
||||||
|
return computerBehaviour.getPeripheralCapability();
|
||||||
|
return super.getCapability(cap, side);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidateCaps() {
|
||||||
|
super.invalidateCaps();
|
||||||
|
computerBehaviour.removePeripheral();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ import com.simibubi.create.foundation.blockEntity.behaviour.filtering.FilteringB
|
||||||
import com.simibubi.create.foundation.blockEntity.behaviour.filtering.SidedFilteringBehaviour;
|
import com.simibubi.create.foundation.blockEntity.behaviour.filtering.SidedFilteringBehaviour;
|
||||||
import com.simibubi.create.foundation.blockEntity.behaviour.scrollvalue.INamedIconOptions;
|
import com.simibubi.create.foundation.blockEntity.behaviour.scrollvalue.INamedIconOptions;
|
||||||
import com.simibubi.create.foundation.blockEntity.behaviour.scrollvalue.ScrollOptionBehaviour;
|
import com.simibubi.create.foundation.blockEntity.behaviour.scrollvalue.ScrollOptionBehaviour;
|
||||||
|
import com.simibubi.create.foundation.config.AllConfigs;
|
||||||
import com.simibubi.create.foundation.gui.AllIcons;
|
import com.simibubi.create.foundation.gui.AllIcons;
|
||||||
import com.simibubi.create.foundation.utility.BlockHelper;
|
import com.simibubi.create.foundation.utility.BlockHelper;
|
||||||
import com.simibubi.create.foundation.utility.Components;
|
import com.simibubi.create.foundation.utility.Components;
|
||||||
|
@ -174,7 +175,7 @@ public class BrassTunnelBlockEntity extends BeltTunnelBlockEntity implements IHa
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (selectionMode.get() != SelectionMode.SYNCHRONIZE || syncedOutputActive) {
|
if (selectionMode.get() != SelectionMode.SYNCHRONIZE || syncedOutputActive) {
|
||||||
distributionProgress = 10;
|
distributionProgress = AllConfigs.server().logistics.brassTunnelTimer.get();
|
||||||
sendData();
|
sendData();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -7,6 +7,7 @@ import com.simibubi.create.foundation.blockEntity.SmartBlockEntity;
|
||||||
|
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraftforge.common.capabilities.Capability;
|
import net.minecraftforge.common.capabilities.Capability;
|
||||||
|
@ -33,4 +34,8 @@ public class DepotBlockEntity extends SmartBlockEntity {
|
||||||
return depotBehaviour.getItemCapability(cap, side);
|
return depotBehaviour.getItemCapability(cap, side);
|
||||||
return super.getCapability(cap, side);
|
return super.getCapability(cap, side);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ItemStack getHeldItem() {
|
||||||
|
return depotBehaviour.getHeldItemStack();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,8 @@ import java.util.Map;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import com.simibubi.create.Create;
|
import com.simibubi.create.Create;
|
||||||
|
import com.simibubi.create.compat.Mods;
|
||||||
|
import com.simibubi.create.content.logistics.block.display.source.ComputerDisplaySource;
|
||||||
import com.simibubi.create.content.logistics.block.display.source.DeathCounterDisplaySource;
|
import com.simibubi.create.content.logistics.block.display.source.DeathCounterDisplaySource;
|
||||||
import com.simibubi.create.content.logistics.block.display.source.DisplaySource;
|
import com.simibubi.create.content.logistics.block.display.source.DisplaySource;
|
||||||
import com.simibubi.create.content.logistics.block.display.source.EnchantPowerDisplaySource;
|
import com.simibubi.create.content.logistics.block.display.source.EnchantPowerDisplaySource;
|
||||||
|
@ -241,5 +243,14 @@ public class AllDisplayBehaviours {
|
||||||
assignBlockEntity(register(Create.asResource("scoreboard_display_source"), new ScoreboardDisplaySource()), BlockEntityType.COMMAND_BLOCK);
|
assignBlockEntity(register(Create.asResource("scoreboard_display_source"), new ScoreboardDisplaySource()), BlockEntityType.COMMAND_BLOCK);
|
||||||
assignBlockEntity(register(Create.asResource("enchant_power_display_source"), new EnchantPowerDisplaySource()), BlockEntityType.ENCHANTING_TABLE);
|
assignBlockEntity(register(Create.asResource("enchant_power_display_source"), new EnchantPowerDisplaySource()), BlockEntityType.ENCHANTING_TABLE);
|
||||||
assignBlock(register(Create.asResource("redstone_power_display_source"), new RedstonePowerDisplaySource()), Blocks.TARGET);
|
assignBlock(register(Create.asResource("redstone_power_display_source"), new RedstonePowerDisplaySource()), Blocks.TARGET);
|
||||||
|
|
||||||
|
Mods.COMPUTERCRAFT.executeIfInstalled(() -> () -> {
|
||||||
|
DisplayBehaviour computerDisplaySource = register(Create.asResource("computer_display_source"), new ComputerDisplaySource());
|
||||||
|
|
||||||
|
assignTile(computerDisplaySource, new ResourceLocation(Mods.COMPUTERCRAFT.asId(), "wired_modem_full"));
|
||||||
|
assignTile(computerDisplaySource, new ResourceLocation(Mods.COMPUTERCRAFT.asId(), "computer_normal"));
|
||||||
|
assignTile(computerDisplaySource, new ResourceLocation(Mods.COMPUTERCRAFT.asId(), "computer_advanced"));
|
||||||
|
assignTile(computerDisplaySource, new ResourceLocation(Mods.COMPUTERCRAFT.asId(), "computer_command"));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,11 @@ package com.simibubi.create.content.logistics.block.display;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import com.simibubi.create.compat.computercraft.AbstractComputerBehaviour;
|
||||||
|
import com.simibubi.create.compat.computercraft.ComputerCraftProxy;
|
||||||
import com.simibubi.create.content.logistics.block.display.source.DisplaySource;
|
import com.simibubi.create.content.logistics.block.display.source.DisplaySource;
|
||||||
import com.simibubi.create.content.logistics.block.display.target.DisplayTarget;
|
import com.simibubi.create.content.logistics.block.display.target.DisplayTarget;
|
||||||
import com.simibubi.create.foundation.advancement.AllAdvancements;
|
import com.simibubi.create.foundation.advancement.AllAdvancements;
|
||||||
|
@ -18,6 +23,8 @@ import net.minecraft.nbt.NbtUtils;
|
||||||
import net.minecraft.resources.ResourceLocation;
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraftforge.common.capabilities.Capability;
|
||||||
|
import net.minecraftforge.common.util.LazyOptional;
|
||||||
|
|
||||||
public class DisplayLinkBlockEntity extends SmartBlockEntity {
|
public class DisplayLinkBlockEntity extends SmartBlockEntity {
|
||||||
|
|
||||||
|
@ -33,6 +40,7 @@ public class DisplayLinkBlockEntity extends SmartBlockEntity {
|
||||||
private boolean sendPulse;
|
private boolean sendPulse;
|
||||||
|
|
||||||
public int refreshTicks;
|
public int refreshTicks;
|
||||||
|
public AbstractComputerBehaviour computerBehaviour;
|
||||||
|
|
||||||
public DisplayLinkBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
|
public DisplayLinkBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
|
||||||
super(type, pos, state);
|
super(type, pos, state);
|
||||||
|
@ -44,6 +52,12 @@ public class DisplayLinkBlockEntity extends SmartBlockEntity {
|
||||||
glow.chase(0, 0.5f, Chaser.EXP);
|
glow.chase(0, 0.5f, Chaser.EXP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addBehaviours(List<BlockEntityBehaviour> behaviours) {
|
||||||
|
behaviours.add(computerBehaviour = ComputerCraftProxy.behaviour(this));
|
||||||
|
registerAwardables(behaviours, AllAdvancements.DISPLAY_LINK, AllAdvancements.DISPLAY_BOARD);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void tick() {
|
public void tick() {
|
||||||
super.tick();
|
super.tick();
|
||||||
|
@ -61,7 +75,7 @@ public class DisplayLinkBlockEntity extends SmartBlockEntity {
|
||||||
}
|
}
|
||||||
|
|
||||||
refreshTicks++;
|
refreshTicks++;
|
||||||
if (refreshTicks < activeSource.getPassiveRefreshTicks())
|
if (refreshTicks < activeSource.getPassiveRefreshTicks() || !activeSource.shouldPassiveReset())
|
||||||
return;
|
return;
|
||||||
tickSource();
|
tickSource();
|
||||||
}
|
}
|
||||||
|
@ -118,11 +132,6 @@ public class DisplayLinkBlockEntity extends SmartBlockEntity {
|
||||||
award(AllAdvancements.DISPLAY_LINK);
|
award(AllAdvancements.DISPLAY_LINK);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addBehaviours(List<BlockEntityBehaviour> behaviours) {
|
|
||||||
registerAwardables(behaviours, AllAdvancements.DISPLAY_LINK, AllAdvancements.DISPLAY_BOARD);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void writeSafe(CompoundTag tag) {
|
public void writeSafe(CompoundTag tag) {
|
||||||
super.writeSafe(tag);
|
super.writeSafe(tag);
|
||||||
|
@ -173,6 +182,21 @@ public class DisplayLinkBlockEntity extends SmartBlockEntity {
|
||||||
sourceConfig = data.copy();
|
sourceConfig = data.copy();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
|
@Override
|
||||||
|
public <T> LazyOptional<T> getCapability(@NotNull Capability<T> cap, @Nullable Direction side) {
|
||||||
|
if (computerBehaviour.isPeripheralCap(cap))
|
||||||
|
return computerBehaviour.getPeripheralCapability();
|
||||||
|
|
||||||
|
return super.getCapability(cap, side);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidateCaps() {
|
||||||
|
super.invalidateCaps();
|
||||||
|
computerBehaviour.removePeripheral();
|
||||||
|
}
|
||||||
|
|
||||||
public void target(BlockPos targetPosition) {
|
public void target(BlockPos targetPosition) {
|
||||||
this.targetOffset = targetPosition.subtract(worldPosition);
|
this.targetOffset = targetPosition.subtract(worldPosition);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
package com.simibubi.create.content.logistics.block.display.source;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.simibubi.create.content.logistics.block.display.DisplayLinkContext;
|
||||||
|
import com.simibubi.create.content.logistics.block.display.target.DisplayTargetStats;
|
||||||
|
import com.simibubi.create.foundation.utility.Components;
|
||||||
|
|
||||||
|
import net.minecraft.nbt.ListTag;
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
|
import net.minecraft.network.chat.MutableComponent;
|
||||||
|
|
||||||
|
public class ComputerDisplaySource extends DisplaySource {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<MutableComponent> provideText(DisplayLinkContext context, DisplayTargetStats stats) {
|
||||||
|
List<MutableComponent> components = new ArrayList<>();
|
||||||
|
ListTag tag = context.sourceConfig().getList("ComputerSourceList", Tag.TAG_STRING);
|
||||||
|
|
||||||
|
for (int i = 0; i < tag.size(); i++) {
|
||||||
|
components.add(Components.literal(tag.getString(i)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return components;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldPassiveReset() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -49,6 +49,10 @@ public abstract class DisplaySource extends DisplayBehaviour {
|
||||||
return 100;
|
return 100;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public boolean shouldPassiveReset() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
protected String getTranslationKey() {
|
protected String getTranslationKey() {
|
||||||
return id.getPath();
|
return id.getPath();
|
||||||
}
|
}
|
||||||
|
|
|
@ -123,6 +123,10 @@ public class NixieTubeBlockEntity extends SmartBlockEntity {
|
||||||
customText = Optional.empty();
|
customText = Optional.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getRedstoneStrength() {
|
||||||
|
return redstoneStrength;
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -8,6 +8,7 @@ import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import com.simibubi.create.Create;
|
import com.simibubi.create.Create;
|
||||||
|
import com.simibubi.create.api.event.TrackGraphMergeEvent;
|
||||||
import com.simibubi.create.content.logistics.trains.TrackNodeLocation.DiscoveredLocation;
|
import com.simibubi.create.content.logistics.trains.TrackNodeLocation.DiscoveredLocation;
|
||||||
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalPropagator;
|
import com.simibubi.create.content.logistics.trains.management.edgePoint.signal.SignalPropagator;
|
||||||
|
|
||||||
|
@ -16,6 +17,7 @@ import net.minecraft.util.Mth;
|
||||||
import net.minecraft.world.level.LevelAccessor;
|
import net.minecraft.world.level.LevelAccessor;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
import net.minecraftforge.common.MinecraftForge;
|
||||||
|
|
||||||
public class TrackPropagator {
|
public class TrackPropagator {
|
||||||
|
|
||||||
|
@ -135,6 +137,7 @@ public class TrackPropagator {
|
||||||
if (graph == null)
|
if (graph == null)
|
||||||
graph = other;
|
graph = other;
|
||||||
else {
|
else {
|
||||||
|
MinecraftForge.EVENT_BUS.post(new TrackGraphMergeEvent(other, graph));
|
||||||
other.transferAll(graph);
|
other.transferAll(graph);
|
||||||
manager.removeGraphAndGroup(other);
|
manager.removeGraphAndGroup(other);
|
||||||
sync.graphRemoved(other);
|
sync.graphRemoved(other);
|
||||||
|
|
|
@ -108,7 +108,7 @@ public class Carriage {
|
||||||
|
|
||||||
DimensionalCarriageEntity dimensional = getDimensional(level);
|
DimensionalCarriageEntity dimensional = getDimensional(level);
|
||||||
dimensional.alignEntity(entity);
|
dimensional.alignEntity(entity);
|
||||||
dimensional.removeAndSaveEntity(entity, false);
|
dimensional.removeAndSaveEntity(entity, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DimensionalCarriageEntity getDimensional(Level level) {
|
public DimensionalCarriageEntity getDimensional(Level level) {
|
||||||
|
@ -739,8 +739,8 @@ public class Carriage {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dismountPlayer(ServerLevel sLevel, ServerPlayer sp, Integer seat, boolean portal) {
|
private void dismountPlayer(ServerLevel sLevel, ServerPlayer sp, Integer seat, boolean capture) {
|
||||||
if (!portal) {
|
if (!capture) {
|
||||||
sp.stopRiding();
|
sp.stopRiding();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,10 +17,6 @@ import java.util.function.Consumer;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import com.simibubi.create.content.logistics.trains.track.AbstractBogeyBlockEntity;
|
|
||||||
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
|
||||||
|
|
||||||
import org.apache.commons.lang3.mutable.MutableBoolean;
|
import org.apache.commons.lang3.mutable.MutableBoolean;
|
||||||
import org.apache.commons.lang3.mutable.MutableObject;
|
import org.apache.commons.lang3.mutable.MutableObject;
|
||||||
|
|
||||||
|
@ -46,6 +42,7 @@ import com.simibubi.create.content.logistics.trains.management.edgePoint.station
|
||||||
import com.simibubi.create.content.logistics.trains.management.edgePoint.station.StationBlockEntity;
|
import com.simibubi.create.content.logistics.trains.management.edgePoint.station.StationBlockEntity;
|
||||||
import com.simibubi.create.content.logistics.trains.management.schedule.ScheduleRuntime;
|
import com.simibubi.create.content.logistics.trains.management.schedule.ScheduleRuntime;
|
||||||
import com.simibubi.create.content.logistics.trains.management.schedule.ScheduleRuntime.State;
|
import com.simibubi.create.content.logistics.trains.management.schedule.ScheduleRuntime.State;
|
||||||
|
import com.simibubi.create.content.logistics.trains.track.AbstractBogeyBlockEntity;
|
||||||
import com.simibubi.create.foundation.advancement.AllAdvancements;
|
import com.simibubi.create.foundation.advancement.AllAdvancements;
|
||||||
import com.simibubi.create.foundation.config.AllConfigs;
|
import com.simibubi.create.foundation.config.AllConfigs;
|
||||||
import com.simibubi.create.foundation.networking.AllPackets;
|
import com.simibubi.create.foundation.networking.AllPackets;
|
||||||
|
@ -70,6 +67,7 @@ import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.level.Explosion.BlockInteraction;
|
import net.minecraft.world.level.Explosion.BlockInteraction;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.phys.Vec3;
|
import net.minecraft.world.phys.Vec3;
|
||||||
import net.minecraftforge.common.ForgeHooks;
|
import net.minecraftforge.common.ForgeHooks;
|
||||||
import net.minecraftforge.fluids.FluidStack;
|
import net.minecraftforge.fluids.FluidStack;
|
||||||
|
@ -90,6 +88,7 @@ public class Train {
|
||||||
public boolean honk = false;
|
public boolean honk = false;
|
||||||
|
|
||||||
public UUID id;
|
public UUID id;
|
||||||
|
@Nullable
|
||||||
public UUID owner;
|
public UUID owner;
|
||||||
public TrackGraph graph;
|
public TrackGraph graph;
|
||||||
public Navigation navigation;
|
public Navigation navigation;
|
||||||
|
@ -1108,6 +1107,7 @@ public class Train {
|
||||||
public CompoundTag write(DimensionPalette dimensions) {
|
public CompoundTag write(DimensionPalette dimensions) {
|
||||||
CompoundTag tag = new CompoundTag();
|
CompoundTag tag = new CompoundTag();
|
||||||
tag.putUUID("Id", id);
|
tag.putUUID("Id", id);
|
||||||
|
if (owner != null)
|
||||||
tag.putUUID("Owner", owner);
|
tag.putUUID("Owner", owner);
|
||||||
if (graph != null)
|
if (graph != null)
|
||||||
tag.putUUID("Graph", graph.id);
|
tag.putUUID("Graph", graph.id);
|
||||||
|
@ -1154,7 +1154,7 @@ public class Train {
|
||||||
|
|
||||||
public static Train read(CompoundTag tag, Map<UUID, TrackGraph> trackNetworks, DimensionPalette dimensions) {
|
public static Train read(CompoundTag tag, Map<UUID, TrackGraph> trackNetworks, DimensionPalette dimensions) {
|
||||||
UUID id = tag.getUUID("Id");
|
UUID id = tag.getUUID("Id");
|
||||||
UUID owner = tag.getUUID("Owner");
|
UUID owner = tag.contains("Owner") ? tag.getUUID("Owner") : null;
|
||||||
UUID graphId = tag.contains("Graph") ? tag.getUUID("Graph") : null;
|
UUID graphId = tag.contains("Graph") ? tag.getUUID("Graph") : null;
|
||||||
TrackGraph graph = graphId == null ? null : trackNetworks.get(graphId);
|
TrackGraph graph = graphId == null ? null : trackNetworks.get(graphId);
|
||||||
List<Carriage> carriages = new ArrayList<>();
|
List<Carriage> carriages = new ArrayList<>();
|
||||||
|
|
|
@ -37,7 +37,10 @@ public class TrainPacket extends SimplePacketBase {
|
||||||
if (!add)
|
if (!add)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
UUID owner = buffer.readUUID();
|
UUID owner = null;
|
||||||
|
if (buffer.readBoolean())
|
||||||
|
owner = buffer.readUUID();
|
||||||
|
|
||||||
List<Carriage> carriages = new ArrayList<>();
|
List<Carriage> carriages = new ArrayList<>();
|
||||||
List<Integer> carriageSpacing = new ArrayList<>();
|
List<Integer> carriageSpacing = new ArrayList<>();
|
||||||
|
|
||||||
|
@ -75,6 +78,8 @@ public class TrainPacket extends SimplePacketBase {
|
||||||
if (!add)
|
if (!add)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
buffer.writeBoolean(train.owner != null);
|
||||||
|
if (train.owner != null)
|
||||||
buffer.writeUUID(train.owner);
|
buffer.writeUUID(train.owner);
|
||||||
|
|
||||||
buffer.writeVarInt(train.carriages.size());
|
buffer.writeVarInt(train.carriages.size());
|
||||||
|
|
|
@ -170,6 +170,10 @@ public class FlapDisplaySection {
|
||||||
return !singleFlap;
|
return !singleFlap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Component getText() {
|
||||||
|
return component;
|
||||||
|
}
|
||||||
|
|
||||||
public static String[] getFlapCycle(String key) {
|
public static String[] getFlapCycle(String key) {
|
||||||
return LOADED_FLAP_CYCLES.computeIfAbsent(key, k -> Lang.translateDirect("flap_display.cycles." + key)
|
return LOADED_FLAP_CYCLES.computeIfAbsent(key, k -> Lang.translateDirect("flap_display.cycles." + key)
|
||||||
.getString()
|
.getString()
|
||||||
|
|
|
@ -30,10 +30,11 @@ public class GlobalTrainDisplayData {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<TrainDeparturePrediction> prepare(String filter, int maxLines) {
|
public static List<TrainDeparturePrediction> prepare(String filter, int maxLines) {
|
||||||
|
String regex = filter.isBlank() ? filter : "\\Q" + filter.replace("*", "\\E.*\\Q") + "\\E";
|
||||||
return statusByDestination.entrySet()
|
return statusByDestination.entrySet()
|
||||||
.stream()
|
.stream()
|
||||||
.filter(e -> e.getKey()
|
.filter(e -> e.getKey()
|
||||||
.matches(filter.replace("*", ".*")))
|
.matches(regex))
|
||||||
.flatMap(e -> e.getValue()
|
.flatMap(e -> e.getValue()
|
||||||
.stream())
|
.stream())
|
||||||
.sorted()
|
.sorted()
|
||||||
|
|
|
@ -7,6 +7,7 @@ import com.jozufozu.flywheel.core.PartialModel;
|
||||||
import com.jozufozu.flywheel.util.transform.TransformStack;
|
import com.jozufozu.flywheel.util.transform.TransformStack;
|
||||||
import com.mojang.blaze3d.vertex.PoseStack;
|
import com.mojang.blaze3d.vertex.PoseStack;
|
||||||
import com.simibubi.create.CreateClient;
|
import com.simibubi.create.CreateClient;
|
||||||
|
import com.simibubi.create.compat.computercraft.ComputerScreen;
|
||||||
import com.simibubi.create.content.logistics.trains.entity.Carriage;
|
import com.simibubi.create.content.logistics.trains.entity.Carriage;
|
||||||
import com.simibubi.create.content.logistics.trains.entity.Train;
|
import com.simibubi.create.content.logistics.trains.entity.Train;
|
||||||
import com.simibubi.create.content.logistics.trains.entity.TrainIconType;
|
import com.simibubi.create.content.logistics.trains.entity.TrainIconType;
|
||||||
|
@ -15,6 +16,7 @@ import com.simibubi.create.foundation.gui.AllGuiTextures;
|
||||||
import com.simibubi.create.foundation.gui.AllIcons;
|
import com.simibubi.create.foundation.gui.AllIcons;
|
||||||
import com.simibubi.create.foundation.gui.element.GuiGameElement;
|
import com.simibubi.create.foundation.gui.element.GuiGameElement;
|
||||||
import com.simibubi.create.foundation.gui.widget.IconButton;
|
import com.simibubi.create.foundation.gui.widget.IconButton;
|
||||||
|
import com.simibubi.create.foundation.utility.Components;
|
||||||
|
|
||||||
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||||
|
|
||||||
|
@ -39,6 +41,10 @@ public abstract class AbstractStationScreen extends AbstractSimiScreen {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void init() {
|
protected void init() {
|
||||||
|
if (te.computerBehaviour.hasAttachedComputer())
|
||||||
|
minecraft.setScreen(new ComputerScreen(title, () -> Components.literal(station.name),
|
||||||
|
this::renderAdditional, this, te.computerBehaviour::hasAttachedComputer));
|
||||||
|
|
||||||
setWindowSize(background.width, background.height);
|
setWindowSize(background.width, background.height);
|
||||||
super.init();
|
super.init();
|
||||||
clearWidgets();
|
clearWidgets();
|
||||||
|
@ -71,17 +77,29 @@ public abstract class AbstractStationScreen extends AbstractSimiScreen {
|
||||||
return w;
|
return w;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void tick() {
|
||||||
|
super.tick();
|
||||||
|
|
||||||
|
if (te.computerBehaviour.hasAttachedComputer())
|
||||||
|
minecraft.setScreen(new ComputerScreen(title, () -> Components.literal(station.name),
|
||||||
|
this::renderAdditional, this, te.computerBehaviour::hasAttachedComputer));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void renderWindow(PoseStack ms, int mouseX, int mouseY, float partialTicks) {
|
protected void renderWindow(PoseStack ms, int mouseX, int mouseY, float partialTicks) {
|
||||||
int x = guiLeft;
|
int x = guiLeft;
|
||||||
int y = guiTop;
|
int y = guiTop;
|
||||||
|
|
||||||
background.render(ms, x, y, this);
|
background.render(ms, x, y, this);
|
||||||
|
renderAdditional(ms, mouseX, mouseY, partialTicks, x, y, background);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void renderAdditional(PoseStack ms, int mouseX, int mouseY, float partialTicks, int guiLeft, int guiTop, AllGuiTextures background) {
|
||||||
ms.pushPose();
|
ms.pushPose();
|
||||||
TransformStack msr = TransformStack.cast(ms);
|
TransformStack msr = TransformStack.cast(ms);
|
||||||
msr.pushPose()
|
msr.pushPose()
|
||||||
.translate(x + background.width + 4, y + background.height + 4, 100)
|
.translate(guiLeft + background.width + 4, guiTop + background.height + 4, 100)
|
||||||
.scale(40)
|
.scale(40)
|
||||||
.rotateX(-22)
|
.rotateX(-22)
|
||||||
.rotateY(63);
|
.rotateY(63);
|
||||||
|
|
|
@ -9,13 +9,18 @@ import java.util.Map;
|
||||||
import java.util.Map.Entry;
|
import java.util.Map.Entry;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
import com.simibubi.create.AllBlocks;
|
import com.simibubi.create.AllBlocks;
|
||||||
import com.simibubi.create.AllItems;
|
import com.simibubi.create.AllItems;
|
||||||
import com.simibubi.create.AllSoundEvents;
|
import com.simibubi.create.AllSoundEvents;
|
||||||
import com.simibubi.create.Create;
|
import com.simibubi.create.Create;
|
||||||
|
import com.simibubi.create.compat.computercraft.AbstractComputerBehaviour;
|
||||||
|
import com.simibubi.create.compat.computercraft.ComputerCraftProxy;
|
||||||
import com.simibubi.create.content.contraptions.components.actors.DoorControlBehaviour;
|
import com.simibubi.create.content.contraptions.components.actors.DoorControlBehaviour;
|
||||||
import com.simibubi.create.content.contraptions.components.structureMovement.AssemblyException;
|
import com.simibubi.create.content.contraptions.components.structureMovement.AssemblyException;
|
||||||
import com.simibubi.create.content.contraptions.components.structureMovement.ITransformableBlockEntity;
|
import com.simibubi.create.content.contraptions.components.structureMovement.ITransformableBlockEntity;
|
||||||
|
@ -23,6 +28,7 @@ import com.simibubi.create.content.contraptions.components.structureMovement.Str
|
||||||
import com.simibubi.create.content.logistics.block.depot.DepotBehaviour;
|
import com.simibubi.create.content.logistics.block.depot.DepotBehaviour;
|
||||||
import com.simibubi.create.content.logistics.block.display.DisplayLinkBlock;
|
import com.simibubi.create.content.logistics.block.display.DisplayLinkBlock;
|
||||||
import com.simibubi.create.content.logistics.trains.AbstractBogeyBlock;
|
import com.simibubi.create.content.logistics.trains.AbstractBogeyBlock;
|
||||||
|
import com.simibubi.create.content.logistics.trains.GraphLocation;
|
||||||
import com.simibubi.create.content.logistics.trains.ITrackBlock;
|
import com.simibubi.create.content.logistics.trains.ITrackBlock;
|
||||||
import com.simibubi.create.content.logistics.trains.TrackEdge;
|
import com.simibubi.create.content.logistics.trains.TrackEdge;
|
||||||
import com.simibubi.create.content.logistics.trains.TrackGraph;
|
import com.simibubi.create.content.logistics.trains.TrackGraph;
|
||||||
|
@ -49,6 +55,7 @@ import com.simibubi.create.foundation.networking.AllPackets;
|
||||||
import com.simibubi.create.foundation.utility.Iterate;
|
import com.simibubi.create.foundation.utility.Iterate;
|
||||||
import com.simibubi.create.foundation.utility.Lang;
|
import com.simibubi.create.foundation.utility.Lang;
|
||||||
import com.simibubi.create.foundation.utility.NBTHelper;
|
import com.simibubi.create.foundation.utility.NBTHelper;
|
||||||
|
import com.simibubi.create.foundation.utility.VecHelper;
|
||||||
import com.simibubi.create.foundation.utility.WorldAttached;
|
import com.simibubi.create.foundation.utility.WorldAttached;
|
||||||
import com.simibubi.create.foundation.utility.animation.LerpedFloat;
|
import com.simibubi.create.foundation.utility.animation.LerpedFloat;
|
||||||
import com.simibubi.create.foundation.utility.animation.LerpedFloat.Chaser;
|
import com.simibubi.create.foundation.utility.animation.LerpedFloat.Chaser;
|
||||||
|
@ -63,9 +70,11 @@ import net.minecraft.core.particles.ParticleTypes;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.network.chat.Component;
|
import net.minecraft.network.chat.Component;
|
||||||
import net.minecraft.server.level.ServerLevel;
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
import net.minecraft.sounds.SoundSource;
|
import net.minecraft.sounds.SoundSource;
|
||||||
import net.minecraft.util.Mth;
|
import net.minecraft.util.Mth;
|
||||||
import net.minecraft.world.InteractionHand;
|
import net.minecraft.world.InteractionHand;
|
||||||
|
import net.minecraft.world.entity.item.ItemEntity;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
import net.minecraft.world.item.ItemStack;
|
import net.minecraft.world.item.ItemStack;
|
||||||
import net.minecraft.world.level.block.SoundType;
|
import net.minecraft.world.level.block.SoundType;
|
||||||
|
@ -90,6 +99,7 @@ public class StationBlockEntity extends SmartBlockEntity implements ITransformab
|
||||||
protected int failedCarriageIndex;
|
protected int failedCarriageIndex;
|
||||||
protected AssemblyException lastException;
|
protected AssemblyException lastException;
|
||||||
protected DepotBehaviour depotBehaviour;
|
protected DepotBehaviour depotBehaviour;
|
||||||
|
public AbstractComputerBehaviour computerBehaviour;
|
||||||
|
|
||||||
// for display
|
// for display
|
||||||
UUID imminentTrain;
|
UUID imminentTrain;
|
||||||
|
@ -122,6 +132,7 @@ public class StationBlockEntity extends SmartBlockEntity implements ITransformab
|
||||||
depotBehaviour.addSubBehaviours(behaviours);
|
depotBehaviour.addSubBehaviours(behaviours);
|
||||||
registerAwardables(behaviours, AllAdvancements.CONTRAPTION_ACTORS, AllAdvancements.TRAIN,
|
registerAwardables(behaviours, AllAdvancements.CONTRAPTION_ACTORS, AllAdvancements.TRAIN,
|
||||||
AllAdvancements.LONG_TRAIN, AllAdvancements.CONDUCTOR);
|
AllAdvancements.LONG_TRAIN, AllAdvancements.CONDUCTOR);
|
||||||
|
behaviours.add(computerBehaviour = ComputerCraftProxy.behaviour(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -343,6 +354,63 @@ public class StationBlockEntity extends SmartBlockEntity implements ITransformab
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean enterAssemblyMode(@Nullable ServerPlayer sender) {
|
||||||
|
if (isAssembling())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
tryDisassembleTrain(sender);
|
||||||
|
if (!tryEnterAssemblyMode())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
BlockState newState = getBlockState().setValue(StationBlock.ASSEMBLING, true);
|
||||||
|
level.setBlock(getBlockPos(), newState, 3);
|
||||||
|
refreshBlockState();
|
||||||
|
refreshAssemblyInfo();
|
||||||
|
|
||||||
|
updateStationState(station -> station.assembling = true);
|
||||||
|
GlobalStation station = getStation();
|
||||||
|
if (station != null) {
|
||||||
|
for (Train train : Create.RAILWAYS.sided(level).trains.values()) {
|
||||||
|
if (train.navigation.destination != station)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
GlobalStation preferredDestination = train.runtime.startCurrentInstruction();
|
||||||
|
train.navigation.startNavigation(preferredDestination != null ? preferredDestination : station, Double.MAX_VALUE, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean exitAssemblyMode() {
|
||||||
|
if (!isAssembling())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
cancelAssembly();
|
||||||
|
BlockState newState = getBlockState().setValue(StationBlock.ASSEMBLING, false);
|
||||||
|
level.setBlock(getBlockPos(), newState, 3);
|
||||||
|
refreshBlockState();
|
||||||
|
|
||||||
|
return updateStationState(station -> station.assembling = false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean tryDisassembleTrain(@Nullable ServerPlayer sender) {
|
||||||
|
GlobalStation station = getStation();
|
||||||
|
if (station == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
Train train = station.getPresentTrain();
|
||||||
|
if (train == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
BlockPos trackPosition = edgePoint.getGlobalPosition();
|
||||||
|
if (!train.disassemble(getAssemblyDirection(), trackPosition.above()))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
dropSchedule(sender);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isAssembling() {
|
public boolean isAssembling() {
|
||||||
BlockState state = getBlockState();
|
BlockState state = getBlockState();
|
||||||
return state.hasProperty(StationBlock.ASSEMBLING) && state.getValue(StationBlock.ASSEMBLING);
|
return state.hasProperty(StationBlock.ASSEMBLING) && state.getValue(StationBlock.ASSEMBLING);
|
||||||
|
@ -370,6 +438,42 @@ public class StationBlockEntity extends SmartBlockEntity implements ITransformab
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void dropSchedule(@Nullable ServerPlayer sender) {
|
||||||
|
GlobalStation station = getStation();
|
||||||
|
if (station == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Train train = station.getPresentTrain();
|
||||||
|
if (train == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ItemStack schedule = train.runtime.returnSchedule();
|
||||||
|
if (schedule.isEmpty())
|
||||||
|
return;
|
||||||
|
if (sender != null && sender.getMainHandItem().isEmpty()) {
|
||||||
|
sender.getInventory()
|
||||||
|
.placeItemBackInInventory(schedule);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Vec3 v = VecHelper.getCenterOf(getBlockPos());
|
||||||
|
ItemEntity itemEntity = new ItemEntity(getLevel(), v.x, v.y, v.z, schedule);
|
||||||
|
itemEntity.setDeltaMovement(Vec3.ZERO);
|
||||||
|
getLevel().addFreshEntity(itemEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean updateStationState(Consumer<GlobalStation> updateState) {
|
||||||
|
GlobalStation station = getStation();
|
||||||
|
GraphLocation graphLocation = edgePoint.determineGraphLocation();
|
||||||
|
if (station == null || graphLocation == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
updateState.accept(station);
|
||||||
|
Create.RAILWAYS.sync.pointAdded(graphLocation.graph, station);
|
||||||
|
Create.RAILWAYS.markTracksDirty();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public void refreshAssemblyInfo() {
|
public void refreshAssemblyInfo() {
|
||||||
if (!edgePoint.hasValidTrack())
|
if (!edgePoint.hasValidTrack())
|
||||||
return;
|
return;
|
||||||
|
@ -450,6 +554,14 @@ public class StationBlockEntity extends SmartBlockEntity implements ITransformab
|
||||||
map.put(worldPosition, BoundingBox.fromCorners(startPosition, trackEnd));
|
map.put(worldPosition, BoundingBox.fromCorners(startPosition, trackEnd));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean updateName(String name) {
|
||||||
|
if (!updateStationState(station -> station.name = name))
|
||||||
|
return false;
|
||||||
|
notifyUpdate();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isValidBogeyOffset(int i) {
|
public boolean isValidBogeyOffset(int i) {
|
||||||
if ((i < 3 || bogeyCount == 0) && i != 0)
|
if ((i < 3 || bogeyCount == 0) && i != 0)
|
||||||
return false;
|
return false;
|
||||||
|
@ -744,12 +856,20 @@ public class StationBlockEntity extends SmartBlockEntity implements ITransformab
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <T> LazyOptional<T> getCapability(Capability<T> cap, Direction side) {
|
public <T> @NotNull LazyOptional<T> getCapability(@NotNull Capability<T> cap, Direction side) {
|
||||||
if (isItemHandlerCap(cap))
|
if (isItemHandlerCap(cap))
|
||||||
return depotBehaviour.getItemCapability(cap, side);
|
return depotBehaviour.getItemCapability(cap, side);
|
||||||
|
if (computerBehaviour.isPeripheralCap(cap))
|
||||||
|
return computerBehaviour.getPeripheralCapability();
|
||||||
return super.getCapability(cap, side);
|
return super.getCapability(cap, side);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void invalidateCaps() {
|
||||||
|
super.invalidateCaps();
|
||||||
|
computerBehaviour.removePeripheral();
|
||||||
|
}
|
||||||
|
|
||||||
private void applyAutoSchedule() {
|
private void applyAutoSchedule() {
|
||||||
ItemStack stack = getAutoSchedule();
|
ItemStack stack = getAutoSchedule();
|
||||||
if (!AllItems.SCHEDULE.isIn(stack))
|
if (!AllItems.SCHEDULE.isIn(stack))
|
||||||
|
|
|
@ -1,21 +1,14 @@
|
||||||
package com.simibubi.create.content.logistics.trains.management.edgePoint.station;
|
package com.simibubi.create.content.logistics.trains.management.edgePoint.station;
|
||||||
|
|
||||||
import com.simibubi.create.Create;
|
|
||||||
import com.simibubi.create.content.contraptions.components.actors.DoorControl;
|
import com.simibubi.create.content.contraptions.components.actors.DoorControl;
|
||||||
import com.simibubi.create.content.logistics.trains.GraphLocation;
|
|
||||||
import com.simibubi.create.content.logistics.trains.entity.Train;
|
|
||||||
import com.simibubi.create.foundation.networking.BlockEntityConfigurationPacket;
|
import com.simibubi.create.foundation.networking.BlockEntityConfigurationPacket;
|
||||||
import com.simibubi.create.foundation.utility.VecHelper;
|
|
||||||
|
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.network.FriendlyByteBuf;
|
import net.minecraft.network.FriendlyByteBuf;
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
import net.minecraft.util.Mth;
|
import net.minecraft.util.Mth;
|
||||||
import net.minecraft.world.entity.item.ItemEntity;
|
|
||||||
import net.minecraft.world.item.ItemStack;
|
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.phys.Vec3;
|
|
||||||
|
|
||||||
public class StationEditPacket extends BlockEntityConfigurationPacket<StationBlockEntity> {
|
public class StationEditPacket extends BlockEntityConfigurationPacket<StationBlockEntity> {
|
||||||
|
|
||||||
|
@ -101,22 +94,15 @@ public class StationEditPacket extends BlockEntityConfigurationPacket<StationBlo
|
||||||
BlockState blockState = level.getBlockState(blockPos);
|
BlockState blockState = level.getBlockState(blockPos);
|
||||||
|
|
||||||
if (dropSchedule) {
|
if (dropSchedule) {
|
||||||
scheduleDropRequested(player, be);
|
be.dropSchedule(player);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (doorControl != null)
|
if (doorControl != null)
|
||||||
be.doorControls.set(doorControl);
|
be.doorControls.set(doorControl);
|
||||||
|
|
||||||
if (!name.isBlank()) {
|
if (!name.isBlank())
|
||||||
GlobalStation station = be.getStation();
|
be.updateName(name);
|
||||||
GraphLocation graphLocation = be.edgePoint.determineGraphLocation();
|
|
||||||
if (station != null && graphLocation != null) {
|
|
||||||
station.name = name;
|
|
||||||
Create.RAILWAYS.sync.pointAdded(graphLocation.graph, station);
|
|
||||||
Create.RAILWAYS.markTracksDirty();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!(blockState.getBlock() instanceof StationBlock))
|
if (!(blockState.getBlock() instanceof StationBlock))
|
||||||
return;
|
return;
|
||||||
|
@ -132,89 +118,17 @@ public class StationEditPacket extends BlockEntityConfigurationPacket<StationBlo
|
||||||
assemblyComplete = be.getStation() != null && be.getStation()
|
assemblyComplete = be.getStation() != null && be.getStation()
|
||||||
.getPresentTrain() != null;
|
.getPresentTrain() != null;
|
||||||
} else {
|
} else {
|
||||||
if (disassembleAndEnterMode(player, be))
|
if (be.tryDisassembleTrain(player) && be.tryEnterAssemblyMode())
|
||||||
be.refreshAssemblyInfo();
|
be.refreshAssemblyInfo();
|
||||||
}
|
}
|
||||||
if (!assemblyComplete)
|
if (!assemblyComplete)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (isAssemblyMode == assemblyMode)
|
|
||||||
return;
|
|
||||||
|
|
||||||
BlockState newState = blockState.cycle(StationBlock.ASSEMBLING);
|
if (assemblyMode)
|
||||||
Boolean nowAssembling = newState.getValue(StationBlock.ASSEMBLING);
|
be.enterAssemblyMode(player);
|
||||||
|
|
||||||
if (nowAssembling) {
|
|
||||||
if (!disassembleAndEnterMode(player, be))
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
be.cancelAssembly();
|
|
||||||
}
|
|
||||||
|
|
||||||
level.setBlock(blockPos, newState, 3);
|
|
||||||
be.refreshBlockState();
|
|
||||||
|
|
||||||
if (nowAssembling)
|
|
||||||
be.refreshAssemblyInfo();
|
|
||||||
|
|
||||||
GlobalStation station = be.getStation();
|
|
||||||
GraphLocation graphLocation = be.edgePoint.determineGraphLocation();
|
|
||||||
if (station != null && graphLocation != null) {
|
|
||||||
station.assembling = nowAssembling;
|
|
||||||
Create.RAILWAYS.sync.pointAdded(graphLocation.graph, station);
|
|
||||||
Create.RAILWAYS.markTracksDirty();
|
|
||||||
|
|
||||||
if (nowAssembling)
|
|
||||||
for (Train train : Create.RAILWAYS.sided(level).trains.values()) {
|
|
||||||
if (train.navigation.destination != station)
|
|
||||||
continue;
|
|
||||||
GlobalStation preferredDestination = train.runtime.startCurrentInstruction();
|
|
||||||
if (preferredDestination != null)
|
|
||||||
train.navigation.startNavigation(preferredDestination, Double.MAX_VALUE, false);
|
|
||||||
else
|
else
|
||||||
train.navigation.startNavigation(station, Double.MAX_VALUE, false);
|
be.exitAssemblyMode();
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void scheduleDropRequested(ServerPlayer sender, StationBlockEntity be) {
|
|
||||||
GlobalStation station = be.getStation();
|
|
||||||
if (station == null)
|
|
||||||
return;
|
|
||||||
Train train = station.getPresentTrain();
|
|
||||||
if (train == null)
|
|
||||||
return;
|
|
||||||
ItemStack schedule = train.runtime.returnSchedule();
|
|
||||||
dropSchedule(sender, be, schedule);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean disassembleAndEnterMode(ServerPlayer sender, StationBlockEntity be) {
|
|
||||||
GlobalStation station = be.getStation();
|
|
||||||
if (station != null) {
|
|
||||||
Train train = station.getPresentTrain();
|
|
||||||
BlockPos trackPosition = be.edgePoint.getGlobalPosition();
|
|
||||||
ItemStack schedule = train == null ? ItemStack.EMPTY : train.runtime.returnSchedule();
|
|
||||||
if (train != null && !train.disassemble(be.getAssemblyDirection(), trackPosition.above()))
|
|
||||||
return false;
|
|
||||||
dropSchedule(sender, be, schedule);
|
|
||||||
}
|
|
||||||
return be.tryEnterAssemblyMode();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void dropSchedule(ServerPlayer sender, StationBlockEntity be, ItemStack schedule) {
|
|
||||||
if (schedule.isEmpty())
|
|
||||||
return;
|
|
||||||
if (sender.getMainHandItem()
|
|
||||||
.isEmpty()) {
|
|
||||||
sender.getInventory()
|
|
||||||
.placeItemBackInInventory(schedule);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
Vec3 v = VecHelper.getCenterOf(be.getBlockPos());
|
|
||||||
ItemEntity itemEntity = new ItemEntity(be.getLevel(), v.x, v.y, v.z, schedule);
|
|
||||||
itemEntity.setDeltaMovement(Vec3.ZERO);
|
|
||||||
be.getLevel()
|
|
||||||
.addFreshEntity(itemEntity);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -359,6 +359,8 @@ public class StationScreen extends AbstractStationScreen {
|
||||||
@Override
|
@Override
|
||||||
public void removed() {
|
public void removed() {
|
||||||
super.removed();
|
super.removed();
|
||||||
|
if (nameBox == null || trainNameBox == null)
|
||||||
|
return;
|
||||||
AllPackets.getChannel()
|
AllPackets.getChannel()
|
||||||
.sendToServer(StationEditPacket.configure(blockEntity.getBlockPos(), switchingToAssemblyMode,
|
.sendToServer(StationEditPacket.configure(blockEntity.getBlockPos(), switchingToAssemblyMode,
|
||||||
nameBox.getValue(), doorControl));
|
nameBox.getValue(), doorControl));
|
||||||
|
|
|
@ -25,6 +25,8 @@ public interface IScheduleInput {
|
||||||
|
|
||||||
public abstract CompoundTag getData();
|
public abstract CompoundTag getData();
|
||||||
|
|
||||||
|
public abstract void setData(CompoundTag data);
|
||||||
|
|
||||||
public default int slotsTargeted() {
|
public default int slotsTargeted() {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,12 @@ public abstract class ScheduleDataEntry implements IScheduleInput {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setData(CompoundTag data) {
|
||||||
|
this.data = data;
|
||||||
|
readAdditional(data);
|
||||||
|
}
|
||||||
|
|
||||||
protected void writeAdditional(CompoundTag tag) {};
|
protected void writeAdditional(CompoundTag tag) {};
|
||||||
|
|
||||||
protected void readAdditional(CompoundTag tag) {};
|
protected void readAdditional(CompoundTag tag) {};
|
||||||
|
|
|
@ -68,6 +68,7 @@ public class FluidThresholdCondition extends CargoThresholdCondition {
|
||||||
@Override
|
@Override
|
||||||
protected void readAdditional(CompoundTag tag) {
|
protected void readAdditional(CompoundTag tag) {
|
||||||
super.readAdditional(tag);
|
super.readAdditional(tag);
|
||||||
|
if (tag.contains("Bucket"))
|
||||||
compareStack = ItemStack.of(tag.getCompound("Bucket"));
|
compareStack = ItemStack.of(tag.getCompound("Bucket"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -69,6 +69,7 @@ public class ItemThresholdCondition extends CargoThresholdCondition {
|
||||||
@Override
|
@Override
|
||||||
protected void readAdditional(CompoundTag tag) {
|
protected void readAdditional(CompoundTag tag) {
|
||||||
super.readAdditional(tag);
|
super.readAdditional(tag);
|
||||||
|
if (tag.contains("Item"))
|
||||||
stack = ItemStack.of(tag.getCompound("Item"));
|
stack = ItemStack.of(tag.getCompound("Item"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -107,6 +107,7 @@ public class RedstoneLinkCondition extends ScheduleWaitCondition {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void readAdditional(CompoundTag tag) {
|
protected void readAdditional(CompoundTag tag) {
|
||||||
|
if (tag.contains("Frequency"))
|
||||||
freq = Couple.deserializeEach(tag.getList("Frequency", Tag.TAG_COMPOUND), c -> Frequency.of(ItemStack.of(c)));
|
freq = Couple.deserializeEach(tag.getList("Frequency", Tag.TAG_COMPOUND), c -> Frequency.of(ItemStack.of(c)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,9 +23,10 @@ public abstract class ScheduleWaitCondition extends ScheduleDataEntry {
|
||||||
|
|
||||||
public final CompoundTag write() {
|
public final CompoundTag write() {
|
||||||
CompoundTag tag = new CompoundTag();
|
CompoundTag tag = new CompoundTag();
|
||||||
|
CompoundTag dataCopy = data.copy();
|
||||||
|
writeAdditional(dataCopy);
|
||||||
tag.putString("Id", getId().toString());
|
tag.putString("Id", getId().toString());
|
||||||
tag.put("Data", data.copy());
|
tag.put("Data", dataCopy);
|
||||||
writeAdditional(tag);
|
|
||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,8 +44,11 @@ public abstract class ScheduleWaitCondition extends ScheduleDataEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
ScheduleWaitCondition condition = supplier.get();
|
ScheduleWaitCondition condition = supplier.get();
|
||||||
condition.data = tag.getCompound("Data");
|
// Left around for migration purposes. Data added in writeAdditional has moved into the "Data" tag
|
||||||
condition.readAdditional(tag);
|
condition.readAdditional(tag);
|
||||||
|
CompoundTag data = tag.getCompound("Data");
|
||||||
|
condition.readAdditional(data);
|
||||||
|
condition.data = data;
|
||||||
return condition;
|
return condition;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -16,9 +16,10 @@ public abstract class ScheduleInstruction extends ScheduleDataEntry {
|
||||||
|
|
||||||
public final CompoundTag write() {
|
public final CompoundTag write() {
|
||||||
CompoundTag tag = new CompoundTag();
|
CompoundTag tag = new CompoundTag();
|
||||||
|
CompoundTag dataCopy = data.copy();
|
||||||
|
writeAdditional(dataCopy);
|
||||||
tag.putString("Id", getId().toString());
|
tag.putString("Id", getId().toString());
|
||||||
tag.put("Data", data.copy());
|
tag.put("Data", dataCopy);
|
||||||
writeAdditional(tag);
|
|
||||||
return tag;
|
return tag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,8 +37,11 @@ public abstract class ScheduleInstruction extends ScheduleDataEntry {
|
||||||
}
|
}
|
||||||
|
|
||||||
ScheduleInstruction scheduleDestination = supplier.get();
|
ScheduleInstruction scheduleDestination = supplier.get();
|
||||||
scheduleDestination.data = tag.getCompound("Data");
|
// Left around for migration purposes. Data added in writeAdditional has moved into the "Data" tag
|
||||||
scheduleDestination.readAdditional(tag);
|
scheduleDestination.readAdditional(tag);
|
||||||
|
CompoundTag data = tag.getCompound("Data");
|
||||||
|
scheduleDestination.readAdditional(data);
|
||||||
|
scheduleDestination.data = data;
|
||||||
return scheduleDestination;
|
return scheduleDestination;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
package com.simibubi.create.content.schematics;
|
||||||
|
|
||||||
|
import com.simibubi.create.Create;
|
||||||
|
import com.simibubi.create.content.schematics.item.SchematicAndQuillItem;
|
||||||
|
import com.simibubi.create.foundation.utility.FilesHelper;
|
||||||
|
import com.simibubi.create.foundation.utility.Lang;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.nbt.NbtIo;
|
||||||
|
import net.minecraft.world.level.Level;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.BoundingBox;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
|
||||||
|
import net.minecraft.world.phys.AABB;
|
||||||
|
import net.minecraftforge.fml.loading.FMLEnvironment;
|
||||||
|
import net.minecraftforge.fml.loading.FMLPaths;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.nio.file.StandardOpenOption;
|
||||||
|
|
||||||
|
public class SchematicExport {
|
||||||
|
public static final Path SCHEMATICS = FMLPaths.GAMEDIR.get().resolve("schematics");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save a schematic to a file from a world.
|
||||||
|
* @param dir the directory the schematic will be created in
|
||||||
|
* @param fileName the ideal name of the schematic, may not be the name of the created file
|
||||||
|
* @param overwrite whether overwriting an existing schematic is allowed
|
||||||
|
* @param level the level where the schematic structure is placed
|
||||||
|
* @param first the first corner of the schematic area
|
||||||
|
* @param second the second corner of the schematic area
|
||||||
|
* @return a SchematicExportResult, or null if an error occurred.
|
||||||
|
*/
|
||||||
|
@Nullable
|
||||||
|
public static SchematicExportResult saveSchematic(Path dir, String fileName, boolean overwrite, Level level, BlockPos first, BlockPos second) {
|
||||||
|
BoundingBox bb = BoundingBox.fromCorners(first, second);
|
||||||
|
BlockPos origin = new BlockPos(bb.minX(), bb.minY(), bb.minZ());
|
||||||
|
BlockPos bounds = new BlockPos(bb.getXSpan(), bb.getYSpan(), bb.getZSpan());
|
||||||
|
|
||||||
|
StructureTemplate structure = new StructureTemplate();
|
||||||
|
structure.fillFromWorld(level, origin, bounds, true, Blocks.AIR);
|
||||||
|
CompoundTag data = structure.save(new CompoundTag());
|
||||||
|
SchematicAndQuillItem.replaceStructureVoidWithAir(data);
|
||||||
|
SchematicAndQuillItem.clampGlueBoxes(level, new AABB(origin, origin.offset(bounds)), data);
|
||||||
|
|
||||||
|
if (fileName.isEmpty())
|
||||||
|
fileName = Lang.translateDirect("schematicAndQuill.fallbackName").getString();
|
||||||
|
if (!overwrite)
|
||||||
|
fileName = FilesHelper.findFirstValidFilename(fileName, dir, "nbt");
|
||||||
|
if (!fileName.endsWith(".nbt"))
|
||||||
|
fileName += ".nbt";
|
||||||
|
Path file = dir.resolve(fileName).toAbsolutePath();
|
||||||
|
|
||||||
|
try {
|
||||||
|
Files.createDirectories(dir);
|
||||||
|
boolean overwritten = Files.deleteIfExists(file);
|
||||||
|
try (OutputStream out = Files.newOutputStream(file, StandardOpenOption.CREATE)) {
|
||||||
|
NbtIo.writeCompressed(data, out);
|
||||||
|
}
|
||||||
|
return new SchematicExportResult(file, dir, fileName, overwritten, origin, bounds);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Create.LOGGER.error("An error occurred while saving schematic [" + fileName + "]", e);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public record SchematicExportResult(Path file, Path dir, String fileName, boolean overwritten, BlockPos origin, BlockPos bounds) {
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ import java.nio.file.Paths;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
@ -16,8 +17,8 @@ import java.util.stream.Stream;
|
||||||
import com.simibubi.create.AllBlocks;
|
import com.simibubi.create.AllBlocks;
|
||||||
import com.simibubi.create.AllItems;
|
import com.simibubi.create.AllItems;
|
||||||
import com.simibubi.create.Create;
|
import com.simibubi.create.Create;
|
||||||
|
import com.simibubi.create.content.schematics.SchematicExport.SchematicExportResult;
|
||||||
import com.simibubi.create.content.schematics.block.SchematicTableBlockEntity;
|
import com.simibubi.create.content.schematics.block.SchematicTableBlockEntity;
|
||||||
import com.simibubi.create.content.schematics.item.SchematicAndQuillItem;
|
|
||||||
import com.simibubi.create.content.schematics.item.SchematicItem;
|
import com.simibubi.create.content.schematics.item.SchematicItem;
|
||||||
import com.simibubi.create.foundation.config.AllConfigs;
|
import com.simibubi.create.foundation.config.AllConfigs;
|
||||||
import com.simibubi.create.foundation.config.CSchematics;
|
import com.simibubi.create.foundation.config.CSchematics;
|
||||||
|
@ -25,18 +26,14 @@ import com.simibubi.create.foundation.utility.Components;
|
||||||
import com.simibubi.create.foundation.utility.FilesHelper;
|
import com.simibubi.create.foundation.utility.FilesHelper;
|
||||||
import com.simibubi.create.foundation.utility.Lang;
|
import com.simibubi.create.foundation.utility.Lang;
|
||||||
|
|
||||||
|
import net.minecraft.ChatFormatting;
|
||||||
import net.minecraft.Util;
|
import net.minecraft.Util;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
|
||||||
import net.minecraft.nbt.NbtIo;
|
|
||||||
import net.minecraft.server.level.ServerPlayer;
|
import net.minecraft.server.level.ServerPlayer;
|
||||||
import net.minecraft.world.InteractionHand;
|
import net.minecraft.world.InteractionHand;
|
||||||
import net.minecraft.world.level.Level;
|
import net.minecraft.world.level.Level;
|
||||||
import net.minecraft.world.level.block.Blocks;
|
|
||||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
import net.minecraft.world.level.block.state.BlockState;
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
|
|
||||||
import net.minecraft.world.phys.AABB;
|
|
||||||
|
|
||||||
public class ServerSchematicLoader {
|
public class ServerSchematicLoader {
|
||||||
|
|
||||||
|
@ -284,10 +281,9 @@ public class ServerSchematicLoader {
|
||||||
|
|
||||||
public void handleInstantSchematic(ServerPlayer player, String schematic, Level world, BlockPos pos,
|
public void handleInstantSchematic(ServerPlayer player, String schematic, Level world, BlockPos pos,
|
||||||
BlockPos bounds) {
|
BlockPos bounds) {
|
||||||
String playerPath = getSchematicPath() + "/" + player.getGameProfile()
|
String playerName = player.getGameProfile().getName();
|
||||||
.getName();
|
String playerPath = getSchematicPath() + "/" + playerName;
|
||||||
String playerSchematicId = player.getGameProfile()
|
String playerSchematicId = playerName + "/" + schematic;
|
||||||
.getName() + "/" + schematic;
|
|
||||||
FilesHelper.createFolderIfMissing(playerPath);
|
FilesHelper.createFolderIfMissing(playerPath);
|
||||||
|
|
||||||
// Unsupported Format
|
// Unsupported Format
|
||||||
|
@ -310,43 +306,43 @@ public class ServerSchematicLoader {
|
||||||
if (!AllItems.SCHEMATIC_AND_QUILL.isIn(player.getMainHandItem()))
|
if (!AllItems.SCHEMATIC_AND_QUILL.isIn(player.getMainHandItem()))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// if there's too many schematics, delete oldest
|
||||||
|
Path playerSchematics = Paths.get(playerPath);
|
||||||
|
|
||||||
|
if (!tryDeleteOldestSchematic(playerSchematics))
|
||||||
|
return;
|
||||||
|
|
||||||
|
SchematicExportResult result = SchematicExport.saveSchematic(
|
||||||
|
playerSchematics, schematic, true,
|
||||||
|
world, pos, pos.offset(bounds).offset(-1, -1, -1)
|
||||||
|
);
|
||||||
|
if (result != null)
|
||||||
|
player.setItemInHand(InteractionHand.MAIN_HAND, SchematicItem.create(schematic, playerName));
|
||||||
|
else Lang.translate("schematicAndQuill.instant_failed")
|
||||||
|
.style(ChatFormatting.RED)
|
||||||
|
.sendStatus(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean tryDeleteOldestSchematic(Path dir) {
|
||||||
|
try (Stream<Path> stream = Files.list(dir)) {
|
||||||
|
List<Path> files = stream.toList();
|
||||||
|
if (files.size() < getConfig().maxSchematics.get())
|
||||||
|
return true;
|
||||||
|
Optional<Path> oldest = files.stream().min(Comparator.comparingLong(this::getLastModifiedTime));
|
||||||
|
Files.delete(oldest.orElseThrow());
|
||||||
|
return true;
|
||||||
|
} catch (IOException | IllegalStateException e) {
|
||||||
|
Create.LOGGER.error("Error deleting oldest schematic", e);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private long getLastModifiedTime(Path file) {
|
||||||
try {
|
try {
|
||||||
// Delete schematic with same name
|
return Files.getLastModifiedTime(file).toMillis();
|
||||||
Files.deleteIfExists(path);
|
|
||||||
|
|
||||||
// Too many Schematics
|
|
||||||
long count;
|
|
||||||
try (Stream<Path> list = Files.list(Paths.get(playerPath))) {
|
|
||||||
count = list.count();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count >= getConfig().maxSchematics.get()) {
|
|
||||||
Stream<Path> list2 = Files.list(Paths.get(playerPath));
|
|
||||||
Optional<Path> lastFilePath = list2.filter(f -> !Files.isDirectory(f))
|
|
||||||
.min(Comparator.comparingLong(f -> f.toFile()
|
|
||||||
.lastModified()));
|
|
||||||
list2.close();
|
|
||||||
if (lastFilePath.isPresent())
|
|
||||||
Files.deleteIfExists(lastFilePath.get());
|
|
||||||
}
|
|
||||||
|
|
||||||
StructureTemplate t = new StructureTemplate();
|
|
||||||
t.fillFromWorld(world, pos, bounds, true, Blocks.AIR);
|
|
||||||
|
|
||||||
try (OutputStream outputStream = Files.newOutputStream(path)) {
|
|
||||||
CompoundTag nbttagcompound = t.save(new CompoundTag());
|
|
||||||
SchematicAndQuillItem.replaceStructureVoidWithAir(nbttagcompound);
|
|
||||||
SchematicAndQuillItem.clampGlueBoxes(world, new AABB(pos, pos.offset(bounds)), nbttagcompound);
|
|
||||||
NbtIo.writeCompressed(nbttagcompound, outputStream);
|
|
||||||
player.setItemInHand(InteractionHand.MAIN_HAND, SchematicItem.create(schematic, player.getGameProfile()
|
|
||||||
.getName()));
|
|
||||||
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
Create.LOGGER.error("Error getting modification time of file " + file.getFileName(), e);
|
||||||
}
|
throw new IllegalStateException(e);
|
||||||
} catch (IOException e) {
|
|
||||||
Create.LOGGER.error("Exception Thrown in direct Schematic Upload: " + playerSchematicId);
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,13 +1,8 @@
|
||||||
package com.simibubi.create.content.schematics.client;
|
package com.simibubi.create.content.schematics.client;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
|
||||||
import java.nio.file.StandardOpenOption;
|
|
||||||
|
|
||||||
import org.apache.commons.io.IOUtils;
|
|
||||||
|
|
||||||
import com.simibubi.create.AllItems;
|
import com.simibubi.create.AllItems;
|
||||||
import com.simibubi.create.AllKeys;
|
import com.simibubi.create.AllKeys;
|
||||||
|
@ -15,34 +10,29 @@ import com.simibubi.create.AllSpecialTextures;
|
||||||
import com.simibubi.create.Create;
|
import com.simibubi.create.Create;
|
||||||
import com.simibubi.create.CreateClient;
|
import com.simibubi.create.CreateClient;
|
||||||
import com.simibubi.create.content.schematics.ClientSchematicLoader;
|
import com.simibubi.create.content.schematics.ClientSchematicLoader;
|
||||||
import com.simibubi.create.content.schematics.item.SchematicAndQuillItem;
|
import com.simibubi.create.content.schematics.SchematicExport;
|
||||||
|
import com.simibubi.create.content.schematics.SchematicExport.SchematicExportResult;
|
||||||
import com.simibubi.create.content.schematics.packet.InstantSchematicPacket;
|
import com.simibubi.create.content.schematics.packet.InstantSchematicPacket;
|
||||||
import com.simibubi.create.foundation.gui.ScreenOpener;
|
import com.simibubi.create.foundation.gui.ScreenOpener;
|
||||||
import com.simibubi.create.foundation.networking.AllPackets;
|
import com.simibubi.create.foundation.networking.AllPackets;
|
||||||
import com.simibubi.create.foundation.utility.AnimationTickHolder;
|
import com.simibubi.create.foundation.utility.AnimationTickHolder;
|
||||||
import com.simibubi.create.foundation.utility.FilesHelper;
|
|
||||||
import com.simibubi.create.foundation.utility.Lang;
|
import com.simibubi.create.foundation.utility.Lang;
|
||||||
import com.simibubi.create.foundation.utility.RaycastHelper;
|
import com.simibubi.create.foundation.utility.RaycastHelper;
|
||||||
import com.simibubi.create.foundation.utility.RaycastHelper.PredicateTraceResult;
|
import com.simibubi.create.foundation.utility.RaycastHelper.PredicateTraceResult;
|
||||||
import com.simibubi.create.foundation.utility.VecHelper;
|
import com.simibubi.create.foundation.utility.VecHelper;
|
||||||
import com.simibubi.create.foundation.utility.outliner.Outliner;
|
import com.simibubi.create.foundation.utility.outliner.Outliner;
|
||||||
|
|
||||||
|
import net.minecraft.ChatFormatting;
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.player.LocalPlayer;
|
import net.minecraft.client.player.LocalPlayer;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
import net.minecraft.core.Direction;
|
import net.minecraft.core.Direction;
|
||||||
import net.minecraft.core.Direction.AxisDirection;
|
import net.minecraft.core.Direction.AxisDirection;
|
||||||
import net.minecraft.core.Vec3i;
|
import net.minecraft.core.Vec3i;
|
||||||
import net.minecraft.nbt.CompoundTag;
|
|
||||||
import net.minecraft.nbt.NbtIo;
|
|
||||||
import net.minecraft.util.Mth;
|
import net.minecraft.util.Mth;
|
||||||
import net.minecraft.world.InteractionHand;
|
import net.minecraft.world.InteractionHand;
|
||||||
import net.minecraft.world.item.context.BlockPlaceContext;
|
import net.minecraft.world.item.context.BlockPlaceContext;
|
||||||
import net.minecraft.world.item.context.UseOnContext;
|
import net.minecraft.world.item.context.UseOnContext;
|
||||||
import net.minecraft.world.level.Level;
|
|
||||||
import net.minecraft.world.level.block.Blocks;
|
|
||||||
import net.minecraft.world.level.levelgen.structure.BoundingBox;
|
|
||||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate;
|
|
||||||
import net.minecraft.world.phys.AABB;
|
import net.minecraft.world.phys.AABB;
|
||||||
import net.minecraft.world.phys.BlockHitResult;
|
import net.minecraft.world.phys.BlockHitResult;
|
||||||
import net.minecraft.world.phys.HitResult.Type;
|
import net.minecraft.world.phys.HitResult.Type;
|
||||||
|
@ -52,8 +42,8 @@ public class SchematicAndQuillHandler {
|
||||||
|
|
||||||
private Object outlineSlot = new Object();
|
private Object outlineSlot = new Object();
|
||||||
|
|
||||||
private BlockPos firstPos;
|
public BlockPos firstPos;
|
||||||
private BlockPos secondPos;
|
public BlockPos secondPos;
|
||||||
private BlockPos selectedPos;
|
private BlockPos selectedPos;
|
||||||
private Direction selectedFace;
|
private Direction selectedFace;
|
||||||
private int range = 10;
|
private int range = 10;
|
||||||
|
@ -212,58 +202,31 @@ public class SchematicAndQuillHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void saveSchematic(String string, boolean convertImmediately) {
|
public void saveSchematic(String string, boolean convertImmediately) {
|
||||||
StructureTemplate t = new StructureTemplate();
|
SchematicExportResult result = SchematicExport.saveSchematic(
|
||||||
BoundingBox bb = BoundingBox.fromCorners(firstPos, secondPos);
|
SchematicExport.SCHEMATICS, string, false,
|
||||||
BlockPos origin = new BlockPos(bb.minX(), bb.minY(), bb.minZ());
|
Minecraft.getInstance().level, firstPos, secondPos
|
||||||
BlockPos bounds = new BlockPos(bb.getXSpan(), bb.getYSpan(), bb.getZSpan());
|
);
|
||||||
Level level = Minecraft.getInstance().level;
|
LocalPlayer player = Minecraft.getInstance().player;
|
||||||
|
if (result == null) {
|
||||||
t.fillFromWorld(level, origin, bounds, true, Blocks.AIR);
|
Lang.translate("schematicAndQuill.failed")
|
||||||
|
.style(ChatFormatting.RED)
|
||||||
if (string.isEmpty())
|
.sendStatus(player);
|
||||||
string = Lang.translateDirect("schematicAndQuill.fallbackName")
|
return;
|
||||||
.getString();
|
|
||||||
|
|
||||||
String folderPath = "schematics";
|
|
||||||
FilesHelper.createFolderIfMissing(folderPath);
|
|
||||||
String filename = FilesHelper.findFirstValidFilename(string, folderPath, "nbt");
|
|
||||||
String filepath = folderPath + "/" + filename;
|
|
||||||
|
|
||||||
Path path = Paths.get(filepath);
|
|
||||||
OutputStream outputStream = null;
|
|
||||||
try {
|
|
||||||
outputStream = Files.newOutputStream(path, StandardOpenOption.CREATE);
|
|
||||||
CompoundTag nbttagcompound = t.save(new CompoundTag());
|
|
||||||
SchematicAndQuillItem.replaceStructureVoidWithAir(nbttagcompound);
|
|
||||||
SchematicAndQuillItem.clampGlueBoxes(level, new AABB(origin, origin.offset(bounds)), nbttagcompound);
|
|
||||||
NbtIo.writeCompressed(nbttagcompound, outputStream);
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
} finally {
|
|
||||||
if (outputStream != null)
|
|
||||||
IOUtils.closeQuietly(outputStream);
|
|
||||||
}
|
}
|
||||||
|
Path file = result.file();
|
||||||
|
Lang.translate("schematicAndQuill.saved", file)
|
||||||
|
.sendStatus(player);
|
||||||
firstPos = null;
|
firstPos = null;
|
||||||
secondPos = null;
|
secondPos = null;
|
||||||
LocalPlayer player = Minecraft.getInstance().player;
|
|
||||||
Lang.translate("schematicAndQuill.saved", filepath)
|
|
||||||
.sendStatus(player);
|
|
||||||
|
|
||||||
if (!convertImmediately)
|
if (!convertImmediately)
|
||||||
return;
|
return;
|
||||||
if (!Files.exists(path)) {
|
|
||||||
Create.LOGGER.error("Missing Schematic file: " + path.toString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
try {
|
||||||
if (!ClientSchematicLoader.validateSizeLimitation(Files.size(path)))
|
if (!ClientSchematicLoader.validateSizeLimitation(Files.size(file)))
|
||||||
return;
|
return;
|
||||||
AllPackets.getChannel().sendToServer(new InstantSchematicPacket(filename, origin, bounds));
|
AllPackets.getChannel()
|
||||||
|
.sendToServer(new InstantSchematicPacket(result.fileName(), result.origin(), result.bounds()));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Create.LOGGER.error("Error finding Schematic file: " + path.toString());
|
Create.LOGGER.error("Error instantly uploading Schematic file: " + file, e);
|
||||||
e.printStackTrace();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -109,5 +109,4 @@ public class SchematicPromptScreen extends AbstractSimiScreen {
|
||||||
CreateClient.SCHEMATIC_AND_QUILL_HANDLER.saveSchematic(nameField.getValue(), convertImmediately);
|
CreateClient.SCHEMATIC_AND_QUILL_HANDLER.saveSchematic(nameField.getValue(), convertImmediately);
|
||||||
onClose();
|
onClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -295,14 +295,14 @@ public class ClientEvents {
|
||||||
|
|
||||||
if (AllFluids.CHOCOLATE.get()
|
if (AllFluids.CHOCOLATE.get()
|
||||||
.isSame(fluid)) {
|
.isSame(fluid)) {
|
||||||
event.scaleFarPlaneDistance(1f / 32f);
|
event.scaleFarPlaneDistance(1f / 32f * AllConfigs.CLIENT.chocolateTransparencyMultiplier.getF());
|
||||||
event.setCanceled(true);
|
event.setCanceled(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (AllFluids.HONEY.get()
|
if (AllFluids.HONEY.get()
|
||||||
.isSame(fluid)) {
|
.isSame(fluid)) {
|
||||||
event.scaleFarPlaneDistance(1f / 8f);
|
event.scaleFarPlaneDistance(1f / 8f * AllConfigs.CLIENT.honeyTransparencyMultiplier.getF());
|
||||||
event.setCanceled(true);
|
event.setCanceled(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,8 @@ import com.mojang.brigadier.tree.LiteralCommandNode;
|
||||||
import net.minecraft.commands.CommandSourceStack;
|
import net.minecraft.commands.CommandSourceStack;
|
||||||
import net.minecraft.commands.Commands;
|
import net.minecraft.commands.Commands;
|
||||||
import net.minecraft.world.entity.player.Player;
|
import net.minecraft.world.entity.player.Player;
|
||||||
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
|
import net.minecraftforge.fml.loading.FMLLoader;
|
||||||
|
|
||||||
public class AllCommands {
|
public class AllCommands {
|
||||||
|
|
||||||
|
@ -20,7 +22,7 @@ public class AllCommands {
|
||||||
|
|
||||||
LiteralCommandNode<CommandSourceStack> util = buildUtilityCommands();
|
LiteralCommandNode<CommandSourceStack> util = buildUtilityCommands();
|
||||||
|
|
||||||
LiteralCommandNode<CommandSourceStack> createRoot = dispatcher.register(Commands.literal("create")
|
LiteralArgumentBuilder<CommandSourceStack> root = Commands.literal("create")
|
||||||
.requires(cs -> cs.hasPermission(0))
|
.requires(cs -> cs.hasPermission(0))
|
||||||
// general purpose
|
// general purpose
|
||||||
.then(new ToggleDebugCommand().register())
|
.then(new ToggleDebugCommand().register())
|
||||||
|
@ -38,8 +40,12 @@ public class AllCommands {
|
||||||
.then(GlueCommand.register())
|
.then(GlueCommand.register())
|
||||||
|
|
||||||
// utility
|
// utility
|
||||||
.then(util)
|
.then(util);
|
||||||
);
|
|
||||||
|
if (!FMLLoader.isProduction() && FMLLoader.getDist() == Dist.CLIENT)
|
||||||
|
root.then(CreateTestCommand.register());
|
||||||
|
|
||||||
|
LiteralCommandNode<CommandSourceStack> createRoot = dispatcher.register(root);
|
||||||
|
|
||||||
createRoot.addChild(buildRedirect("u", util));
|
createRoot.addChild(buildRedirect("u", util));
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
package com.simibubi.create.foundation.command;
|
||||||
|
|
||||||
|
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||||
|
import com.mojang.brigadier.builder.ArgumentBuilder;
|
||||||
|
|
||||||
|
import com.mojang.brigadier.context.CommandContext;
|
||||||
|
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||||
|
import com.mojang.brigadier.suggestion.Suggestions;
|
||||||
|
|
||||||
|
import com.mojang.brigadier.suggestion.SuggestionsBuilder;
|
||||||
|
|
||||||
|
import com.simibubi.create.CreateClient;
|
||||||
|
|
||||||
|
import com.simibubi.create.content.schematics.SchematicExport;
|
||||||
|
import com.simibubi.create.content.schematics.SchematicExport.SchematicExportResult;
|
||||||
|
import com.simibubi.create.content.schematics.client.SchematicAndQuillHandler;
|
||||||
|
|
||||||
|
import com.simibubi.create.foundation.utility.Components;
|
||||||
|
|
||||||
|
import net.minecraft.ChatFormatting;
|
||||||
|
import net.minecraft.commands.CommandSourceStack;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraftforge.fml.loading.FMLPaths;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import static net.minecraft.commands.Commands.argument;
|
||||||
|
import static net.minecraft.commands.Commands.literal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This command allows for quick exporting of GameTests.
|
||||||
|
* It is only registered in a client development environment. It is not safe in production or multiplayer.
|
||||||
|
*/
|
||||||
|
public class CreateTestCommand {
|
||||||
|
private static final Path gametests = FMLPaths.GAMEDIR.get()
|
||||||
|
.getParent()
|
||||||
|
.resolve("src/main/resources/data/create/structures/gametest")
|
||||||
|
.toAbsolutePath();
|
||||||
|
|
||||||
|
public static ArgumentBuilder<CommandSourceStack, ?> register() {
|
||||||
|
return literal("test")
|
||||||
|
.then(literal("export")
|
||||||
|
.then(argument("path", StringArgumentType.greedyString())
|
||||||
|
.suggests(CreateTestCommand::getSuggestions)
|
||||||
|
.executes(ctx -> handleExport(
|
||||||
|
ctx.getSource(),
|
||||||
|
ctx.getSource().getLevel(),
|
||||||
|
StringArgumentType.getString(ctx, "path")
|
||||||
|
))
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int handleExport(CommandSourceStack source, ServerLevel level, String path) {
|
||||||
|
SchematicAndQuillHandler handler = CreateClient.SCHEMATIC_AND_QUILL_HANDLER;
|
||||||
|
if (handler.firstPos == null || handler.secondPos == null) {
|
||||||
|
source.sendFailure(Components.literal("You must select an area with the Schematic and Quill first."));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
SchematicExportResult result = SchematicExport.saveSchematic(
|
||||||
|
gametests, path, true,
|
||||||
|
level, handler.firstPos, handler.secondPos
|
||||||
|
);
|
||||||
|
if (result == null)
|
||||||
|
source.sendFailure(Components.literal("Failed to export, check logs").withStyle(ChatFormatting.RED));
|
||||||
|
else {
|
||||||
|
sendSuccess(source, "Successfully exported test!", ChatFormatting.GREEN);
|
||||||
|
sendSuccess(source, "Overwritten: " + result.overwritten(), ChatFormatting.AQUA);
|
||||||
|
sendSuccess(source, "File: " + result.file(), ChatFormatting.GRAY);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void sendSuccess(CommandSourceStack source, String text, ChatFormatting color) {
|
||||||
|
source.sendSuccess(Components.literal(text).withStyle(color), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// find existing tests and folders for autofill
|
||||||
|
private static CompletableFuture<Suggestions> getSuggestions(CommandContext<CommandSourceStack> context,
|
||||||
|
SuggestionsBuilder builder) throws CommandSyntaxException {
|
||||||
|
String path = builder.getRemaining();
|
||||||
|
if (!path.contains("/") || path.contains(".."))
|
||||||
|
return findInDir(gametests, builder);
|
||||||
|
int lastSlash = path.lastIndexOf("/");
|
||||||
|
Path subDir = gametests.resolve(path.substring(0, lastSlash));
|
||||||
|
if (Files.exists(subDir))
|
||||||
|
findInDir(subDir, builder);
|
||||||
|
return builder.buildFuture();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CompletableFuture<Suggestions> findInDir(Path dir, SuggestionsBuilder builder) {
|
||||||
|
try (Stream<Path> paths = Files.list(dir)) {
|
||||||
|
paths.filter(p -> Files.isDirectory(p) || p.toString().endsWith(".nbt"))
|
||||||
|
.forEach(path -> {
|
||||||
|
String file = path.toString()
|
||||||
|
.replaceAll("\\\\", "/")
|
||||||
|
.substring(gametests.toString().length() + 1);
|
||||||
|
if (Files.isDirectory(path))
|
||||||
|
file += "/";
|
||||||
|
builder.suggest(file);
|
||||||
|
});
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
return builder.buildFuture();
|
||||||
|
}
|
||||||
|
}
|
|
@ -33,6 +33,13 @@ public class CClient extends ConfigBase {
|
||||||
public final ConfigBool ignoreFabulousWarning = b(false, "ignoreFabulousWarning",
|
public final ConfigBool ignoreFabulousWarning = b(false, "ignoreFabulousWarning",
|
||||||
Comments.ignoreFabulousWarning);
|
Comments.ignoreFabulousWarning);
|
||||||
|
|
||||||
|
// custom fluid fog
|
||||||
|
public final ConfigGroup fluidFogSettings = group(1, "fluidFogSettings", Comments.fluidFogSettings);
|
||||||
|
public final ConfigFloat honeyTransparencyMultiplier =
|
||||||
|
f(1, .125f, 256, "honey", Comments.honeyTransparencyMultiplier);
|
||||||
|
public final ConfigFloat chocolateTransparencyMultiplier =
|
||||||
|
f(1, .125f, 256, "chocolate", Comments.chocolateTransparencyMultiplier);
|
||||||
|
|
||||||
//overlay group
|
//overlay group
|
||||||
public final ConfigGroup overlay = group(1, "goggleOverlay",
|
public final ConfigGroup overlay = group(1, "goggleOverlay",
|
||||||
Comments.overlay);
|
Comments.overlay);
|
||||||
|
@ -149,6 +156,9 @@ public class CClient extends ConfigBase {
|
||||||
static String mountedZoomMultiplier = "How far away the Camera should zoom when seated on a train";
|
static String mountedZoomMultiplier = "How far away the Camera should zoom when seated on a train";
|
||||||
static String showTrackGraphOnF3 = "Display nodes and edges of a Railway Network while f3 debug mode is active";
|
static String showTrackGraphOnF3 = "Display nodes and edges of a Railway Network while f3 debug mode is active";
|
||||||
static String showExtendedTrackGraphOnF3 = "Additionally display materials of a Rail Network while f3 debug mode is active";
|
static String showExtendedTrackGraphOnF3 = "Additionally display materials of a Rail Network while f3 debug mode is active";
|
||||||
|
static String fluidFogSettings = "Configure your vision range when submerged in Create's custom fluids";
|
||||||
|
static String honeyTransparencyMultiplier = "The vision range through honey will be multiplied by this factor";
|
||||||
|
static String chocolateTransparencyMultiplier = "The vision range though chocolate will be multiplied by this factor";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ public class CLogistics extends ConfigBase {
|
||||||
public final ConfigInt linkRange = i(256, 1, "linkRange", Comments.linkRange);
|
public final ConfigInt linkRange = i(256, 1, "linkRange", Comments.linkRange);
|
||||||
public final ConfigInt displayLinkRange = i(64, 1, "displayLinkRange", Comments.displayLinkRange);
|
public final ConfigInt displayLinkRange = i(64, 1, "displayLinkRange", Comments.displayLinkRange);
|
||||||
public final ConfigInt vaultCapacity = i(20, 1, "vaultCapacity", Comments.vaultCapacity);
|
public final ConfigInt vaultCapacity = i(20, 1, "vaultCapacity", Comments.vaultCapacity);
|
||||||
|
public final ConfigInt brassTunnelTimer = i(10, 1, 10, "brassTunnelTimer", Comments.brassTunnelTimer);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getName() {
|
public String getName() {
|
||||||
|
@ -24,6 +25,7 @@ public class CLogistics extends ConfigBase {
|
||||||
"The amount of ticks a portable storage interface waits for transfers until letting contraptions move along.";
|
"The amount of ticks a portable storage interface waits for transfers until letting contraptions move along.";
|
||||||
static String mechanicalArmRange = "Maximum distance in blocks a Mechanical Arm can reach across.";
|
static String mechanicalArmRange = "Maximum distance in blocks a Mechanical Arm can reach across.";
|
||||||
static String vaultCapacity = "The total amount of stacks a vault can hold per block in size.";
|
static String vaultCapacity = "The total amount of stacks a vault can hold per block in size.";
|
||||||
|
static String brassTunnelTimer = "The amount of ticks a brass tunnel waits between distributions.";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -189,7 +189,10 @@ public enum AllGuiTextures implements ScreenElement {
|
||||||
TRAIN_PROMPT("widgets", 0, 230, 256, 16),
|
TRAIN_PROMPT("widgets", 0, 230, 256, 16),
|
||||||
|
|
||||||
// PlacementIndicator
|
// PlacementIndicator
|
||||||
PLACEMENT_INDICATOR_SHEET("placement_indicator", 0, 0, 16, 256);
|
PLACEMENT_INDICATOR_SHEET("placement_indicator", 0, 0, 16, 256),
|
||||||
|
|
||||||
|
// ComputerCraft
|
||||||
|
COMPUTER("computer", 200, 102);
|
||||||
|
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
package com.simibubi.create.foundation.mixin;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.gametest.framework.GameTestRegistry;
|
||||||
|
import net.minecraft.gametest.framework.GameTestRunner;
|
||||||
|
import net.minecraft.gametest.framework.GameTestServer;
|
||||||
|
import net.minecraft.server.Main;
|
||||||
|
|
||||||
|
import net.minecraft.server.MinecraftServer;
|
||||||
|
|
||||||
|
import net.minecraft.server.packs.repository.PackRepository;
|
||||||
|
import net.minecraft.world.level.storage.LevelStorageSource.LevelStorageAccess;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.ModifyVariable;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
@Mixin(Main.class)
|
||||||
|
public class MainMixin {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forge completely bypasses vanilla's
|
||||||
|
* {@link GameTestServer#create(Thread, LevelStorageAccess, PackRepository, Collection, BlockPos)},
|
||||||
|
* which causes tests to generate at bedrock level in a regular world. This causes interference
|
||||||
|
* (ex. darkness, liquids, gravel) that makes tests fail and act inconsistently. Replacing the server Forge
|
||||||
|
* makes with one made by vanilla's factory causes tests to run on a superflat, as they should.
|
||||||
|
* <p>
|
||||||
|
* The system property 'create.useOriginalGametestServer' may be set to true to avoid this behavior.
|
||||||
|
* This may be desirable for other mods which pull Create into their development environments.
|
||||||
|
*/
|
||||||
|
@ModifyVariable(
|
||||||
|
method = "lambda$main$5",
|
||||||
|
at = @At(
|
||||||
|
value = "STORE",
|
||||||
|
ordinal = 0
|
||||||
|
),
|
||||||
|
require = 0 // don't crash if this fails
|
||||||
|
)
|
||||||
|
private static MinecraftServer create$correctlyInitializeGametestServer(MinecraftServer original) {
|
||||||
|
if (original instanceof GameTestServer && !Boolean.getBoolean("create.useOriginalGametestServer")) {
|
||||||
|
return GameTestServer.create(
|
||||||
|
original.getRunningThread(),
|
||||||
|
original.storageSource,
|
||||||
|
original.getPackRepository(),
|
||||||
|
GameTestRunner.groupTestsIntoBatches(GameTestRegistry.getAllTestFunctions()),
|
||||||
|
BlockPos.ZERO
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return original;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
package com.simibubi.create.foundation.mixin;
|
||||||
|
|
||||||
|
import com.simibubi.create.gametest.infrastructure.CreateTestFunction;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.gametest.framework.GameTestRegistry;
|
||||||
|
import net.minecraft.gametest.framework.MultipleTestTracker;
|
||||||
|
import net.minecraft.gametest.framework.TestCommand;
|
||||||
|
|
||||||
|
import net.minecraft.gametest.framework.TestFunction;
|
||||||
|
|
||||||
|
import net.minecraft.nbt.CompoundTag;
|
||||||
|
import net.minecraft.nbt.Tag;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
|
||||||
|
import net.minecraft.world.level.block.entity.StructureBlockEntity;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.injection.At;
|
||||||
|
import org.spongepowered.asm.mixin.injection.Redirect;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
@Mixin(TestCommand.class)
|
||||||
|
public class TestCommandMixin {
|
||||||
|
@Redirect(
|
||||||
|
method = "runTest(Lnet/minecraft/server/level/ServerLevel;Lnet/minecraft/core/BlockPos;Lnet/minecraft/gametest/framework/MultipleTestTracker;)V",
|
||||||
|
at = @At(
|
||||||
|
value = "INVOKE",
|
||||||
|
target = "Lnet/minecraft/gametest/framework/GameTestRegistry;getTestFunction(Ljava/lang/String;)Lnet/minecraft/gametest/framework/TestFunction;"
|
||||||
|
),
|
||||||
|
require = 0 // don't crash if this fails. non-critical
|
||||||
|
)
|
||||||
|
private static TestFunction create$getCorrectTestFunction(String testName,
|
||||||
|
ServerLevel level, BlockPos pos, @Nullable MultipleTestTracker tracker) {
|
||||||
|
StructureBlockEntity be = (StructureBlockEntity) level.getBlockEntity(pos);
|
||||||
|
CompoundTag data = be.getTileData();
|
||||||
|
if (!data.contains("CreateTestFunction", Tag.TAG_STRING))
|
||||||
|
return GameTestRegistry.getTestFunction(testName);
|
||||||
|
String name = data.getString("CreateTestFunction");
|
||||||
|
CreateTestFunction function = CreateTestFunction.NAMES_TO_FUNCTIONS.get(name);
|
||||||
|
if (function == null)
|
||||||
|
throw new IllegalStateException("Structure block has CreateTestFunction attached, but test [" + name + "] doesn't exist");
|
||||||
|
return function;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,17 @@
|
||||||
|
package com.simibubi.create.foundation.mixin.accessor;
|
||||||
|
|
||||||
|
import net.minecraft.gametest.framework.GameTestHelper;
|
||||||
|
import net.minecraft.gametest.framework.GameTestInfo;
|
||||||
|
|
||||||
|
import org.spongepowered.asm.mixin.Mixin;
|
||||||
|
import org.spongepowered.asm.mixin.gen.Accessor;
|
||||||
|
|
||||||
|
@Mixin(GameTestHelper.class)
|
||||||
|
public interface GameTestHelperAccessor {
|
||||||
|
@Accessor
|
||||||
|
GameTestInfo getTestInfo();
|
||||||
|
@Accessor
|
||||||
|
boolean getFinalCheckAdded();
|
||||||
|
@Accessor
|
||||||
|
void setFinalCheckAdded(boolean value);
|
||||||
|
}
|
|
@ -8,6 +8,7 @@ import java.util.function.Function;
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import com.simibubi.create.Create;
|
import com.simibubi.create.Create;
|
||||||
|
import com.simibubi.create.compat.computercraft.AttachedComputerPacket;
|
||||||
import com.simibubi.create.content.contraptions.components.actors.controls.ContraptionDisableActorPacket;
|
import com.simibubi.create.content.contraptions.components.actors.controls.ContraptionDisableActorPacket;
|
||||||
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionBlockChangedPacket;
|
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionBlockChangedPacket;
|
||||||
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionColliderLockPacket;
|
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionColliderLockPacket;
|
||||||
|
@ -202,6 +203,7 @@ public enum AllPackets {
|
||||||
SET_FIRE_IMMUNE(NetheriteDivingHandler.SetFireImmunePacket.class, NetheriteDivingHandler.SetFireImmunePacket::new,
|
SET_FIRE_IMMUNE(NetheriteDivingHandler.SetFireImmunePacket.class, NetheriteDivingHandler.SetFireImmunePacket::new,
|
||||||
PLAY_TO_CLIENT),
|
PLAY_TO_CLIENT),
|
||||||
CONTRAPTION_COLLIDER_LOCK(ContraptionColliderLockPacket.class, ContraptionColliderLockPacket::new, PLAY_TO_CLIENT),
|
CONTRAPTION_COLLIDER_LOCK(ContraptionColliderLockPacket.class, ContraptionColliderLockPacket::new, PLAY_TO_CLIENT),
|
||||||
|
ATTACHED_COMPUTER(AttachedComputerPacket.class, AttachedComputerPacket::new, PLAY_TO_CLIENT),
|
||||||
|
|
||||||
;
|
;
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ package com.simibubi.create.foundation.ponder.content;
|
||||||
import com.simibubi.create.AllBlocks;
|
import com.simibubi.create.AllBlocks;
|
||||||
import com.simibubi.create.AllItems;
|
import com.simibubi.create.AllItems;
|
||||||
import com.simibubi.create.Create;
|
import com.simibubi.create.Create;
|
||||||
|
import com.simibubi.create.compat.Mods;
|
||||||
import com.simibubi.create.content.logistics.trains.TrackMaterial;
|
import com.simibubi.create.content.logistics.trains.TrackMaterial;
|
||||||
import com.simibubi.create.content.logistics.trains.track.TrackBlock;
|
import com.simibubi.create.content.logistics.trains.track.TrackBlock;
|
||||||
import com.simibubi.create.foundation.config.AllConfigs;
|
import com.simibubi.create.foundation.config.AllConfigs;
|
||||||
|
@ -23,7 +24,9 @@ import com.simibubi.create.foundation.ponder.content.trains.TrainSignalScenes;
|
||||||
import com.simibubi.create.foundation.ponder.content.trains.TrainStationScenes;
|
import com.simibubi.create.foundation.ponder.content.trains.TrainStationScenes;
|
||||||
import com.tterrag.registrate.util.entry.BlockEntry;
|
import com.tterrag.registrate.util.entry.BlockEntry;
|
||||||
|
|
||||||
|
import net.minecraft.resources.ResourceLocation;
|
||||||
import net.minecraft.world.item.DyeColor;
|
import net.minecraft.world.item.DyeColor;
|
||||||
|
import net.minecraft.world.level.block.Block;
|
||||||
import net.minecraft.world.level.block.Blocks;
|
import net.minecraft.world.level.block.Blocks;
|
||||||
import net.minecraftforge.registries.ForgeRegistries;
|
import net.minecraftforge.registries.ForgeRegistries;
|
||||||
import net.minecraftforge.registries.RegistryObject;
|
import net.minecraftforge.registries.RegistryObject;
|
||||||
|
@ -596,6 +599,12 @@ public class PonderIndex {
|
||||||
.add(Blocks.COMMAND_BLOCK)
|
.add(Blocks.COMMAND_BLOCK)
|
||||||
.add(Blocks.TARGET);
|
.add(Blocks.TARGET);
|
||||||
|
|
||||||
|
Mods.COMPUTERCRAFT.executeIfInstalled(() -> () -> {
|
||||||
|
Block computer = ForgeRegistries.BLOCKS.getValue(new ResourceLocation(Mods.COMPUTERCRAFT.asId(), "computer_advanced"));
|
||||||
|
if (computer != null)
|
||||||
|
PonderRegistry.TAGS.forTag(PonderTag.DISPLAY_SOURCES).add(computer);
|
||||||
|
});
|
||||||
|
|
||||||
PonderRegistry.TAGS.forTag(PonderTag.DISPLAY_TARGETS)
|
PonderRegistry.TAGS.forTag(PonderTag.DISPLAY_TARGETS)
|
||||||
.add(AllBlocks.ORANGE_NIXIE_TUBE)
|
.add(AllBlocks.ORANGE_NIXIE_TUBE)
|
||||||
.add(AllBlocks.DISPLAY_BOARD)
|
.add(AllBlocks.DISPLAY_BOARD)
|
||||||
|
|
|
@ -5,6 +5,7 @@ import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
import java.nio.file.StandardOpenOption;
|
import java.nio.file.StandardOpenOption;
|
||||||
|
|
||||||
|
@ -27,15 +28,15 @@ public class FilesHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String findFirstValidFilename(String name, String folderPath, String extension) {
|
public static String findFirstValidFilename(String name, Path folderPath, String extension) {
|
||||||
int index = 0;
|
int index = 0;
|
||||||
String filename;
|
String filename;
|
||||||
String filepath;
|
Path filepath;
|
||||||
do {
|
do {
|
||||||
filename = slug(name) + ((index == 0) ? "" : "_" + index) + "." + extension;
|
filename = slug(name) + ((index == 0) ? "" : "_" + index) + "." + extension;
|
||||||
index++;
|
index++;
|
||||||
filepath = folderPath + "/" + filename;
|
filepath = folderPath.resolve(filename);
|
||||||
} while (Files.exists(Paths.get(filepath)));
|
} while (Files.exists(filepath));
|
||||||
return filename;
|
return filename;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
package com.simibubi.create.foundation.utility;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
public class StringHelper {
|
||||||
|
|
||||||
|
public static String snakeCaseToCamelCase(String text) {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
builder.append(text.substring(0, 1).toUpperCase(Locale.ROOT));
|
||||||
|
|
||||||
|
for (int i = 1; i < text.length(); i++) {
|
||||||
|
int j = text.indexOf('_', i);
|
||||||
|
|
||||||
|
if (j == -1) {
|
||||||
|
builder.append(text.substring(i));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.append(text.substring(i, j).toLowerCase(Locale.ROOT));
|
||||||
|
builder.append(text.substring(j + 1, j + 2).toUpperCase(Locale.ROOT));
|
||||||
|
|
||||||
|
i = j + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String camelCaseToSnakeCase(String text) {
|
||||||
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
|
for (char c : text.toCharArray()) {
|
||||||
|
if (Character.isUpperCase(c)) {
|
||||||
|
builder.append('_');
|
||||||
|
builder.append(Character.toLowerCase(c));
|
||||||
|
} else {
|
||||||
|
builder.append(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (builder.length() > 0 && builder.charAt(0) == '_')
|
||||||
|
builder.deleteCharAt(0);
|
||||||
|
|
||||||
|
return builder.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
package com.simibubi.create.gametest;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import com.simibubi.create.gametest.infrastructure.CreateTestFunction;
|
||||||
|
|
||||||
|
import com.simibubi.create.gametest.tests.TestContraptions;
|
||||||
|
import com.simibubi.create.gametest.tests.TestFluids;
|
||||||
|
import com.simibubi.create.gametest.tests.TestItems;
|
||||||
|
import com.simibubi.create.gametest.tests.TestMisc;
|
||||||
|
import com.simibubi.create.gametest.tests.TestProcessing;
|
||||||
|
|
||||||
|
import net.minecraft.gametest.framework.GameTestGenerator;
|
||||||
|
import net.minecraft.gametest.framework.TestFunction;
|
||||||
|
import net.minecraftforge.event.RegisterGameTestsEvent;
|
||||||
|
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
||||||
|
import net.minecraftforge.fml.common.Mod.EventBusSubscriber;
|
||||||
|
import net.minecraftforge.fml.common.Mod.EventBusSubscriber.Bus;
|
||||||
|
|
||||||
|
@EventBusSubscriber(bus = Bus.MOD)
|
||||||
|
public class CreateGameTests {
|
||||||
|
private static final Class<?>[] testHolders = {
|
||||||
|
TestContraptions.class,
|
||||||
|
TestFluids.class,
|
||||||
|
TestItems.class,
|
||||||
|
TestMisc.class,
|
||||||
|
TestProcessing.class
|
||||||
|
};
|
||||||
|
|
||||||
|
@SubscribeEvent
|
||||||
|
public static void registerTests(RegisterGameTestsEvent event) {
|
||||||
|
event.register(CreateGameTests.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTestGenerator
|
||||||
|
public static Collection<TestFunction> generateTests() {
|
||||||
|
return CreateTestFunction.getTestsFrom(testHolders);
|
||||||
|
}
|
||||||
|
}
|
15
src/main/java/com/simibubi/create/gametest/TESTING.md
Normal file
15
src/main/java/com/simibubi/create/gametest/TESTING.md
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
# Adding to GameTests
|
||||||
|
|
||||||
|
#### Adding Tests
|
||||||
|
All tests must be static, take a `CreateGameTestHelper`, return void, and be annotated with `@GameTest`.
|
||||||
|
Non-annotated methods will be ignored. The annotation must also specify a structure template.
|
||||||
|
Classes holding registered tests must be annotated with `GameTestGroup`.
|
||||||
|
|
||||||
|
#### Adding Groups/Classes
|
||||||
|
Added test classes must be added to the list in `CreateGameTests`. They must be annotated with
|
||||||
|
`@GameTestGroup` and given a structure path.
|
||||||
|
|
||||||
|
#### Exporting Structures
|
||||||
|
Structures can be quickly exported using the `/create test export` command (or `/c test export`).
|
||||||
|
Select an area with the Schematic and Quill, and run it to quickly export a test structure
|
||||||
|
directly to the correct directory.
|
|
@ -0,0 +1,452 @@
|
||||||
|
package com.simibubi.create.gametest.infrastructure;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import com.simibubi.create.foundation.mixin.accessor.GameTestHelperAccessor;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2LongArrayMap;
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2LongMap;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
|
import net.minecraft.world.level.block.LeverBlock;
|
||||||
|
import net.minecraftforge.fluids.FluidStack;
|
||||||
|
|
||||||
|
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
|
||||||
|
import net.minecraftforge.fluids.capability.IFluidHandler;
|
||||||
|
import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction;
|
||||||
|
import net.minecraftforge.items.CapabilityItemHandler;
|
||||||
|
import net.minecraftforge.items.IItemHandler;
|
||||||
|
import net.minecraftforge.items.ItemHandlerHelper;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Contract;
|
||||||
|
|
||||||
|
import com.simibubi.create.AllTileEntities;
|
||||||
|
import com.simibubi.create.content.logistics.block.belts.tunnel.BrassTunnelTileEntity.SelectionMode;
|
||||||
|
import com.simibubi.create.content.logistics.block.redstone.NixieTubeTileEntity;
|
||||||
|
import com.simibubi.create.foundation.item.ItemHelper;
|
||||||
|
import com.simibubi.create.foundation.tileEntity.IMultiTileContainer;
|
||||||
|
import com.simibubi.create.foundation.tileEntity.TileEntityBehaviour;
|
||||||
|
import com.simibubi.create.foundation.tileEntity.behaviour.BehaviourType;
|
||||||
|
import com.simibubi.create.foundation.tileEntity.behaviour.scrollvalue.ScrollOptionBehaviour;
|
||||||
|
import com.simibubi.create.foundation.tileEntity.behaviour.scrollvalue.ScrollValueBehaviour;
|
||||||
|
import com.simibubi.create.foundation.utility.RegisteredObjects;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Direction;
|
||||||
|
import net.minecraft.core.Registry;
|
||||||
|
import net.minecraft.gametest.framework.GameTestHelper;
|
||||||
|
import net.minecraft.gametest.framework.GameTestInfo;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.world.entity.Entity;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.entity.item.ItemEntity;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.level.ItemLike;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||||
|
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
|
||||||
|
import net.minecraft.world.level.levelgen.structure.BoundingBox;
|
||||||
|
import net.minecraft.world.phys.Vec3;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A helper class expanding the functionality of {@link GameTestHelper}.
|
||||||
|
* This class may replace the default helper parameter if a test is registered through {@link CreateTestFunction}.
|
||||||
|
*/
|
||||||
|
public class CreateGameTestHelper extends GameTestHelper {
|
||||||
|
public static final int TICKS_PER_SECOND = 20;
|
||||||
|
public static final int TEN_SECONDS = 10 * TICKS_PER_SECOND;
|
||||||
|
public static final int FIFTEEN_SECONDS = 15 * TICKS_PER_SECOND;
|
||||||
|
public static final int TWENTY_SECONDS = 20 * TICKS_PER_SECOND;
|
||||||
|
|
||||||
|
private CreateGameTestHelper(GameTestInfo testInfo) {
|
||||||
|
super(testInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CreateGameTestHelper of(GameTestHelper original) {
|
||||||
|
GameTestHelperAccessor access = (GameTestHelperAccessor) original;
|
||||||
|
CreateGameTestHelper helper = new CreateGameTestHelper(access.getTestInfo());
|
||||||
|
//noinspection DataFlowIssue // accessor applied at runtime
|
||||||
|
GameTestHelperAccessor newAccess = (GameTestHelperAccessor) helper;
|
||||||
|
newAccess.setFinalCheckAdded(access.getFinalCheckAdded());
|
||||||
|
return helper;
|
||||||
|
}
|
||||||
|
|
||||||
|
// blocks
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flip the direction of any block with the {@link BlockStateProperties#FACING} property.
|
||||||
|
*/
|
||||||
|
public void flipBlock(BlockPos pos) {
|
||||||
|
BlockState original = getBlockState(pos);
|
||||||
|
if (!original.hasProperty(BlockStateProperties.FACING))
|
||||||
|
fail("FACING property not in block: " + Registry.BLOCK.getId(original.getBlock()));
|
||||||
|
Direction facing = original.getValue(BlockStateProperties.FACING);
|
||||||
|
BlockState reversed = original.setValue(BlockStateProperties.FACING, facing.getOpposite());
|
||||||
|
setBlock(pos, reversed);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void assertNixiePower(BlockPos pos, int strength) {
|
||||||
|
NixieTubeTileEntity nixie = getBlockEntity(AllTileEntities.NIXIE_TUBE.get(), pos);
|
||||||
|
int actualStrength = nixie.getRedstoneStrength();
|
||||||
|
if (actualStrength != strength)
|
||||||
|
fail("Expected nixie tube at %s to have power of %s, got %s".formatted(pos, strength, actualStrength));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turn off a lever.
|
||||||
|
*/
|
||||||
|
public void powerLever(BlockPos pos) {
|
||||||
|
assertBlockPresent(Blocks.LEVER, pos);
|
||||||
|
if (!getBlockState(pos).getValue(LeverBlock.POWERED)) {
|
||||||
|
pullLever(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Turn on a lever.
|
||||||
|
*/
|
||||||
|
public void unpowerLever(BlockPos pos) {
|
||||||
|
assertBlockPresent(Blocks.LEVER, pos);
|
||||||
|
if (getBlockState(pos).getValue(LeverBlock.POWERED)) {
|
||||||
|
pullLever(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the {@link SelectionMode} of a belt tunnel at the given position.
|
||||||
|
* @param pos
|
||||||
|
* @param mode
|
||||||
|
*/
|
||||||
|
public void setTunnelMode(BlockPos pos, SelectionMode mode) {
|
||||||
|
ScrollValueBehaviour behavior = getBehavior(pos, ScrollOptionBehaviour.TYPE);
|
||||||
|
behavior.setValue(mode.ordinal());
|
||||||
|
}
|
||||||
|
|
||||||
|
// block entities
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the block entity of the expected type. If the type does not match, this fails the test.
|
||||||
|
*/
|
||||||
|
public <T extends BlockEntity> T getBlockEntity(BlockEntityType<T> type, BlockPos pos) {
|
||||||
|
BlockEntity be = getBlockEntity(pos);
|
||||||
|
BlockEntityType<?> actualType = be == null ? null : be.getType();
|
||||||
|
if (actualType != type) {
|
||||||
|
String actualId = actualType == null ? "null" : RegisteredObjects.getKeyOrThrow(actualType).toString();
|
||||||
|
String error = "Expected block entity at pos [%s] with type [%s], got [%s]".formatted(
|
||||||
|
pos, RegisteredObjects.getKeyOrThrow(type), actualId
|
||||||
|
);
|
||||||
|
fail(error);
|
||||||
|
}
|
||||||
|
return (T) be;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given any segment of an {@link IMultiTileContainer}, get the controller for it.
|
||||||
|
*/
|
||||||
|
public <T extends BlockEntity & IMultiTileContainer> T getControllerBlockEntity(BlockEntityType<T> type, BlockPos anySegment) {
|
||||||
|
T be = getBlockEntity(type, anySegment).getControllerTE();
|
||||||
|
if (be == null)
|
||||||
|
fail("Could not get block entity controller with type [%s] from pos [%s]".formatted(RegisteredObjects.getKeyOrThrow(type), anySegment));
|
||||||
|
return be;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the expected {@link TileEntityBehaviour} from the given position, failing if not present.
|
||||||
|
*/
|
||||||
|
public <T extends TileEntityBehaviour> T getBehavior(BlockPos pos, BehaviourType<T> type) {
|
||||||
|
T behavior = TileEntityBehaviour.get(getLevel(), absolutePos(pos), type);
|
||||||
|
if (behavior == null)
|
||||||
|
fail("Behavior at " + pos + " missing, expected " + type.getName());
|
||||||
|
return behavior;
|
||||||
|
}
|
||||||
|
|
||||||
|
// entities
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spawn an item entity at the given position with no velocity.
|
||||||
|
*/
|
||||||
|
public ItemEntity spawnItem(BlockPos pos, ItemStack stack) {
|
||||||
|
Vec3 spawn = Vec3.atCenterOf(absolutePos(pos));
|
||||||
|
ServerLevel level = getLevel();
|
||||||
|
ItemEntity item = new ItemEntity(level, spawn.x, spawn.y, spawn.z, stack, 0, 0, 0);
|
||||||
|
level.addFreshEntity(item);
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Spawn item entities given an item and amount. The amount will be split into multiple entities if
|
||||||
|
* larger than the item's max stack size.
|
||||||
|
*/
|
||||||
|
public void spawnItems(BlockPos pos, Item item, int amount) {
|
||||||
|
while (amount > 0) {
|
||||||
|
int toSpawn = Math.min(amount, item.getMaxStackSize());
|
||||||
|
amount -= toSpawn;
|
||||||
|
ItemStack stack = new ItemStack(item, toSpawn);
|
||||||
|
spawnItem(pos, stack);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the first entity found at the given position.
|
||||||
|
*/
|
||||||
|
public <T extends Entity> T getFirstEntity(EntityType<T> type, BlockPos pos) {
|
||||||
|
List<T> list = getEntitiesBetween(type, pos.north().east().above(), pos.south().west().below());
|
||||||
|
if (list.isEmpty())
|
||||||
|
fail("No entities at pos: " + pos);
|
||||||
|
return list.get(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a list of all entities between two positions, inclusive.
|
||||||
|
*/
|
||||||
|
public <T extends Entity> List<T> getEntitiesBetween(EntityType<T> type, BlockPos pos1, BlockPos pos2) {
|
||||||
|
BoundingBox box = BoundingBox.fromCorners(absolutePos(pos1), absolutePos(pos2));
|
||||||
|
List<? extends T> entities = getLevel().getEntities(type, e -> box.isInside(e.blockPosition()));
|
||||||
|
return (List<T>) entities;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// transfer - fluids
|
||||||
|
|
||||||
|
public IFluidHandler fluidStorageAt(BlockPos pos) {
|
||||||
|
BlockEntity be = getBlockEntity(pos);
|
||||||
|
if (be == null)
|
||||||
|
fail("BlockEntity not present");
|
||||||
|
Optional<IFluidHandler> handler = be.getCapability(CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY).resolve();
|
||||||
|
if (handler.isEmpty())
|
||||||
|
fail("handler not present");
|
||||||
|
return handler.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the content of the tank at the pos.
|
||||||
|
* content is determined by what the tank allows to be extracted.
|
||||||
|
*/
|
||||||
|
public FluidStack getTankContents(BlockPos tank) {
|
||||||
|
IFluidHandler handler = fluidStorageAt(tank);
|
||||||
|
return handler.drain(Integer.MAX_VALUE, FluidAction.SIMULATE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the total capacity of a tank at the given position.
|
||||||
|
*/
|
||||||
|
public long getTankCapacity(BlockPos pos) {
|
||||||
|
IFluidHandler handler = fluidStorageAt(pos);
|
||||||
|
long total = 0;
|
||||||
|
for (int i = 0; i < handler.getTanks(); i++) {
|
||||||
|
total += handler.getTankCapacity(i);
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the total fluid amount across all fluid tanks at the given positions.
|
||||||
|
*/
|
||||||
|
public long getFluidInTanks(BlockPos... tanks) {
|
||||||
|
long total = 0;
|
||||||
|
for (BlockPos tank : tanks) {
|
||||||
|
total += getTankContents(tank).getAmount();
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert that the given fluid stack is present in the given tank. The tank might also hold more than the fluid.
|
||||||
|
*/
|
||||||
|
public void assertFluidPresent(FluidStack fluid, BlockPos pos) {
|
||||||
|
FluidStack contained = getTankContents(pos);
|
||||||
|
if (!fluid.isFluidEqual(contained))
|
||||||
|
fail("Different fluids");
|
||||||
|
if (fluid.getAmount() != contained.getAmount())
|
||||||
|
fail("Different amounts");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert that the given tank holds no fluid.
|
||||||
|
*/
|
||||||
|
public void assertTankEmpty(BlockPos pos) {
|
||||||
|
assertFluidPresent(FluidStack.EMPTY, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void assertTanksEmpty(BlockPos... tanks) {
|
||||||
|
for (BlockPos tank : tanks) {
|
||||||
|
assertTankEmpty(tank);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// transfer - items
|
||||||
|
|
||||||
|
public IItemHandler itemStorageAt(BlockPos pos) {
|
||||||
|
BlockEntity be = getBlockEntity(pos);
|
||||||
|
if (be == null)
|
||||||
|
fail("BlockEntity not present");
|
||||||
|
Optional<IItemHandler> handler = be.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY).resolve();
|
||||||
|
if (handler.isEmpty())
|
||||||
|
fail("handler not present");
|
||||||
|
return handler.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get a map of contained items to their amounts. This is not safe for NBT!
|
||||||
|
*/
|
||||||
|
public Object2LongMap<Item> getItemContent(BlockPos pos) {
|
||||||
|
IItemHandler handler = itemStorageAt(pos);
|
||||||
|
Object2LongMap<Item> map = new Object2LongArrayMap<>();
|
||||||
|
for (int i = 0; i < handler.getSlots(); i++) {
|
||||||
|
ItemStack stack = handler.getStackInSlot(i);
|
||||||
|
if (stack.isEmpty())
|
||||||
|
continue;
|
||||||
|
Item item = stack.getItem();
|
||||||
|
long amount = map.getLong(item);
|
||||||
|
amount += stack.getCount();
|
||||||
|
map.put(item, amount);
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the combined total of all ItemStacks inside the inventory.
|
||||||
|
*/
|
||||||
|
public long getTotalItems(BlockPos pos) {
|
||||||
|
IItemHandler storage = itemStorageAt(pos);
|
||||||
|
long total = 0;
|
||||||
|
for (int i = 0; i < storage.getSlots(); i++) {
|
||||||
|
total += storage.getStackInSlot(i).getCount();
|
||||||
|
}
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Of the provided items, assert that at least one is present in the given inventory.
|
||||||
|
*/
|
||||||
|
public void assertAnyContained(BlockPos pos, Item... items) {
|
||||||
|
IItemHandler handler = itemStorageAt(pos);
|
||||||
|
boolean noneFound = true;
|
||||||
|
for (int i = 0; i < handler.getSlots(); i++) {
|
||||||
|
for (Item item : items) {
|
||||||
|
if (handler.getStackInSlot(i).is(item)) {
|
||||||
|
noneFound = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (noneFound)
|
||||||
|
fail("No matching items " + Arrays.toString(items) + " found in handler at pos: " + pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert that the inventory contains all the provided content.
|
||||||
|
*/
|
||||||
|
public void assertContentPresent(Object2LongMap<Item> content, BlockPos pos) {
|
||||||
|
IItemHandler handler = itemStorageAt(pos);
|
||||||
|
Object2LongMap<Item> map = new Object2LongArrayMap<>(content);
|
||||||
|
for (int i = 0; i < handler.getSlots(); i++) {
|
||||||
|
ItemStack stack = handler.getStackInSlot(i);
|
||||||
|
if (stack.isEmpty())
|
||||||
|
continue;
|
||||||
|
Item item = stack.getItem();
|
||||||
|
long amount = map.getLong(item);
|
||||||
|
amount -= stack.getCount();
|
||||||
|
if (amount == 0)
|
||||||
|
map.removeLong(item);
|
||||||
|
else map.put(item, amount);
|
||||||
|
}
|
||||||
|
if (!map.isEmpty())
|
||||||
|
fail("Storage missing content: " + map);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert that all the given inventories hold no items.
|
||||||
|
*/
|
||||||
|
public void assertContainersEmpty(List<BlockPos> positions) {
|
||||||
|
for (BlockPos pos : positions) {
|
||||||
|
assertContainerEmpty(pos);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert that the given inventory holds no items.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void assertContainerEmpty(@NotNull BlockPos pos) {
|
||||||
|
IItemHandler storage = itemStorageAt(pos);
|
||||||
|
for (int i = 0; i < storage.getSlots(); i++) {
|
||||||
|
if (!storage.getStackInSlot(i).isEmpty())
|
||||||
|
fail("Storage not empty");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @see CreateGameTestHelper#assertContainerContains(BlockPos, ItemStack) */
|
||||||
|
public void assertContainerContains(BlockPos pos, ItemLike item) {
|
||||||
|
assertContainerContains(pos, item.asItem());
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @see CreateGameTestHelper#assertContainerContains(BlockPos, ItemStack) */
|
||||||
|
@Override
|
||||||
|
public void assertContainerContains(@NotNull BlockPos pos, @NotNull Item item) {
|
||||||
|
assertContainerContains(pos, new ItemStack(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert that the inventory holds at least the given ItemStack. It may also hold more than the stack.
|
||||||
|
*/
|
||||||
|
public void assertContainerContains(BlockPos pos, ItemStack item) {
|
||||||
|
IItemHandler storage = itemStorageAt(pos);
|
||||||
|
ItemStack extracted = ItemHelper.extract(storage, stack -> ItemHandlerHelper.canItemStacksStack(stack, item), item.getCount(), true);
|
||||||
|
if (extracted.isEmpty())
|
||||||
|
fail("item not present: " + item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// time
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fail unless the desired number seconds have passed since test start.
|
||||||
|
*/
|
||||||
|
public void assertSecondsPassed(int seconds) {
|
||||||
|
if (getTick() < (long) seconds * TICKS_PER_SECOND)
|
||||||
|
fail("Waiting for %s seconds to pass".formatted(seconds));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the total number of seconds that have passed since test start.
|
||||||
|
*/
|
||||||
|
public long secondsPassed() {
|
||||||
|
return getTick() % 20;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run an action later, once enough time has passed.
|
||||||
|
*/
|
||||||
|
public void whenSecondsPassed(int seconds, Runnable run) {
|
||||||
|
runAfterDelay((long) seconds * TICKS_PER_SECOND, run);
|
||||||
|
}
|
||||||
|
|
||||||
|
// numbers
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assert that a number is <1 away from its expected value
|
||||||
|
*/
|
||||||
|
public void assertCloseEnoughTo(double value, double expected) {
|
||||||
|
assertInRange(value, expected - 1, expected + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void assertInRange(double value, double min, double max) {
|
||||||
|
if (value < min)
|
||||||
|
fail("Value %s below expected min of %s".formatted(value, min));
|
||||||
|
if (value > max)
|
||||||
|
fail("Value %s greater than expected max of %s".formatted(value, max));
|
||||||
|
}
|
||||||
|
|
||||||
|
// misc
|
||||||
|
|
||||||
|
@Contract("_->fail") // make IDEA happier
|
||||||
|
@Override
|
||||||
|
public void fail(@NotNull String exceptionMessage) {
|
||||||
|
super.fail(exceptionMessage);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,122 @@
|
||||||
|
package com.simibubi.create.gametest.infrastructure;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.gametest.framework.GameTest;
|
||||||
|
import net.minecraft.gametest.framework.GameTestGenerator;
|
||||||
|
import net.minecraft.gametest.framework.GameTestHelper;
|
||||||
|
import net.minecraft.gametest.framework.StructureUtils;
|
||||||
|
import net.minecraft.gametest.framework.TestFunction;
|
||||||
|
import net.minecraft.world.level.block.Rotation;
|
||||||
|
|
||||||
|
import net.minecraft.world.level.block.entity.StructureBlockEntity;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.lang.reflect.Modifier;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Comparator;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An extension to game tests implementing functionality for {@link CreateGameTestHelper} and {@link GameTestGroup}.
|
||||||
|
* To use, create a {@link GameTestGenerator} that provides tests using {@link #getTestsFrom(Class[])}.
|
||||||
|
*/
|
||||||
|
public class CreateTestFunction extends TestFunction {
|
||||||
|
// for structure blocks and /test runthis
|
||||||
|
public static final Map<String, CreateTestFunction> NAMES_TO_FUNCTIONS = new HashMap<>();
|
||||||
|
|
||||||
|
public final String fullName;
|
||||||
|
public final String simpleName;
|
||||||
|
|
||||||
|
protected CreateTestFunction(String fullName, String simpleName, String pBatchName, String pTestName,
|
||||||
|
String pStructureName, Rotation pRotation, int pMaxTicks, long pSetupTicks,
|
||||||
|
boolean pRequired, int pRequiredSuccesses, int pMaxAttempts, Consumer<GameTestHelper> pFunction) {
|
||||||
|
super(pBatchName, pTestName, pStructureName, pRotation, pMaxTicks, pSetupTicks, pRequired, pRequiredSuccesses, pMaxAttempts, pFunction);
|
||||||
|
this.fullName = fullName;
|
||||||
|
this.simpleName = simpleName;
|
||||||
|
NAMES_TO_FUNCTIONS.put(fullName, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getTestName() {
|
||||||
|
return simpleName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get all Create test functions from the given classes. This enables functionality
|
||||||
|
* of {@link CreateGameTestHelper} and {@link GameTestGroup}.
|
||||||
|
*/
|
||||||
|
public static Collection<TestFunction> getTestsFrom(Class<?>... classes) {
|
||||||
|
return Stream.of(classes)
|
||||||
|
.map(Class::getDeclaredMethods)
|
||||||
|
.flatMap(Stream::of)
|
||||||
|
.map(CreateTestFunction::of)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.sorted(Comparator.comparing(TestFunction::getTestName))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public static TestFunction of(Method method) {
|
||||||
|
GameTest gt = method.getAnnotation(GameTest.class);
|
||||||
|
if (gt == null) // skip non-test methods
|
||||||
|
return null;
|
||||||
|
Class<?> owner = method.getDeclaringClass();
|
||||||
|
GameTestGroup group = owner.getAnnotation(GameTestGroup.class);
|
||||||
|
String simpleName = owner.getSimpleName() + '.' + method.getName();
|
||||||
|
validateTestMethod(method, gt, owner, group, simpleName);
|
||||||
|
|
||||||
|
String structure = "%s:gametest/%s/%s".formatted(group.namespace(), group.path(), gt.template());
|
||||||
|
Rotation rotation = StructureUtils.getRotationForRotationSteps(gt.rotationSteps());
|
||||||
|
|
||||||
|
String fullName = owner.getName() + "." + method.getName();
|
||||||
|
return new CreateTestFunction(
|
||||||
|
// use structure for test name since that's what MC fills structure blocks with for some reason
|
||||||
|
fullName, simpleName, gt.batch(), structure, structure, rotation, gt.timeoutTicks(), gt.setupTicks(),
|
||||||
|
gt.required(), gt.requiredSuccesses(), gt.attempts(), asConsumer(method)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void validateTestMethod(Method method, GameTest gt, Class<?> owner, GameTestGroup group, String simpleName) {
|
||||||
|
if (gt.template().isEmpty())
|
||||||
|
throw new IllegalArgumentException(simpleName + " must provide a template structure");
|
||||||
|
|
||||||
|
if (!Modifier.isStatic(method.getModifiers()))
|
||||||
|
throw new IllegalArgumentException(simpleName + " must be static");
|
||||||
|
|
||||||
|
if (method.getReturnType() != void.class)
|
||||||
|
throw new IllegalArgumentException(simpleName + " must return void");
|
||||||
|
|
||||||
|
if (method.getParameterCount() != 1 || method.getParameterTypes()[0] != CreateGameTestHelper.class)
|
||||||
|
throw new IllegalArgumentException(simpleName + " must take 1 parameter of type CreateGameTestHelper");
|
||||||
|
|
||||||
|
if (group == null)
|
||||||
|
throw new IllegalArgumentException(owner.getName() + " must be annotated with @GameTestGroup");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Consumer<GameTestHelper> asConsumer(Method method) {
|
||||||
|
return (helper) -> {
|
||||||
|
try {
|
||||||
|
method.invoke(null, helper);
|
||||||
|
} catch (IllegalAccessException | InvocationTargetException e) {
|
||||||
|
throw new RuntimeException(e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(@NotNull GameTestHelper helper) {
|
||||||
|
// give structure block test info
|
||||||
|
StructureBlockEntity be = (StructureBlockEntity) helper.getBlockEntity(BlockPos.ZERO);
|
||||||
|
be.getTileData().putString("CreateTestFunction", fullName);
|
||||||
|
super.run(CreateGameTestHelper.of(helper));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
package com.simibubi.create.gametest.infrastructure;
|
||||||
|
|
||||||
|
import com.simibubi.create.Create;
|
||||||
|
|
||||||
|
import java.lang.annotation.ElementType;
|
||||||
|
import java.lang.annotation.Retention;
|
||||||
|
import java.lang.annotation.RetentionPolicy;
|
||||||
|
import java.lang.annotation.Target;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Allows for test method declarations to be concise by moving subdirectories and namespaces to the class level.
|
||||||
|
*/
|
||||||
|
@Target(ElementType.TYPE)
|
||||||
|
@Retention(RetentionPolicy.RUNTIME)
|
||||||
|
public @interface GameTestGroup {
|
||||||
|
/**
|
||||||
|
* The subdirectory to search for test structures in.
|
||||||
|
*/
|
||||||
|
String path();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The namespace to search for test structures in.
|
||||||
|
*/
|
||||||
|
String namespace() default Create.ID;
|
||||||
|
}
|
|
@ -0,0 +1,98 @@
|
||||||
|
package com.simibubi.create.gametest.tests;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.simibubi.create.gametest.infrastructure.CreateGameTestHelper;
|
||||||
|
import com.simibubi.create.gametest.infrastructure.GameTestGroup;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2LongMap;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.gametest.framework.GameTest;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.entity.projectile.Arrow;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.item.Items;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
|
import net.minecraftforge.fluids.FluidStack;
|
||||||
|
|
||||||
|
@GameTestGroup(path = "contraptions")
|
||||||
|
public class TestContraptions {
|
||||||
|
@GameTest(template = "arrow_dispenser", timeoutTicks = CreateGameTestHelper.TEN_SECONDS)
|
||||||
|
public static void arrowDispenser(CreateGameTestHelper helper) {
|
||||||
|
BlockPos lever = new BlockPos(2, 3, 1);
|
||||||
|
helper.pullLever(lever);
|
||||||
|
BlockPos pos1 = new BlockPos(0, 5, 0);
|
||||||
|
BlockPos pos2 = new BlockPos(4, 5, 4);
|
||||||
|
helper.succeedWhen(() -> {
|
||||||
|
helper.assertSecondsPassed(7);
|
||||||
|
List<Arrow> arrows = helper.getEntitiesBetween(EntityType.ARROW, pos1, pos2);
|
||||||
|
if (arrows.size() != 4)
|
||||||
|
helper.fail("Expected 4 arrows");
|
||||||
|
helper.powerLever(lever); // disassemble contraption
|
||||||
|
BlockPos dispenser = new BlockPos(2, 5, 2);
|
||||||
|
// there should be 1 left over
|
||||||
|
helper.assertContainerContains(dispenser, Items.ARROW);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "crop_farming", timeoutTicks = CreateGameTestHelper.TEN_SECONDS)
|
||||||
|
public static void cropFarming(CreateGameTestHelper helper) {
|
||||||
|
BlockPos lever = new BlockPos(4, 3, 1);
|
||||||
|
helper.pullLever(lever);
|
||||||
|
BlockPos output = new BlockPos(1, 3, 12);
|
||||||
|
helper.succeedWhen(() -> helper.assertAnyContained(output, Items.WHEAT, Items.POTATO, Items.CARROT));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "mounted_item_extract", timeoutTicks = CreateGameTestHelper.TWENTY_SECONDS)
|
||||||
|
public static void mountedItemExtract(CreateGameTestHelper helper) {
|
||||||
|
BlockPos barrel = new BlockPos(1, 3, 2);
|
||||||
|
Object2LongMap<Item> content = helper.getItemContent(barrel);
|
||||||
|
BlockPos lever = new BlockPos(1, 5, 1);
|
||||||
|
helper.pullLever(lever);
|
||||||
|
BlockPos outputPos = new BlockPos(4, 2, 1);
|
||||||
|
helper.succeedWhen(() -> {
|
||||||
|
helper.assertContentPresent(content, outputPos); // verify all extracted
|
||||||
|
helper.powerLever(lever);
|
||||||
|
helper.assertContainerEmpty(barrel); // verify nothing left
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "mounted_fluid_drain", timeoutTicks = CreateGameTestHelper.TEN_SECONDS)
|
||||||
|
public static void mountedFluidDrain(CreateGameTestHelper helper) {
|
||||||
|
BlockPos tank = new BlockPos(1, 3, 2);
|
||||||
|
FluidStack fluid = helper.getTankContents(tank);
|
||||||
|
if (fluid.isEmpty())
|
||||||
|
helper.fail("Tank empty");
|
||||||
|
BlockPos lever = new BlockPos(1, 5, 1);
|
||||||
|
helper.pullLever(lever);
|
||||||
|
BlockPos output = new BlockPos(4, 2, 1);
|
||||||
|
helper.succeedWhen(() -> {
|
||||||
|
helper.assertFluidPresent(fluid, output); // verify all extracted
|
||||||
|
helper.powerLever(lever); // disassemble contraption
|
||||||
|
helper.assertTankEmpty(tank); // verify nothing left
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "ploughing")
|
||||||
|
public static void ploughing(CreateGameTestHelper helper) {
|
||||||
|
BlockPos dirt = new BlockPos(4, 2, 1);
|
||||||
|
BlockPos lever = new BlockPos(3, 3, 2);
|
||||||
|
helper.pullLever(lever);
|
||||||
|
helper.succeedWhen(() -> helper.assertBlockPresent(Blocks.FARMLAND, dirt));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "redstone_contacts")
|
||||||
|
public static void redstoneContacts(CreateGameTestHelper helper) {
|
||||||
|
BlockPos end = new BlockPos(5, 10, 1);
|
||||||
|
BlockPos lever = new BlockPos(1, 3, 2);
|
||||||
|
helper.pullLever(lever);
|
||||||
|
helper.succeedWhen(() -> helper.assertBlockPresent(Blocks.DIAMOND_BLOCK, end));
|
||||||
|
}
|
||||||
|
|
||||||
|
// FIXME: trains do not enjoy being loaded in structures
|
||||||
|
// https://gist.github.com/TropheusJ/f2d0a7df48360d2e078d0987c115c6ef
|
||||||
|
// @GameTest(template = "train_observer")
|
||||||
|
// public static void trainObserver(CreateGameTestHelper helper) {
|
||||||
|
// helper.fail("NYI");
|
||||||
|
// }
|
||||||
|
}
|
152
src/main/java/com/simibubi/create/gametest/tests/TestFluids.java
Normal file
152
src/main/java/com/simibubi/create/gametest/tests/TestFluids.java
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
package com.simibubi.create.gametest.tests;
|
||||||
|
|
||||||
|
import com.simibubi.create.AllTileEntities;
|
||||||
|
import com.simibubi.create.content.contraptions.fluids.actors.HosePulleyFluidHandler;
|
||||||
|
import com.simibubi.create.content.contraptions.relays.gauge.SpeedGaugeTileEntity;
|
||||||
|
import com.simibubi.create.content.contraptions.relays.gauge.StressGaugeTileEntity;
|
||||||
|
|
||||||
|
import com.simibubi.create.gametest.infrastructure.CreateGameTestHelper;
|
||||||
|
|
||||||
|
import com.simibubi.create.gametest.infrastructure.GameTestGroup;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.gametest.framework.GameTest;
|
||||||
|
import net.minecraft.util.Mth;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
|
import net.minecraft.world.level.block.RedStoneWireBlock;
|
||||||
|
import net.minecraft.world.level.block.state.BlockState;
|
||||||
|
import net.minecraft.world.level.block.state.properties.RedstoneSide;
|
||||||
|
import net.minecraft.world.level.material.Fluids;
|
||||||
|
import net.minecraftforge.fluids.FluidAttributes;
|
||||||
|
import net.minecraftforge.fluids.FluidStack;
|
||||||
|
import net.minecraftforge.fluids.capability.IFluidHandler;
|
||||||
|
import net.minecraftforge.fluids.capability.IFluidHandler.FluidAction;
|
||||||
|
|
||||||
|
@GameTestGroup(path = "fluids")
|
||||||
|
public class TestFluids {
|
||||||
|
@GameTest(template = "hose_pulley_transfer", timeoutTicks = CreateGameTestHelper.TWENTY_SECONDS)
|
||||||
|
public static void hosePulleyTransfer(CreateGameTestHelper helper) {
|
||||||
|
// there was supposed to be redstone here built in, but it kept popping off, so put it there manually
|
||||||
|
BlockPos brokenRedstone = new BlockPos(4, 8, 3);
|
||||||
|
BlockState redstone = Blocks.REDSTONE_WIRE.defaultBlockState()
|
||||||
|
.setValue(RedStoneWireBlock.NORTH, RedstoneSide.NONE)
|
||||||
|
.setValue(RedStoneWireBlock.SOUTH, RedstoneSide.NONE)
|
||||||
|
.setValue(RedStoneWireBlock.EAST, RedstoneSide.UP)
|
||||||
|
.setValue(RedStoneWireBlock.WEST, RedstoneSide.SIDE)
|
||||||
|
.setValue(RedStoneWireBlock.POWER, 14);
|
||||||
|
helper.setBlock(brokenRedstone, redstone);
|
||||||
|
// pump
|
||||||
|
BlockPos lever = new BlockPos(6, 9, 3);
|
||||||
|
helper.pullLever(lever);
|
||||||
|
helper.succeedWhen(() -> {
|
||||||
|
helper.assertSecondsPassed(15);
|
||||||
|
// check filled
|
||||||
|
BlockPos filledLowerCorner = new BlockPos(8, 3, 2);
|
||||||
|
BlockPos filledUpperCorner = new BlockPos(10, 5, 4);
|
||||||
|
BlockPos.betweenClosed(filledLowerCorner, filledUpperCorner)
|
||||||
|
.forEach(pos -> helper.assertBlockPresent(Blocks.WATER, pos));
|
||||||
|
// check emptied
|
||||||
|
BlockPos emptiedLowerCorner = new BlockPos(2, 3, 2);
|
||||||
|
BlockPos emptiedUpperCorner = new BlockPos(4, 5, 4);
|
||||||
|
BlockPos.betweenClosed(emptiedLowerCorner, emptiedUpperCorner)
|
||||||
|
.forEach(pos -> helper.assertBlockPresent(Blocks.AIR, pos));
|
||||||
|
// check nothing left in pulley
|
||||||
|
BlockPos pulleyPos = new BlockPos(8, 7, 4);
|
||||||
|
IFluidHandler storage = helper.fluidStorageAt(pulleyPos);
|
||||||
|
if (storage instanceof HosePulleyFluidHandler hose) {
|
||||||
|
IFluidHandler internalTank = hose.getInternalTank();
|
||||||
|
if (!internalTank.drain(1, FluidAction.SIMULATE).isEmpty())
|
||||||
|
helper.fail("Pulley not empty");
|
||||||
|
} else {
|
||||||
|
helper.fail("Not a pulley");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "in_world_pumping_out")
|
||||||
|
public static void inWorldPumpingOutput(CreateGameTestHelper helper) {
|
||||||
|
BlockPos pumpPos = new BlockPos(3, 2, 2);
|
||||||
|
BlockPos waterPos = pumpPos.west();
|
||||||
|
BlockPos basinPos = pumpPos.east();
|
||||||
|
helper.flipBlock(pumpPos);
|
||||||
|
helper.succeedWhen(() -> {
|
||||||
|
helper.assertBlockPresent(Blocks.WATER, waterPos);
|
||||||
|
helper.assertTankEmpty(basinPos);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "in_world_pumping_in")
|
||||||
|
public static void inWorldPumpingPickup(CreateGameTestHelper helper) {
|
||||||
|
BlockPos pumpPos = new BlockPos(3, 2, 2);
|
||||||
|
BlockPos basinPos = pumpPos.east();
|
||||||
|
BlockPos waterPos = pumpPos.west();
|
||||||
|
FluidStack expectedResult = new FluidStack(Fluids.WATER, FluidAttributes.BUCKET_VOLUME);
|
||||||
|
helper.flipBlock(pumpPos);
|
||||||
|
helper.succeedWhen(() -> {
|
||||||
|
helper.assertBlockPresent(Blocks.AIR, waterPos);
|
||||||
|
helper.assertFluidPresent(expectedResult, basinPos);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "steam_engine")
|
||||||
|
public static void steamEngine(CreateGameTestHelper helper) {
|
||||||
|
BlockPos lever = new BlockPos(4, 3, 3);
|
||||||
|
helper.pullLever(lever);
|
||||||
|
BlockPos stressometer = new BlockPos(5, 2, 5);
|
||||||
|
BlockPos speedometer = new BlockPos(4, 2, 5);
|
||||||
|
helper.succeedWhen(() -> {
|
||||||
|
StressGaugeTileEntity stress = helper.getBlockEntity(AllTileEntities.STRESSOMETER.get(), stressometer);
|
||||||
|
SpeedGaugeTileEntity speed = helper.getBlockEntity(AllTileEntities.SPEEDOMETER.get(), speedometer);
|
||||||
|
float capacity = stress.getNetworkCapacity();
|
||||||
|
helper.assertCloseEnoughTo(capacity, 2048);
|
||||||
|
float rotationSpeed = Mth.abs(speed.getSpeed());
|
||||||
|
helper.assertCloseEnoughTo(rotationSpeed, 16);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "3_pipe_combine", timeoutTicks = CreateGameTestHelper.TWENTY_SECONDS)
|
||||||
|
public static void threePipeCombine(CreateGameTestHelper helper) {
|
||||||
|
BlockPos tank1Pos = new BlockPos(5, 2, 1);
|
||||||
|
BlockPos tank2Pos = tank1Pos.south();
|
||||||
|
BlockPos tank3Pos = tank2Pos.south();
|
||||||
|
long initialContents = helper.getFluidInTanks(tank1Pos, tank2Pos, tank3Pos);
|
||||||
|
|
||||||
|
BlockPos pumpPos = new BlockPos(2, 2, 2);
|
||||||
|
helper.flipBlock(pumpPos);
|
||||||
|
helper.succeedWhen(() -> {
|
||||||
|
helper.assertSecondsPassed(13);
|
||||||
|
// make sure fully drained
|
||||||
|
helper.assertTanksEmpty(tank1Pos, tank2Pos, tank3Pos);
|
||||||
|
// and fully moved
|
||||||
|
BlockPos outputTankPos = new BlockPos(1, 2, 2);
|
||||||
|
long moved = helper.getFluidInTanks(outputTankPos);
|
||||||
|
if (moved != initialContents)
|
||||||
|
helper.fail("Wrong amount of fluid amount. expected [%s], got [%s]".formatted(initialContents, moved));
|
||||||
|
// verify nothing was duped or deleted
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "3_pipe_split", timeoutTicks = CreateGameTestHelper.TEN_SECONDS)
|
||||||
|
public static void threePipeSplit(CreateGameTestHelper helper) {
|
||||||
|
BlockPos pumpPos = new BlockPos(2, 2, 2);
|
||||||
|
BlockPos tank1Pos = new BlockPos(5, 2, 1);
|
||||||
|
BlockPos tank2Pos = tank1Pos.south();
|
||||||
|
BlockPos tank3Pos = tank2Pos.south();
|
||||||
|
BlockPos outputTankPos = new BlockPos(1, 2, 2);
|
||||||
|
|
||||||
|
long totalContents = helper.getFluidInTanks(tank1Pos, tank2Pos, tank3Pos, outputTankPos);
|
||||||
|
helper.flipBlock(pumpPos);
|
||||||
|
|
||||||
|
helper.succeedWhen(() -> {
|
||||||
|
helper.assertSecondsPassed(7);
|
||||||
|
FluidStack contents = helper.getTankContents(outputTankPos);
|
||||||
|
if (!contents.isEmpty()) {
|
||||||
|
helper.fail("Tank not empty: " + contents.getAmount());
|
||||||
|
}
|
||||||
|
long newTotalContents = helper.getFluidInTanks(tank1Pos, tank2Pos, tank3Pos);
|
||||||
|
if (newTotalContents != totalContents) {
|
||||||
|
helper.fail("Wrong total fluid amount. expected [%s], got [%s]".formatted(totalContents, newTotalContents));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
331
src/main/java/com/simibubi/create/gametest/tests/TestItems.java
Normal file
331
src/main/java/com/simibubi/create/gametest/tests/TestItems.java
Normal file
|
@ -0,0 +1,331 @@
|
||||||
|
package com.simibubi.create.gametest.tests;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Locale;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import com.simibubi.create.AllBlocks;
|
||||||
|
import com.simibubi.create.AllItems;
|
||||||
|
import com.simibubi.create.AllTileEntities;
|
||||||
|
import com.simibubi.create.content.logistics.block.belts.tunnel.BrassTunnelTileEntity.SelectionMode;
|
||||||
|
import com.simibubi.create.content.logistics.block.depot.DepotTileEntity;
|
||||||
|
import com.simibubi.create.content.logistics.block.redstone.NixieTubeTileEntity;
|
||||||
|
import com.simibubi.create.content.logistics.trains.management.display.FlapDisplayLayout;
|
||||||
|
import com.simibubi.create.content.logistics.trains.management.display.FlapDisplaySection;
|
||||||
|
import com.simibubi.create.content.logistics.trains.management.display.FlapDisplayTileEntity;
|
||||||
|
import com.simibubi.create.gametest.infrastructure.CreateGameTestHelper;
|
||||||
|
import com.simibubi.create.gametest.infrastructure.GameTestGroup;
|
||||||
|
import com.simibubi.create.foundation.utility.Components;
|
||||||
|
|
||||||
|
import it.unimi.dsi.fastutil.objects.Object2LongMap;
|
||||||
|
import net.minecraft.Util;
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.core.Registry;
|
||||||
|
import net.minecraft.gametest.framework.GameTest;
|
||||||
|
import net.minecraft.network.chat.MutableComponent;
|
||||||
|
import net.minecraft.world.item.EnchantedBookItem;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.Items;
|
||||||
|
import net.minecraft.world.item.enchantment.EnchantmentInstance;
|
||||||
|
import net.minecraft.world.item.enchantment.Enchantments;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
|
import net.minecraft.world.level.block.RedstoneLampBlock;
|
||||||
|
import net.minecraftforge.items.IItemHandler;
|
||||||
|
import net.minecraftforge.items.ItemHandlerHelper;
|
||||||
|
|
||||||
|
@GameTestGroup(path = "items")
|
||||||
|
public class TestItems {
|
||||||
|
@GameTest(template = "andesite_tunnel_split")
|
||||||
|
public static void andesiteTunnelSplit(CreateGameTestHelper helper) {
|
||||||
|
BlockPos lever = new BlockPos(2, 6, 2);
|
||||||
|
helper.pullLever(lever);
|
||||||
|
Map<BlockPos, ItemStack> outputs = Map.of(
|
||||||
|
new BlockPos(2, 2, 1), new ItemStack(AllItems.BRASS_INGOT.get(), 1),
|
||||||
|
new BlockPos(3, 2, 1), new ItemStack(AllItems.BRASS_INGOT.get(), 1),
|
||||||
|
new BlockPos(4, 2, 2), new ItemStack(AllItems.BRASS_INGOT.get(), 3)
|
||||||
|
);
|
||||||
|
helper.succeedWhen(() -> outputs.forEach(helper::assertContainerContains));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "arm_purgatory", timeoutTicks = CreateGameTestHelper.TEN_SECONDS)
|
||||||
|
public static void armPurgatory(CreateGameTestHelper helper) {
|
||||||
|
BlockPos lever = new BlockPos(2, 3, 2);
|
||||||
|
BlockPos depot1Pos = new BlockPos(3, 2, 1);
|
||||||
|
DepotTileEntity depot1 = helper.getBlockEntity(AllTileEntities.DEPOT.get(), depot1Pos);
|
||||||
|
BlockPos depot2Pos = new BlockPos(1, 2, 1);
|
||||||
|
DepotTileEntity depot2 = helper.getBlockEntity(AllTileEntities.DEPOT.get(), depot2Pos);
|
||||||
|
helper.pullLever(lever);
|
||||||
|
helper.succeedWhen(() -> {
|
||||||
|
helper.assertSecondsPassed(5);
|
||||||
|
ItemStack held1 = depot1.getHeldItem();
|
||||||
|
boolean held1Empty = held1.isEmpty();
|
||||||
|
int held1Count = held1.getCount();
|
||||||
|
ItemStack held2 = depot2.getHeldItem();
|
||||||
|
boolean held2Empty = held2.isEmpty();
|
||||||
|
int held2Count = held2.getCount();
|
||||||
|
if (held1Empty && held2Empty)
|
||||||
|
helper.fail("No item present");
|
||||||
|
if (!held1Empty && held1Count != 1)
|
||||||
|
helper.fail("Unexpected count on depot 1: " + held1Count);
|
||||||
|
if (!held2Empty && held2Count != 1)
|
||||||
|
helper.fail("Unexpected count on depot 2: " + held2Count);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "attribute_filters", timeoutTicks = CreateGameTestHelper.TEN_SECONDS)
|
||||||
|
public static void attributeFilters(CreateGameTestHelper helper) {
|
||||||
|
BlockPos lever = new BlockPos(2, 3, 1);
|
||||||
|
BlockPos end = new BlockPos(11, 2, 2);
|
||||||
|
Map<BlockPos, ItemStack> outputs = Map.of(
|
||||||
|
new BlockPos(3, 2, 1), new ItemStack(AllBlocks.BRASS_BLOCK.get()),
|
||||||
|
new BlockPos(4, 2, 1), new ItemStack(Items.APPLE),
|
||||||
|
new BlockPos(5, 2, 1), new ItemStack(Items.WATER_BUCKET),
|
||||||
|
new BlockPos(6, 2, 1), EnchantedBookItem.createForEnchantment(
|
||||||
|
new EnchantmentInstance(Enchantments.ALL_DAMAGE_PROTECTION, 1)
|
||||||
|
),
|
||||||
|
new BlockPos(7, 2, 1), Util.make(
|
||||||
|
new ItemStack(Items.NETHERITE_SWORD),
|
||||||
|
s -> s.setDamageValue(1)
|
||||||
|
),
|
||||||
|
new BlockPos(8, 2, 1), new ItemStack(Items.IRON_HELMET),
|
||||||
|
new BlockPos(9, 2, 1), new ItemStack(Items.COAL),
|
||||||
|
new BlockPos(10, 2, 1), new ItemStack(Items.POTATO)
|
||||||
|
);
|
||||||
|
helper.pullLever(lever);
|
||||||
|
helper.succeedWhen(() -> {
|
||||||
|
outputs.forEach(helper::assertContainerContains);
|
||||||
|
helper.assertContainerEmpty(end);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "belt_coaster", timeoutTicks = CreateGameTestHelper.TEN_SECONDS)
|
||||||
|
public static void beltCoaster(CreateGameTestHelper helper) {
|
||||||
|
BlockPos input = new BlockPos(1, 5, 6);
|
||||||
|
BlockPos output = new BlockPos(3, 8, 6);
|
||||||
|
BlockPos lever = new BlockPos(1, 5, 5);
|
||||||
|
helper.pullLever(lever);
|
||||||
|
helper.succeedWhen(() -> {
|
||||||
|
long outputItems = helper.getTotalItems(output);
|
||||||
|
if (outputItems != 27)
|
||||||
|
helper.fail("Expected 27 items, got " + outputItems);
|
||||||
|
long remainingItems = helper.getTotalItems(input);
|
||||||
|
if (remainingItems != 2)
|
||||||
|
helper.fail("Expected 2 items remaining, got " + remainingItems);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "brass_tunnel_filtering")
|
||||||
|
public static void brassTunnelFiltering(CreateGameTestHelper helper) {
|
||||||
|
Map<BlockPos, ItemStack> outputs = Map.of(
|
||||||
|
new BlockPos(3, 2, 2), new ItemStack(Items.COPPER_INGOT, 13),
|
||||||
|
new BlockPos(4, 2, 3), new ItemStack(AllItems.ZINC_INGOT.get(), 4),
|
||||||
|
new BlockPos(4, 2, 4), new ItemStack(Items.IRON_INGOT, 2),
|
||||||
|
new BlockPos(4, 2, 5), new ItemStack(Items.GOLD_INGOT, 24),
|
||||||
|
new BlockPos(3, 2, 6), new ItemStack(Items.DIAMOND, 17)
|
||||||
|
);
|
||||||
|
BlockPos lever = new BlockPos(2, 3, 2);
|
||||||
|
helper.pullLever(lever);
|
||||||
|
helper.succeedWhen(() -> outputs.forEach(helper::assertContainerContains));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "brass_tunnel_prefer_nearest", timeoutTicks = CreateGameTestHelper.TEN_SECONDS)
|
||||||
|
public static void brassTunnelPreferNearest(CreateGameTestHelper helper) {
|
||||||
|
List<BlockPos> tunnels = List.of(
|
||||||
|
new BlockPos(3, 3, 1),
|
||||||
|
new BlockPos(3, 3, 2),
|
||||||
|
new BlockPos(3, 3, 3)
|
||||||
|
);
|
||||||
|
List<BlockPos> out = List.of(
|
||||||
|
new BlockPos(5, 2, 1),
|
||||||
|
new BlockPos(5, 2, 2),
|
||||||
|
new BlockPos(5, 2, 3)
|
||||||
|
);
|
||||||
|
BlockPos lever = new BlockPos(2, 3, 2);
|
||||||
|
helper.pullLever(lever);
|
||||||
|
// tunnels reconnect and lose their modes
|
||||||
|
tunnels.forEach(tunnel -> helper.setTunnelMode(tunnel, SelectionMode.PREFER_NEAREST));
|
||||||
|
helper.succeedWhen(() ->
|
||||||
|
out.forEach(pos ->
|
||||||
|
helper.assertContainerContains(pos, AllBlocks.BRASS_CASING.get())
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "brass_tunnel_round_robin", timeoutTicks = CreateGameTestHelper.TEN_SECONDS)
|
||||||
|
public static void brassTunnelRoundRobin(CreateGameTestHelper helper) {
|
||||||
|
List<BlockPos> outputs = List.of(
|
||||||
|
new BlockPos(7, 3, 1),
|
||||||
|
new BlockPos(7, 3, 2),
|
||||||
|
new BlockPos(7, 3, 3)
|
||||||
|
);
|
||||||
|
brassTunnelModeTest(helper, SelectionMode.ROUND_ROBIN, outputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "brass_tunnel_split")
|
||||||
|
public static void brassTunnelSplit(CreateGameTestHelper helper) {
|
||||||
|
List<BlockPos> outputs = List.of(
|
||||||
|
new BlockPos(7, 2, 1),
|
||||||
|
new BlockPos(7, 2, 2),
|
||||||
|
new BlockPos(7, 2, 3)
|
||||||
|
);
|
||||||
|
brassTunnelModeTest(helper, SelectionMode.SPLIT, outputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void brassTunnelModeTest(CreateGameTestHelper helper, SelectionMode mode, List<BlockPos> outputs) {
|
||||||
|
BlockPos lever = new BlockPos(2, 3, 2);
|
||||||
|
List<BlockPos> tunnels = List.of(
|
||||||
|
new BlockPos(3, 3, 1),
|
||||||
|
new BlockPos(3, 3, 2),
|
||||||
|
new BlockPos(3, 3, 3)
|
||||||
|
);
|
||||||
|
helper.pullLever(lever);
|
||||||
|
tunnels.forEach(tunnel -> helper.setTunnelMode(tunnel, mode));
|
||||||
|
helper.succeedWhen(() -> {
|
||||||
|
long items = 0;
|
||||||
|
for (BlockPos out : outputs) {
|
||||||
|
helper.assertContainerContains(out, AllBlocks.BRASS_CASING.get());
|
||||||
|
items += helper.getTotalItems(out);
|
||||||
|
}
|
||||||
|
if (items != 10)
|
||||||
|
helper.fail("expected 10 items, got " + items);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "brass_tunnel_sync_input", timeoutTicks = CreateGameTestHelper.TEN_SECONDS)
|
||||||
|
public static void brassTunnelSyncInput(CreateGameTestHelper helper) {
|
||||||
|
BlockPos lever = new BlockPos(1, 3, 2);
|
||||||
|
List<BlockPos> redstoneBlocks = List.of(
|
||||||
|
new BlockPos(3, 4, 1),
|
||||||
|
new BlockPos(3, 4, 2),
|
||||||
|
new BlockPos(3, 4, 3)
|
||||||
|
);
|
||||||
|
List<BlockPos> tunnels = List.of(
|
||||||
|
new BlockPos(5, 3, 1),
|
||||||
|
new BlockPos(5, 3, 2),
|
||||||
|
new BlockPos(5, 3, 3)
|
||||||
|
);
|
||||||
|
List<BlockPos> outputs = List.of(
|
||||||
|
new BlockPos(7, 2, 1),
|
||||||
|
new BlockPos(7, 2, 2),
|
||||||
|
new BlockPos(7, 2, 3)
|
||||||
|
);
|
||||||
|
helper.pullLever(lever);
|
||||||
|
tunnels.forEach(tunnel -> helper.setTunnelMode(tunnel, SelectionMode.SYNCHRONIZE));
|
||||||
|
helper.succeedWhen(() -> {
|
||||||
|
if (helper.secondsPassed() < 9) {
|
||||||
|
helper.setBlock(redstoneBlocks.get(0), Blocks.AIR);
|
||||||
|
helper.assertSecondsPassed(3);
|
||||||
|
outputs.forEach(helper::assertContainerEmpty);
|
||||||
|
helper.setBlock(redstoneBlocks.get(1), Blocks.AIR);
|
||||||
|
helper.assertSecondsPassed(6);
|
||||||
|
outputs.forEach(helper::assertContainerEmpty);
|
||||||
|
helper.setBlock(redstoneBlocks.get(2), Blocks.AIR);
|
||||||
|
helper.assertSecondsPassed(9);
|
||||||
|
} else {
|
||||||
|
outputs.forEach(out -> helper.assertContainerContains(out, AllBlocks.BRASS_CASING.get()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "content_observer_counting")
|
||||||
|
public static void contentObserverCounting(CreateGameTestHelper helper) {
|
||||||
|
BlockPos chest = new BlockPos(3, 2, 1);
|
||||||
|
long totalChestItems = helper.getTotalItems(chest);
|
||||||
|
BlockPos chestNixiePos = new BlockPos(2, 3, 1);
|
||||||
|
NixieTubeTileEntity chestNixie = helper.getBlockEntity(AllTileEntities.NIXIE_TUBE.get(), chestNixiePos);
|
||||||
|
|
||||||
|
BlockPos doubleChest = new BlockPos(2, 2, 3);
|
||||||
|
long totalDoubleChestItems = helper.getTotalItems(doubleChest);
|
||||||
|
BlockPos doubleChestNixiePos = new BlockPos(1, 3, 3);
|
||||||
|
NixieTubeTileEntity doubleChestNixie = helper.getBlockEntity(AllTileEntities.NIXIE_TUBE.get(), doubleChestNixiePos);
|
||||||
|
|
||||||
|
helper.succeedWhen(() -> {
|
||||||
|
String chestNixieText = chestNixie.getFullText().getString();
|
||||||
|
long chestNixieReading = Long.parseLong(chestNixieText);
|
||||||
|
if (chestNixieReading != totalChestItems)
|
||||||
|
helper.fail("Chest nixie detected %s, expected %s".formatted(chestNixieReading, totalChestItems));
|
||||||
|
String doubleChestNixieText = doubleChestNixie.getFullText().getString();
|
||||||
|
long doubleChestNixieReading = Long.parseLong(doubleChestNixieText);
|
||||||
|
if (doubleChestNixieReading != totalDoubleChestItems)
|
||||||
|
helper.fail("Double chest nixie detected %s, expected %s".formatted(doubleChestNixieReading, totalDoubleChestItems));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "depot_display", timeoutTicks = CreateGameTestHelper.TEN_SECONDS)
|
||||||
|
public static void depotDisplay(CreateGameTestHelper helper) {
|
||||||
|
BlockPos displayPos = new BlockPos(5, 3, 1);
|
||||||
|
List<DepotTileEntity> depots = Stream.of(
|
||||||
|
new BlockPos(2, 2, 1),
|
||||||
|
new BlockPos(1, 2, 1)
|
||||||
|
).map(pos -> helper.getBlockEntity(AllTileEntities.DEPOT.get(), pos)).toList();
|
||||||
|
List<BlockPos> levers = List.of(
|
||||||
|
new BlockPos(2, 5, 0),
|
||||||
|
new BlockPos(1, 5, 0)
|
||||||
|
);
|
||||||
|
levers.forEach(helper::pullLever);
|
||||||
|
FlapDisplayTileEntity display = helper.getBlockEntity(AllTileEntities.FLAP_DISPLAY.get(), displayPos).getController();
|
||||||
|
helper.succeedWhen(() -> {
|
||||||
|
for (int i = 0; i < 2; i++) {
|
||||||
|
FlapDisplayLayout line = display.getLines().get(i);
|
||||||
|
MutableComponent textComponent = Components.empty();
|
||||||
|
line.getSections().stream().map(FlapDisplaySection::getText).forEach(textComponent::append);
|
||||||
|
String text = textComponent.getString().toLowerCase(Locale.ROOT).trim();
|
||||||
|
|
||||||
|
DepotTileEntity depot = depots.get(i);
|
||||||
|
ItemStack item = depot.getHeldItem();
|
||||||
|
String name = Registry.ITEM.getKey(item.getItem()).getPath();
|
||||||
|
|
||||||
|
if (!name.equals(text))
|
||||||
|
helper.fail("Text mismatch: wanted [" + name + "], got: " + text);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "stockpile_switch")
|
||||||
|
public static void stockpileSwitch(CreateGameTestHelper helper) {
|
||||||
|
BlockPos chest = new BlockPos(1, 2, 1);
|
||||||
|
BlockPos lamp = new BlockPos(2, 3, 1);
|
||||||
|
helper.assertBlockProperty(lamp, RedstoneLampBlock.LIT, false);
|
||||||
|
IItemHandler chestStorage = helper.itemStorageAt(chest);
|
||||||
|
for (int i = 0; i < 18; i++) { // insert 18 stacks
|
||||||
|
ItemHandlerHelper.insertItem(chestStorage, new ItemStack(Items.DIAMOND, 64), false);
|
||||||
|
}
|
||||||
|
helper.succeedWhen(() -> helper.assertBlockProperty(lamp, RedstoneLampBlock.LIT, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "storages", timeoutTicks = CreateGameTestHelper.TEN_SECONDS)
|
||||||
|
public static void storages(CreateGameTestHelper helper) {
|
||||||
|
BlockPos lever = new BlockPos(12, 3, 2);
|
||||||
|
BlockPos startChest = new BlockPos(13, 3, 1);
|
||||||
|
Object2LongMap<Item> originalContent = helper.getItemContent(startChest);
|
||||||
|
BlockPos endShulker = new BlockPos(1, 3, 1);
|
||||||
|
helper.pullLever(lever);
|
||||||
|
helper.succeedWhen(() -> helper.assertContentPresent(originalContent, endShulker));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "vault_comparator_output")
|
||||||
|
public static void vaultComparatorOutput(CreateGameTestHelper helper) {
|
||||||
|
BlockPos smallInput = new BlockPos(1, 4, 1);
|
||||||
|
BlockPos smallNixie = new BlockPos(3, 2, 1);
|
||||||
|
helper.assertNixiePower(smallNixie, 0);
|
||||||
|
helper.whenSecondsPassed(1, () -> helper.spawnItems(smallInput, Items.BREAD, 64 * 9));
|
||||||
|
|
||||||
|
BlockPos medInput = new BlockPos(1, 5, 4);
|
||||||
|
BlockPos medNixie = new BlockPos(4, 2, 4);
|
||||||
|
helper.assertNixiePower(medNixie, 0);
|
||||||
|
helper.whenSecondsPassed(2, () -> helper.spawnItems(medInput, Items.BREAD, 64 * 77));
|
||||||
|
|
||||||
|
BlockPos bigInput = new BlockPos(1, 6, 8);
|
||||||
|
BlockPos bigNixie = new BlockPos(5, 2, 7);
|
||||||
|
helper.assertNixiePower(bigNixie, 0);
|
||||||
|
helper.whenSecondsPassed(3, () -> helper.spawnItems(bigInput, Items.BREAD, 64 * 240));
|
||||||
|
|
||||||
|
helper.succeedWhen(() -> {
|
||||||
|
helper.assertNixiePower(smallNixie, 7);
|
||||||
|
helper.assertNixiePower(medNixie, 7);
|
||||||
|
helper.assertNixiePower(bigNixie, 7);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,67 @@
|
||||||
|
package com.simibubi.create.gametest.tests;
|
||||||
|
|
||||||
|
import com.simibubi.create.AllTileEntities;
|
||||||
|
import com.simibubi.create.content.schematics.SchematicExport;
|
||||||
|
import com.simibubi.create.content.schematics.block.SchematicannonTileEntity;
|
||||||
|
import com.simibubi.create.content.schematics.block.SchematicannonTileEntity.State;
|
||||||
|
import com.simibubi.create.content.schematics.item.SchematicItem;
|
||||||
|
|
||||||
|
import com.simibubi.create.gametest.infrastructure.CreateGameTestHelper;
|
||||||
|
import com.simibubi.create.gametest.infrastructure.GameTestGroup;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.gametest.framework.GameTest;
|
||||||
|
import net.minecraft.nbt.NbtUtils;
|
||||||
|
import net.minecraft.server.level.ServerLevel;
|
||||||
|
import net.minecraft.sounds.SoundSource;
|
||||||
|
import net.minecraft.world.entity.EntityType;
|
||||||
|
import net.minecraft.world.entity.animal.Sheep;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.Items;
|
||||||
|
import net.minecraft.world.level.block.Blocks;
|
||||||
|
|
||||||
|
import static com.simibubi.create.gametest.infrastructure.CreateGameTestHelper.FIFTEEN_SECONDS;
|
||||||
|
|
||||||
|
@GameTestGroup(path = "misc")
|
||||||
|
public class TestMisc {
|
||||||
|
@GameTest(template = "schematicannon", timeoutTicks = FIFTEEN_SECONDS)
|
||||||
|
public static void schematicannon(CreateGameTestHelper helper) {
|
||||||
|
// load the structure
|
||||||
|
BlockPos whiteEndBottom = helper.absolutePos(new BlockPos(5, 2, 1));
|
||||||
|
BlockPos redEndTop = helper.absolutePos(new BlockPos(5, 4, 7));
|
||||||
|
ServerLevel level = helper.getLevel();
|
||||||
|
SchematicExport.saveSchematic(
|
||||||
|
SchematicExport.SCHEMATICS.resolve("uploaded/Deployer"), "schematicannon_gametest", true,
|
||||||
|
level, whiteEndBottom, redEndTop
|
||||||
|
);
|
||||||
|
ItemStack schematic = SchematicItem.create("schematicannon_gametest.nbt", "Deployer");
|
||||||
|
// deploy to pos
|
||||||
|
BlockPos anchor = helper.absolutePos(new BlockPos(1, 2, 1));
|
||||||
|
schematic.getOrCreateTag().putBoolean("Deployed", true);
|
||||||
|
schematic.getOrCreateTag().put("Anchor", NbtUtils.writeBlockPos(anchor));
|
||||||
|
// setup cannon
|
||||||
|
BlockPos cannonPos = new BlockPos(3, 2, 6);
|
||||||
|
SchematicannonTileEntity cannon = helper.getBlockEntity(AllTileEntities.SCHEMATICANNON.get(), cannonPos);
|
||||||
|
cannon.inventory.setStackInSlot(0, schematic);
|
||||||
|
// run
|
||||||
|
cannon.state = State.RUNNING;
|
||||||
|
cannon.statusMsg = "running";
|
||||||
|
helper.succeedWhen(() -> {
|
||||||
|
if (cannon.state != State.STOPPED) {
|
||||||
|
helper.fail("Schematicannon not done");
|
||||||
|
}
|
||||||
|
BlockPos lastBlock = new BlockPos(1, 4, 7);
|
||||||
|
helper.assertBlockPresent(Blocks.RED_WOOL, lastBlock);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "shearing")
|
||||||
|
public static void shearing(CreateGameTestHelper helper) {
|
||||||
|
BlockPos sheepPos = new BlockPos(2, 1, 2);
|
||||||
|
Sheep sheep = helper.getFirstEntity(EntityType.SHEEP, sheepPos);
|
||||||
|
sheep.shear(SoundSource.NEUTRAL);
|
||||||
|
helper.succeedWhen(() -> {
|
||||||
|
helper.assertItemEntityPresent(Items.WHITE_WOOL, sheepPos, 2);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,129 @@
|
||||||
|
package com.simibubi.create.gametest.tests;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.simibubi.create.AllBlocks;
|
||||||
|
import com.simibubi.create.AllItems;
|
||||||
|
|
||||||
|
import com.simibubi.create.Create;
|
||||||
|
import com.simibubi.create.content.contraptions.itemAssembly.SequencedAssemblyRecipe;
|
||||||
|
import com.simibubi.create.content.contraptions.processing.ProcessingOutput;
|
||||||
|
import com.simibubi.create.gametest.infrastructure.CreateGameTestHelper;
|
||||||
|
import com.simibubi.create.gametest.infrastructure.GameTestGroup;
|
||||||
|
import com.simibubi.create.foundation.item.ItemHelper;
|
||||||
|
|
||||||
|
import net.minecraft.core.BlockPos;
|
||||||
|
import net.minecraft.gametest.framework.GameTest;
|
||||||
|
import net.minecraft.gametest.framework.GameTestAssertException;
|
||||||
|
import net.minecraft.world.item.Item;
|
||||||
|
import net.minecraft.world.item.ItemStack;
|
||||||
|
import net.minecraft.world.item.Items;
|
||||||
|
import net.minecraft.world.item.alchemy.PotionUtils;
|
||||||
|
import net.minecraft.world.item.alchemy.Potions;
|
||||||
|
import net.minecraftforge.items.IItemHandler;
|
||||||
|
|
||||||
|
@GameTestGroup(path = "processing")
|
||||||
|
public class TestProcessing {
|
||||||
|
@GameTest(template = "brass_mixing", timeoutTicks = CreateGameTestHelper.TEN_SECONDS)
|
||||||
|
public static void brassMixing(CreateGameTestHelper helper) {
|
||||||
|
BlockPos lever = new BlockPos(2, 3, 2);
|
||||||
|
BlockPos chest = new BlockPos(7, 3, 1);
|
||||||
|
helper.pullLever(lever);
|
||||||
|
helper.succeedWhen(() -> helper.assertContainerContains(chest, AllItems.BRASS_INGOT.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "brass_mixing_2", timeoutTicks = CreateGameTestHelper.TWENTY_SECONDS)
|
||||||
|
public static void brassMixing2(CreateGameTestHelper helper) {
|
||||||
|
BlockPos basinLever = new BlockPos(3, 3, 1);
|
||||||
|
BlockPos armLever = new BlockPos(3, 3, 5);
|
||||||
|
BlockPos output = new BlockPos(1, 2, 3);
|
||||||
|
helper.pullLever(armLever);
|
||||||
|
helper.whenSecondsPassed(7, () -> helper.pullLever(armLever));
|
||||||
|
helper.whenSecondsPassed(10, () -> helper.pullLever(basinLever));
|
||||||
|
helper.succeedWhen(() -> helper.assertContainerContains(output, AllItems.BRASS_INGOT.get()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "crushing_wheel_crafting", timeoutTicks = CreateGameTestHelper.TEN_SECONDS)
|
||||||
|
public static void crushingWheelCrafting(CreateGameTestHelper helper) {
|
||||||
|
BlockPos chest = new BlockPos(1, 4, 3);
|
||||||
|
List<BlockPos> levers = List.of(
|
||||||
|
new BlockPos(2, 3, 2),
|
||||||
|
new BlockPos(6, 3, 2),
|
||||||
|
new BlockPos(3, 7, 3)
|
||||||
|
);
|
||||||
|
levers.forEach(helper::pullLever);
|
||||||
|
ItemStack expected = new ItemStack(AllBlocks.CRUSHING_WHEEL.get(), 2);
|
||||||
|
helper.succeedWhen(() -> helper.assertContainerContains(chest, expected));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "precision_mechanism_crafting", timeoutTicks = CreateGameTestHelper.TWENTY_SECONDS)
|
||||||
|
public static void precisionMechanismCrafting(CreateGameTestHelper helper) {
|
||||||
|
BlockPos lever = new BlockPos(6, 3, 6);
|
||||||
|
BlockPos output = new BlockPos(11, 3, 1);
|
||||||
|
helper.pullLever(lever);
|
||||||
|
|
||||||
|
SequencedAssemblyRecipe recipe = (SequencedAssemblyRecipe) helper.getLevel().getRecipeManager()
|
||||||
|
.byKey(Create.asResource("sequenced_assembly/precision_mechanism"))
|
||||||
|
.orElseThrow(() -> new GameTestAssertException("Precision Mechanism recipe not found"));
|
||||||
|
Item result = recipe.getResultItem().getItem();
|
||||||
|
Item[] possibleResults = recipe.resultPool.stream()
|
||||||
|
.map(ProcessingOutput::getStack)
|
||||||
|
.map(ItemStack::getItem)
|
||||||
|
.filter(item -> item != result)
|
||||||
|
.toArray(Item[]::new);
|
||||||
|
|
||||||
|
helper.succeedWhen(() -> {
|
||||||
|
helper.assertContainerContains(output, result);
|
||||||
|
helper.assertAnyContained(output, possibleResults);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "sand_washing", timeoutTicks = CreateGameTestHelper.TEN_SECONDS)
|
||||||
|
public static void sandWashing(CreateGameTestHelper helper) {
|
||||||
|
BlockPos leverPos = new BlockPos(5, 3, 1);
|
||||||
|
helper.pullLever(leverPos);
|
||||||
|
BlockPos chestPos = new BlockPos(8, 3, 2);
|
||||||
|
helper.succeedWhen(() -> helper.assertContainerContains(chestPos, Items.CLAY_BALL));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "stone_cobble_sand_crushing", timeoutTicks = CreateGameTestHelper.TEN_SECONDS)
|
||||||
|
public static void stoneCobbleSandCrushing(CreateGameTestHelper helper) {
|
||||||
|
BlockPos chest = new BlockPos(1, 6, 2);
|
||||||
|
BlockPos lever = new BlockPos(2, 3, 1);
|
||||||
|
helper.pullLever(lever);
|
||||||
|
ItemStack expected = new ItemStack(Items.SAND, 5);
|
||||||
|
helper.succeedWhen(() -> helper.assertContainerContains(chest, expected));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "track_crafting", timeoutTicks = CreateGameTestHelper.TEN_SECONDS)
|
||||||
|
public static void trackCrafting(CreateGameTestHelper helper) {
|
||||||
|
BlockPos output = new BlockPos(7, 3, 2);
|
||||||
|
BlockPos lever = new BlockPos(2, 3, 1);
|
||||||
|
helper.pullLever(lever);
|
||||||
|
ItemStack expected = new ItemStack(AllBlocks.TRACK.get(), 6);
|
||||||
|
helper.succeedWhen(() -> {
|
||||||
|
helper.assertContainerContains(output, expected);
|
||||||
|
IItemHandler handler = helper.itemStorageAt(output);
|
||||||
|
ItemHelper.extract(handler, stack -> stack.sameItem(expected), 6, false);
|
||||||
|
helper.assertContainerEmpty(output);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "water_filling_bottle")
|
||||||
|
public static void waterFillingBottle(CreateGameTestHelper helper) {
|
||||||
|
BlockPos lever = new BlockPos(3, 3, 3);
|
||||||
|
BlockPos output = new BlockPos(2, 2, 4);
|
||||||
|
ItemStack expected = PotionUtils.setPotion(new ItemStack(Items.POTION), Potions.WATER);
|
||||||
|
helper.pullLever(lever);
|
||||||
|
helper.succeedWhen(() -> helper.assertContainerContains(output, expected));
|
||||||
|
}
|
||||||
|
|
||||||
|
@GameTest(template = "wheat_milling")
|
||||||
|
public static void wheatMilling(CreateGameTestHelper helper) {
|
||||||
|
BlockPos output = new BlockPos(1, 2, 1);
|
||||||
|
BlockPos lever = new BlockPos(1, 7, 1);
|
||||||
|
helper.pullLever(lever);
|
||||||
|
ItemStack expected = new ItemStack(AllItems.WHEAT_FLOUR.get(), 3);
|
||||||
|
helper.succeedWhen(() -> helper.assertContainerContains(output, expected));
|
||||||
|
}
|
||||||
|
}
|
|
@ -322,6 +322,8 @@
|
||||||
"create.schematicAndQuill.convert": "Save and Upload Immediately",
|
"create.schematicAndQuill.convert": "Save and Upload Immediately",
|
||||||
"create.schematicAndQuill.fallbackName": "My Schematic",
|
"create.schematicAndQuill.fallbackName": "My Schematic",
|
||||||
"create.schematicAndQuill.saved": "Saved as %1$s",
|
"create.schematicAndQuill.saved": "Saved as %1$s",
|
||||||
|
"create.schematicAndQuill.failed": "Failed to save schematic, check logs for details",
|
||||||
|
"create.schematicAndQuill.instant_failed": "Schematic instant-upload failed, check logs for details",
|
||||||
|
|
||||||
"create.schematic.invalid": "[!] Invalid Item - Use the Schematic Table instead",
|
"create.schematic.invalid": "[!] Invalid Item - Use the Schematic Table instead",
|
||||||
"create.schematic.error": "Schematic failed to Load - Check Game Logs",
|
"create.schematic.error": "Schematic failed to Load - Check Game Logs",
|
||||||
|
|
BIN
src/main/resources/assets/create/textures/gui/computer.png
Normal file
BIN
src/main/resources/assets/create/textures/gui/computer.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1,016 B |
|
@ -13,10 +13,13 @@
|
||||||
"EnchantmentMixin",
|
"EnchantmentMixin",
|
||||||
"EntityMixin",
|
"EntityMixin",
|
||||||
"LavaSwimmingMixin",
|
"LavaSwimmingMixin",
|
||||||
|
"MainMixin",
|
||||||
"MapItemSavedDataMixin",
|
"MapItemSavedDataMixin",
|
||||||
|
"TestCommandMixin",
|
||||||
"accessor.AbstractProjectileDispenseBehaviorAccessor",
|
"accessor.AbstractProjectileDispenseBehaviorAccessor",
|
||||||
"accessor.DispenserBlockAccessor",
|
"accessor.DispenserBlockAccessor",
|
||||||
"accessor.FallingBlockEntityAccessor",
|
"accessor.FallingBlockEntityAccessor",
|
||||||
|
"accessor.GameTestHelperAccessor",
|
||||||
"accessor.LivingEntityAccessor",
|
"accessor.LivingEntityAccessor",
|
||||||
"accessor.NbtAccounterAccessor",
|
"accessor.NbtAccounterAccessor",
|
||||||
"accessor.ServerLevelAccessor"
|
"accessor.ServerLevelAccessor"
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue