Compare commits

..
59 changed files with 793 additions and 1000 deletions

No files matched your search

-17
View File
@@ -1,17 +0,0 @@
name: Optimize images
on:
pull_request:
branches: [ master, main, release/** ]
paths:
- "**/*.png"
push:
branches: [ master, main, release/** ]
paths:
- "**/*.png"
jobs:
optimize-images:
uses: GTNewHorizons/GTNH-Actions-Workflows/.github/workflows/optimize-images.yml@master
secrets: inherit
+1 -2
View File
@@ -1,7 +1,7 @@
javadoc {
javadocTool = javaToolchains.javadocToolFor {
// Last version with frames, but it also has search
languageVersion = JavaLanguageVersion.of(11)
languageVersion = JavaLanguageVersion.of(21)
vendor = JvmVendorSpec.ADOPTIUM
}
@@ -20,7 +20,6 @@ javadoc {
overview "src/main/javadoc/overview.html"
links 'https://docs.oracle.com/javase/8/docs/api/'
links 'https://skmedix.github.io/ForgeJavaDocs/javadoc/forge/1.7.10-10.13.4.1614/'
addBooleanOption("-frames", true)
}
}
+8 -3
View File
@@ -1,10 +1,15 @@
// Add your dependencies here
dependencies {
api("com.github.GTNewHorizons:GTNHLib:0.9.47:dev")
implementation("com.github.GTNewHorizons:NotEnoughItems:2.8.84-GTNH:dev")
api("com.github.GTNewHorizons:GTNHLib:0.6.21:dev")
implementation("com.github.GTNewHorizons:NotEnoughItems:2.7.40-GTNH:dev")
compileOnly("com.github.GTNewHorizons:ServerUtilities:2.1.46")
testImplementation(platform('org.junit:junit-bom:5.9.2'))
testImplementation('org.junit.jupiter:junit-jupiter')
testRuntimeOnly ('org.junit.platform:junit-platform-launcher')
// runtimeOnlyNonPublishable("com.github.GTNewHorizons:GT5-Unofficial:5.09.51.261:dev") {
// exclude group: "com.github.GTNewHorizons", module: "StructureLib"
// }
}
+6 -41
View File
@@ -37,35 +37,11 @@ remoteMappings = https\://raw.githubusercontent.com/MinecraftForge/FML/1.7.10/co
# Select a default username for testing your mod. You can always override this per-run by running
# `./gradlew runClient --username=AnotherPlayer`, or configuring this command in your IDE.
developmentEnvironmentUserName = Developer
developmentEnvironmentUserName = "Developer"
# Enables modern Java syntax support. Valid values:
# - false: No modern syntax, Java 8 only
# - jabel: Jabel syntax-only support, compiles to J8 bytecode
# - jvmDowngrader: Full modern Java via JVM Downgrader (syntax + stdlib APIs)
# - modern: Native modern Java bytecode, no downgrading
enableModernJavaSyntax = jabel
# If set, ignores the above setting and compiles with the given toolchain. This may cause unexpected issues,
# and should *not* be used in most situations. -1 disables this.
# forceToolchainVersion = -1
# Target JVM version for JVM Downgrader bytecode downgrading.
# Only used when enableModernJavaSyntax = jvmDowngrader
# downgradeTargetVersion = 8
# Comma-separated list of Java versions for multi-release jar support (JVM Downgrader only).
# Classes will be available in META-INF/versions/N/ for each version N in this list.
# Default: "21,25" (J25+ gets native classes, J21-24 gets partial downgrade, J8-20 gets full downgrade).
# jvmDowngraderMultiReleaseVersions = 21,25
# Specifies how JVM Downgrader API stubs are provided. Options:
# - shade: Shade minimized stubs into the jar
# - gtnhlib: GTNHLib provides stubs at runtime (adds version constraint)
# - external: Another dependency provides stubs (no constraint, no warning)
# - (empty): Warning reminding you to configure stubs
# Note: 'shade' option requires you to verify license compliance, see: https://github.com/unimined/JvmDowngrader/blob/main/LICENSE.md
# jvmDowngraderStubsProvider =
# Enables using modern Java syntax (up to version 17) via Jabel, while still targeting JVM 8.
# See https://github.com/bsideup/jabel for details on how this works.
enableModernJavaSyntax = true
# Enables injecting missing generics into the decompiled source code for a better coding experience.
# Turns most publicly visible List, Map, etc. into proper List<E>, Map<K, V> types.
@@ -120,9 +96,7 @@ usesMixinDebug = false
# Specify the location of your implementation of IMixinConfigPlugin. Leave it empty otherwise.
mixinPlugin =
# Specify the package that contains all of your Mixins. The package must exist or
# the build will fail. If you have a package property defined in your mixins.<modid>.json,
# it must match with this or the build will fail.
# Specify the package that contains all of your Mixins. You may only place Mixins in this package or the build will fail!
mixinsPackage = mixins
# Specify the core mod entry class if you use a core mod. This class must implement IFMLLoadingPlugin!
@@ -177,7 +151,7 @@ modrinthProjectId =
# type can be one of [project, version],
# and the name is the Modrinth project or version slug/id of the other mod.
# Example: required-project:fplib;optional-project:gasstation;incompatible-project:gregtech
# Note: UniMixins is automatically set as a required dependency if usesMixins = true.
# Note: GTNH Mixins is automatically set as a required dependency if usesMixins = true
modrinthRelations =
# Publishing to CurseForge requires you to set the CURSEFORGE_TOKEN environment variable to one of your CurseForge API tokens.
@@ -198,12 +172,6 @@ curseForgeRelations =
# projects. New projects should not use this parameter.
# customArchiveBaseName =
# Optional parameter to customize the default working directory used by the runClient* tasks. Relative to the project directory.
# runClientWorkingDirectory = run/client
# Optional parameter to customize the default working directory used by the runServer* tasks. Relative to the project directory.
# runServerWorkingDirectory = run/server
# Optional parameter to have the build automatically fail if an illegal version is used.
# This can be useful if you e.g. only want to allow versions in the form of '1.1.xxx'.
# The check is ONLY performed if the version is a git tag.
@@ -233,6 +201,3 @@ curseForgeRelations =
# This is meant to be set in $HOME/.gradle/gradle.properties.
# ideaCheckSpotlessOnBuild = true
# Non-GTNH properties
org.gradle.configuration-cache = true
org.gradle.parallel = true
-12
View File
@@ -1,12 +0,0 @@
#This file is generated by updateDaemonJvm
toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/1630f7ebef05444cb27a2709ea0249b3/redirect
toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect
toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/1630f7ebef05444cb27a2709ea0249b3/redirect
toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect
toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/d4fd992c9557644e637ebe98263e0ae7/redirect
toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/faa12903720d410b387cc69ccafb1a74/redirect
toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/1630f7ebef05444cb27a2709ea0249b3/redirect
toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/cd495626d2ee49a75447e3fdc6afb287/redirect
toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/a4c09dd2e2d7079373d30e524bbc2829/redirect
toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/8e1d9ee5d0f13e442218f6884a306da1/redirect
toolchainVersion=25
Binary file not shown.
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.0-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
Vendored
+6 -3
View File
@@ -1,7 +1,7 @@
#!/bin/sh
#
# Copyright © 2015 the original authors.
# Copyright © 2015-2021 the original authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
@@ -57,7 +57,7 @@
# Darwin, MinGW, and NonStop.
#
# (3) This script is generated from the Groovy template
# https://github.com/gradle/gradle/blob/b631911858264c0b6e4d6603d677ff5218766cee/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
# within the Gradle project.
#
# You can find Gradle at https://github.com/gradle/gradle/.
@@ -114,6 +114,7 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;;
esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
# Determine the Java command to use to start the JVM.
@@ -171,6 +172,7 @@ fi
# For Cygwin or MSYS, switch paths to Windows format before running java
if "$cygwin" || "$msys" ; then
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
JAVACMD=$( cygpath --unix "$JAVACMD" )
@@ -210,7 +212,8 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \
-jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
-classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \
"$@"
# Stop when "xargs" is not available.
Vendored
+2 -1
View File
@@ -70,10 +70,11 @@ goto fail
:execute
@rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
@rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
:end
@rem End local scope for the variables with windows NT shell
+1 -1
View File
@@ -17,7 +17,7 @@ pluginManagement {
}
plugins {
id 'com.gtnewhorizons.gtnhsettingsconvention' version '2.0.24'
id 'com.gtnewhorizons.gtnhsettingsconvention' version '1.0.38'
}
@@ -46,6 +46,7 @@ import com.gtnewhorizon.structurelib.net.SetChannelDataMessage;
import cpw.mods.fml.client.config.GuiConfig;
import cpw.mods.fml.client.event.ConfigChangedEvent;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLLoadCompleteEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.eventhandler.EventPriority;
@@ -259,6 +260,11 @@ public class ClientProxy extends CommonProxy {
MinecraftForge.EVENT_BUS.register(new ForgeEventHandler());
}
@Override
public void init(FMLInitializationEvent e) {
super.init(e);
}
static void markTextureUsed(IIcon icon) {
if (StructureLib.COMPAT instanceof IStructureCompat)
((IStructureCompat) StructureLib.COMPAT).markTextureUsed(icon);
@@ -14,9 +14,12 @@ import net.minecraft.util.IIcon;
import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;
import com.gtnewhorizon.gtnhlib.keybind.SyncedKeybind;
import com.gtnewhorizon.structurelib.item.ItemConstructableTrigger;
import com.gtnewhorizon.structurelib.net.ErrorHintParticleMessage;
import com.gtnewhorizon.structurelib.net.UpdateHintParticleMessage;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLLoadCompleteEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
@@ -64,6 +67,8 @@ public class CommonProxy {
public void preInit(FMLPreInitializationEvent e) {}
public void init(FMLInitializationEvent e) {}
public long getOverworldTime() {
return MinecraftServer.getServer().getEntityWorld().getTotalWorldTime();
}
@@ -108,6 +113,16 @@ public class CommonProxy {
}
}
private static final SyncedKeybind MODE_SWAP_KEY = SyncedKeybind.createConfigurable(
"item.structurelib.constructableTrigger.keys.cycleMode",
"item.structurelib.constructableTrigger.keyCategory",
0).registerGlobalListener((entityPlayerMP, syncedKeybind) -> {
final ItemStack held = entityPlayerMP.getHeldItem();
if (held != null && held.getItem() instanceof ItemConstructableTrigger) {
ItemConstructableTrigger.cycleMode(held);
}
});
public void displayConfigGUI(String category) {}
public static class ForgeEventHandler {
@@ -33,6 +33,7 @@ import com.gtnewhorizon.structurelib.util.XSTR;
import cpw.mods.fml.common.Mod;
import cpw.mods.fml.common.SidedProxy;
import cpw.mods.fml.common.event.FMLInitializationEvent;
import cpw.mods.fml.common.event.FMLInterModComms;
import cpw.mods.fml.common.event.FMLLoadCompleteEvent;
import cpw.mods.fml.common.event.FMLPreInitializationEvent;
@@ -128,6 +129,11 @@ public class StructureLib {
DevelopHelper.onPreInit();
}
@Mod.EventHandler
public void init(FMLInitializationEvent e) {
proxy.init(e);
}
@Mod.EventHandler
public void loadComplete(FMLLoadCompleteEvent e) {
proxy.loadComplete(e);
@@ -14,28 +14,8 @@ import com.gtnewhorizon.structurelib.alignment.enumerable.Rotation;
public interface IAlignmentLimits {
/**
* Can be placed in any orientation.
*/
IAlignmentLimits UNLIMITED = (direction, rotation, flip) -> true;
/**
* Must be standing upright; i.e., top side of the block facing up.
*/
IAlignmentLimits UPRIGHT = (direction, rotation, flip) -> direction.offsetY == 0
&& (flip.isVerticallyFliped() ? rotation.isUpsideDown() : rotation.isNotRotated());
/**
* Can be in any orientation except upside down; i.e., top side of the block facing down.
*/
IAlignmentLimits NOT_UPSIDE_DOWN = (direction, rotation, flip) -> direction.offsetY != 0
|| (flip.isVerticallyFliped() ? !rotation.isNotRotated() : !rotation.isUpsideDown());
/**
* Front side of the block must be facing up.
*/
IAlignmentLimits FACING_UP = (direction, rotation, flip) -> direction == ForgeDirection.UP;
boolean isNewExtendedFacingValid(ForgeDirection direction, Rotation rotation, Flip flip);
default boolean isNewExtendedFacingValid(ExtendedFacing alignment) {
@@ -5,11 +5,7 @@ import static java.lang.Math.abs;
import net.minecraft.util.Vec3;
import net.minecraftforge.common.util.ForgeDirection;
import org.joml.Vector3i;
import com.gtnewhorizon.structurelib.alignment.enumerable.Direction;
import com.gtnewhorizon.structurelib.coords.CoordinateSystem;
import com.gtnewhorizon.structurelib.coords.Position;
import com.gtnewhorizon.structurelib.util.Vec3Impl;
public class IntegerAxisSwap {
@@ -95,32 +91,4 @@ public class IntegerAxisSwap {
out[1] = forFirstAxis.get1() * point[0] + forSecondAxis.get1() * point[1] + forThirdAxis.get1() * point[2];
out[2] = forFirstAxis.get2() * point[0] + forSecondAxis.get2() * point[1] + forThirdAxis.get2() * point[2];
}
public Vector3i translate(Vector3i point) {
return new Vector3i(
forFirstAxis.get0() * point.x() + forFirstAxis.get1() * point.y() + forFirstAxis.get2() * point.z(),
forSecondAxis.get0() * point.x() + forSecondAxis.get1() * point.y() + forSecondAxis.get2() * point.z(),
forThirdAxis.get0() * point.x() + forThirdAxis.get1() * point.y() + forThirdAxis.get2() * point.z());
}
public Vector3i inverseTranslate(Vector3i point) {
return new Vector3i(
forFirstAxis.get0() * point.x() + forSecondAxis.get0() * point.y() + forThirdAxis.get0() * point.z(),
forFirstAxis.get1() * point.x() + forSecondAxis.get1() * point.y() + forThirdAxis.get1() * point.z(),
forFirstAxis.get2() * point.x() + forSecondAxis.get2() * point.y() + forThirdAxis.get2() * point.z());
}
public <C extends CoordinateSystem<C, ?>> Position<C> translate(Position<?> point) {
return new Position<>(
forFirstAxis.get0() * point.x() + forFirstAxis.get1() * point.y() + forFirstAxis.get2() * point.z(),
forSecondAxis.get0() * point.x() + forSecondAxis.get1() * point.y() + forSecondAxis.get2() * point.z(),
forThirdAxis.get0() * point.x() + forThirdAxis.get1() * point.y() + forThirdAxis.get2() * point.z());
}
public <C extends CoordinateSystem<C, ?>> Position<C> inverseTranslate(Position<?> point) {
return new Position<>(
forFirstAxis.get0() * point.x() + forSecondAxis.get0() * point.y() + forThirdAxis.get0() * point.z(),
forFirstAxis.get1() * point.x() + forSecondAxis.get1() * point.y() + forThirdAxis.get1() * point.z(),
forFirstAxis.get2() * point.x() + forSecondAxis.get2() * point.y() + forThirdAxis.get2() * point.z());
}
}
@@ -57,12 +57,12 @@ public class ConstructableUtility {
}
IConstructable constructable = null;
if (tTileEntity instanceof IConstructableProvider)
constructable = ((IConstructableProvider) tTileEntity).getConstructable();
else if (tTileEntity instanceof IConstructable) {
constructable = (IConstructable) tTileEntity;
if (tTileEntity instanceof IConstructableProvider aConstructableProvider)
constructable = aConstructableProvider.getConstructable();
else if (tTileEntity instanceof IConstructable aConstructable) {
constructable = aConstructable;
} else if (IMultiblockInfoContainer.contains(tTileEntity.getClass())) {
ExtendedFacing facing = tTileEntity instanceof IAlignment ? ((IAlignment) tTileEntity).getExtendedFacing()
ExtendedFacing facing = tTileEntity instanceof IAlignment alignment ? alignment.getExtendedFacing()
: ExtendedFacing.of(ForgeDirection.getOrientation(aSide));
constructable = IMultiblockInfoContainer.<TileEntity>get(tTileEntity.getClass())
.toConstructable(tTileEntity, facing);
@@ -70,14 +70,13 @@ public class ConstructableUtility {
if (constructable == null) return false;
if (aPlayer instanceof EntityPlayerMP) {
EntityPlayerMP playerMP = (EntityPlayerMP) aPlayer;
if (aPlayer instanceof EntityPlayerMP playerMP) {
// server side and sneaking (already checked above)
// do construct
if (aPlayer.capabilities.isCreativeMode) {
constructable.construct(aStack, false);
} else if (constructable instanceof ISurvivalConstructable) {
int built = ((ISurvivalConstructable) constructable).survivalConstruct(
} else if (constructable instanceof ISurvivalConstructable aConstructable) {
int built = aConstructable.survivalConstruct(
aStack,
ConfigurationHandler.INSTANCE.getAutoPlaceBudget(),
ISurvivalBuildEnvironment.create(IItemSource.fromPlayer(playerMP), playerMP));
@@ -24,7 +24,6 @@ import net.minecraftforge.common.util.ForgeDirection;
import com.google.common.collect.ImmutableSet;
import com.gtnewhorizon.structurelib.alignment.IAlignment;
import com.gtnewhorizon.structurelib.alignment.IntegerAxisSwap;
import com.gtnewhorizon.structurelib.coords.StructureRelativeCoords;
import com.gtnewhorizon.structurelib.util.Vec3Impl;
public enum ExtendedFacing {
@@ -167,7 +166,6 @@ public enum ExtendedFacing {
private final String name;
private final IntegerAxisSwap integerAxisSwap;
private final StructureRelativeCoords coordinateSystem;
ExtendedFacing(String name) {
this.name = name;
@@ -249,7 +247,6 @@ public enum ExtendedFacing {
this.b = b;
this.c = c;
integerAxisSwap = new IntegerAxisSwap(a, b, c);
coordinateSystem = new StructureRelativeCoords(integerAxisSwap);
}
public static ExtendedFacing of(ForgeDirection direction, Rotation rotation, Flip flip) {
@@ -440,8 +437,4 @@ public enum ExtendedFacing {
public ForgeDirection getRelativeForwardInWorld() {
return c.getOpposite();
}
public StructureRelativeCoords asCoordinateSystem() {
return coordinateSystem;
}
}
@@ -50,10 +50,6 @@ public class BlockHint extends Block {
@Override
public IIcon getIcon(int aSide, int aMeta) {
// For numbered hints, top and bottom are blank
if (aSide <= 1 && aMeta <= 11) {
return hint[13];
}
return hint[aMeta];
}
@@ -1,40 +0,0 @@
package com.gtnewhorizon.structurelib.coords;
/**
* Controller-relative coordinates. The controller is at 0,0,0. No axis swapping takes place, each axis points in the
* same directions as the matching world axis.
*/
public class ControllerRelativeCoords implements CoordinateSystem<ControllerRelativeCoords, WorldCoords> {
public final int controllerX, controllerY, controllerZ;
public ControllerRelativeCoords(int controllerX, int controllerY, int controllerZ) {
this.controllerX = controllerX;
this.controllerY = controllerY;
this.controllerZ = controllerZ;
}
@Override
public Position<ControllerRelativeCoords> translate(Position<WorldCoords> position) {
return translate(position, controllerX, controllerY, controllerZ);
}
@Override
public Position<WorldCoords> translateInverse(Position<ControllerRelativeCoords> position) {
return translateInverse(position, controllerX, controllerY, controllerZ);
}
public static Position<ControllerRelativeCoords> translate(Position<WorldCoords> position, int controllerX,
int controllerY, int controllerZ) {
position.sub(controllerX, controllerY, controllerZ);
return CoordinateSystem.transmute(position);
}
public static Position<WorldCoords> translateInverse(Position<ControllerRelativeCoords> position, int controllerX,
int controllerY, int controllerZ) {
position.add(controllerX, controllerY, controllerZ);
return CoordinateSystem.transmute(position);
}
}
@@ -1,34 +0,0 @@
package com.gtnewhorizon.structurelib.coords;
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
/**
* A coordinate system is a statically-checked helper for determining which coordinates a {@link Position} has.
* Translation methods never copy a {@link Position} - if a position must remain unchanged, it must be copied
* separately. Most coordinate systems will have static methods that allow you to avoid allocations, though some (such
* as the ones provided by {@link ExtendedFacing#asCoordinateSystem()}) have no reason to be static.
*
* @param <Self>
* @param <Parent>
*/
public interface CoordinateSystem<Self extends CoordinateSystem<Self, Parent>, Parent extends CoordinateSystem<Parent, ?>> {
/**
* Translates a position from the parent coordinate system into this one.
*/
Position<Self> translate(Position<Parent> position);
/**
* Translates a position from this coordinate system into the parent.
*/
Position<Parent> translateInverse(Position<Self> position);
/**
* Helper method that converts a position's coordinate system. Should not be used unless you know it's correct.
*/
static <P1 extends CoordinateSystem<P1, ?>, P2 extends CoordinateSystem<P2, ?>> Position<P1> transmute(
Position<P2> pos) {
// noinspection unchecked
return (Position<P1>) pos;
}
}
@@ -1,73 +0,0 @@
package com.gtnewhorizon.structurelib.coords;
import net.minecraft.util.MathHelper;
import net.minecraft.util.Vec3;
import net.minecraft.world.ChunkPosition;
import org.joml.Vector3f;
import org.joml.Vector3i;
import com.gtnewhorizon.gtnhlib.blockpos.BlockPos;
import com.gtnewhorizon.structurelib.util.Vec3Impl;
/**
* A {@link BlockPos} with a statically-checked coordinate system.
*/
public class Position<C extends CoordinateSystem<C, ?>> extends BlockPos {
public Position() {
}
public Position(int x, int y, int z) {
super(x, y, z);
}
public Position(float x, float y, float z) {
super(MathHelper.floor_float(x), MathHelper.floor_float(y), MathHelper.floor_float(z));
}
public Position(double x, double y, double z) {
super(MathHelper.floor_double(x), MathHelper.floor_double(y), MathHelper.floor_double(z));
}
public Position(ChunkPosition chunkPosition) {
super(chunkPosition);
}
public Position(Vector3i v) {
super(v.x, v.y, v.z);
}
public Position(Vector3f v) {
this(v.x, v.y, v.z);
}
public Position(Vec3 v) {
this(v.xCoord, v.yCoord, v.zCoord);
}
public Position(Vec3Impl v) {
this(v.get0(), v.get1(), v.get2());
}
public Position<C> copy() {
return new Position<>(x, y, z);
}
public Vector3i toVector3i() {
return new Vector3i(x, y, z);
}
public Vector3f toVector3f() {
return new Vector3f(x, y, z);
}
public Vec3 toVec3() {
return Vec3.createVectorHelper(x, y, z);
}
public Vec3Impl toVec3Impl() {
return new Vec3Impl(x, y, z);
}
}
@@ -1,38 +0,0 @@
package com.gtnewhorizon.structurelib.coords;
/// The structure definition-local coords. 0,0,0 is the front-most left-most top-most element. Coordinates increase as
/// you go down, right, or back.
public class StructureDefinitionCoords implements CoordinateSystem<StructureDefinitionCoords, StructureRelativeCoords> {
public final int offsetX, offsetY, offsetZ;
public StructureDefinitionCoords(int offsetX, int offsetY, int offsetZ) {
this.offsetX = offsetX;
this.offsetY = offsetY;
this.offsetZ = offsetZ;
}
@Override
public Position<StructureDefinitionCoords> translate(Position<StructureRelativeCoords> position) {
return translate(position, offsetX, offsetY, offsetZ);
}
@Override
public Position<StructureRelativeCoords> translateInverse(Position<StructureDefinitionCoords> position) {
return translateInverse(position, offsetX, offsetY, offsetZ);
}
public static Position<StructureDefinitionCoords> translate(Position<StructureRelativeCoords> position, int offsetX,
int offsetY, int offsetZ) {
position.add(offsetX, offsetY, offsetZ);
return CoordinateSystem.transmute(position);
}
public static Position<StructureRelativeCoords> translateInverse(Position<StructureDefinitionCoords> position,
int offsetX, int offsetY, int offsetZ) {
position.sub(offsetX, offsetY, offsetZ);
return CoordinateSystem.transmute(position);
}
}
@@ -1,31 +0,0 @@
package com.gtnewhorizon.structurelib.coords;
import com.gtnewhorizon.structurelib.alignment.IntegerAxisSwap;
/**
* Coordinates relative to the structure. 0,0,0 is the controller. 0,0,1 is the block immediately behind the controller
* (looking at it from its face). 1,0,0 is the block to the right of the controller. 0,1,0 is the block below the
* controller.
*/
public class StructureRelativeCoords implements CoordinateSystem<StructureRelativeCoords, ControllerRelativeCoords> {
public final IntegerAxisSwap axisSwap;
public StructureRelativeCoords(IntegerAxisSwap axisSwap) {
this.axisSwap = axisSwap;
}
@Override
public Position<StructureRelativeCoords> translate(Position<ControllerRelativeCoords> position) {
position.set(axisSwap.translate(position));
return CoordinateSystem.transmute(position);
}
@Override
public Position<ControllerRelativeCoords> translateInverse(Position<StructureRelativeCoords> position) {
position.set(axisSwap.inverseTranslate(position));
return CoordinateSystem.transmute(position);
}
}
@@ -1,21 +0,0 @@
package com.gtnewhorizon.structurelib.coords;
/**
* World block coordinates
*/
public class WorldCoords implements CoordinateSystem<WorldCoords, WorldCoords> {
public static final WorldCoords INSTANCE = new WorldCoords();
private WorldCoords() {}
@Override
public Position<WorldCoords> translate(Position<WorldCoords> position) {
return position;
}
@Override
public Position<WorldCoords> translateInverse(Position<WorldCoords> position) {
return position;
}
}
@@ -1,20 +1,34 @@
package com.gtnewhorizon.structurelib.core;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.gtnewhorizon.gtnhmixins.IEarlyMixinLoader;
import com.gtnewhorizon.structurelib.mixins.Mixins;
import cpw.mods.fml.relauncher.IFMLLoadingPlugin;
@IFMLLoadingPlugin.MCVersion("1.7.10")
public class StructureLibCore implements IFMLLoadingPlugin, IEarlyMixinLoader {
public static final Logger log = LogManager.getLogger("StructureLibCore");
@Override
public String getMixinConfig() {
return "mixins.structurelib.early.json";
}
@Override
public List<String> getMixins(Set<String> loadedCoreMods) {
return Mixins.getEarlyMixins(loadedCoreMods);
}
@Override
public String[] getASMTransformerClass() {
return null;
return new String[0];
}
@Override
@@ -36,14 +50,4 @@ public class StructureLibCore implements IFMLLoadingPlugin, IEarlyMixinLoader {
public String getAccessTransformerClass() {
return null;
}
@Override
public String getMixinConfig() {
return "mixins.structurelib.early.json";
}
@Override
public List<String> getMixins(Set<String> loadedCoreMods) {
return Arrays.asList("blockChangeNotifier.MixinForgeHooks", "blockChangeNotifier.MixinWorld");
}
}
@@ -164,17 +164,40 @@ public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScre
list.onGuiInit(this);
autoCompleteList.onGuiInit(this);
addButton(new GuiButton(ADD_BTN, guiLeft + 12, guiTop + 157, 47, 20, I18n.format(I18N_PREFIX + "add")));
addButton(new GuiButton(UNSET_BTN, guiLeft + 65, guiTop + 157, 47, 20, I18n.format(I18N_PREFIX + "unset")));
addButton(new GuiButton(WIPE_BTN, guiLeft + 118, guiTop + 157, 47, 20, I18n.format(I18N_PREFIX + "wipe")));
addButton(
new GuiButton(
ADD_BTN,
guiLeft + 12,
guiTop + 157,
47,
20,
I18n.format("item.structurelib.constructableTrigger.gui.add")));
addButton(
new GuiButton(
UNSET_BTN,
guiLeft + 65,
guiTop + 157,
47,
20,
I18n.format("item.structurelib.constructableTrigger.gui.unset")));
addButton(
new GuiButton(
WIPE_BTN,
guiLeft + 118,
guiTop + 157,
47,
20,
I18n.format("item.structurelib.constructableTrigger.gui.wipe")));
updateButtons();
tooltipSplitCache.clear();
info = StreamSupport
.stream(
Splitter.on("\\n").split(StatCollector.translateToLocal(I18N_PREFIX + "info")).spliterator(),
Splitter.on("\\n").split(
StatCollector.translateToLocal("item.structurelib.constructableTrigger.gui.info"))
.spliterator(),
false)
.flatMap(line -> fontRendererObj.listFormattedStringToWidth(line, width * 1 / 3).stream())
.flatMap(line -> fontRendererObj.listFormattedStringToWidth(line, width * 3 / 5).stream())
.collect(Collectors.toList());
}
@@ -218,7 +241,8 @@ public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScre
actionPerformed(but);
}
public List<GuiButton> getButtonList() {
@SuppressWarnings("unchecked")
private List<GuiButton> getButtonList() {
return buttonList;
}
@@ -261,11 +285,6 @@ public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScre
switch (aKey) {
case Keyboard.KEY_TAB:
if (key.isFocused()) {
if (autoCompleteList.getSelectedElement() != null) {
key.setText(autoCompleteList.getSelectedElement());
return;
}
key.setFocused(false);
value.setFocused(true);
} else {
@@ -303,10 +322,11 @@ public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScre
}
break;
}
if (key.textboxKeyTyped(aChar, aKey) || value.textboxKeyTyped(aChar, aKey)) {
if (key.textboxKeyTyped(aChar, aKey)) {
updateButtons();
return;
}
if (value.textboxKeyTyped(aChar, aKey)) return;
super.keyTyped(aChar, aKey);
}
@@ -317,8 +337,7 @@ public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScre
boolean existing = !StringUtils.isEmpty(keyText) && ChannelDataAccessor.hasSubChannel(trigger, keyText);
getButtonList().get(ADD_BTN).displayString = existing ? I18n.format(I18N_PREFIX + "set")
: I18n.format(I18N_PREFIX + "add");
getButtonList().get(ADD_BTN).enabled = !StringUtils.isBlank(key.getText())
&& !StringUtils.isBlank(value.getText())
getButtonList().get(ADD_BTN).enabled = !StringUtils.isBlank(value.getText())
&& Integer.parseInt(value.getText()) > 0;
getButtonList().get(UNSET_BTN).enabled = existing && !StringUtils.isBlank(value.getText());
@@ -378,10 +397,22 @@ public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScre
int topLeftY = (this.height - this.getYSize()) / 2;
drawTexturedModalRect(topLeftX, topLeftY, 0, 0, getXSize(), getYSize());
list.drawScreen(mX, mY, partialTick);
fontRendererObj.drawString(I18n.format(I18N_PREFIX + "title"), guiLeft + 12, guiTop + 9, 0);
fontRendererObj.drawString(I18n.format(I18N_PREFIX + "key"), guiLeft + 12, guiTop + 122, 0);
fontRendererObj.drawString(
I18n.format("item.structurelib.constructableTrigger.gui.title"),
guiLeft + 12,
guiTop + 9,
0);
fontRendererObj.drawString(
I18n.format("item.structurelib.constructableTrigger.gui.key"),
guiLeft + 12,
guiTop + 122,
0);
key.drawTextBox();
fontRendererObj.drawString(I18n.format(I18N_PREFIX + "value"), guiLeft + 12, guiTop + 142, 0);
fontRendererObj.drawString(
I18n.format("item.structurelib.constructableTrigger.gui.value"),
guiLeft + 12,
guiTop + 142,
0);
value.drawTextBox();
}
@@ -424,7 +455,7 @@ public class GuiScreenConfigureChannels extends GuiContainer implements IGuiScre
tooltip.addAll(
fontRendererObj.listFormattedStringToWidth(
StatCollector.translateToLocalFormatted(
I18N_PREFIX + "channels.from",
"item.structurelib.constructableTrigger.gui.channels.from",
Loader.instance().getIndexedModList().get(e.getKey()).getName()),
maxLine));
tooltip.addAll(
@@ -7,9 +7,14 @@ import static net.minecraft.util.StatCollector.translateToLocalFormatted;
import java.util.List;
import net.minecraft.client.renderer.texture.IIconRegister;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.nbt.NBTTagInt;
import net.minecraft.util.IIcon;
import net.minecraft.util.StatCollector;
import net.minecraft.world.World;
import org.lwjgl.input.Keyboard;
@@ -20,20 +25,86 @@ import com.gtnewhorizon.structurelib.alignment.constructable.ConstructableUtilit
public class ItemConstructableTrigger extends Item {
private static final String TAG_MODE = "mode";
private static final IIcon[] textures = new IIcon[3];
public enum TriggerMode {
BUILDING,
UPDATING,
REMOVING
}
public ItemConstructableTrigger() {
setUnlocalizedName("structurelib.constructableTrigger");
setTextureName(MOD_ID + ":itemConstructableTrigger");
setCreativeTab(StructureLib.creativeTab);
}
public static void cycleMode(ItemStack held) {
if (held.getTagCompound() == null) held.setTagCompound(new NBTTagCompound());
if (!held.getTagCompound().hasKey(TAG_MODE)) held.getTagCompound().setTag(TAG_MODE, new NBTTagInt(0));
held.getTagCompound().setInteger(TAG_MODE, (held.getTagCompound().getInteger(TAG_MODE) + 1) % 3);
}
@Override
public void registerIcons(IIconRegister register) {
textures[0] = register.registerIcon(MOD_ID + ":constructableTrigger/BUILDING");
textures[1] = register.registerIcon(MOD_ID + ":constructableTrigger/UPDATING");
textures[2] = register.registerIcon(MOD_ID + ":constructableTrigger/REMOVING");
}
@Override
public IIcon getIcon(ItemStack stack, int renderPass, EntityPlayer player, ItemStack usingItem, int useRemaining) {
return getIcon(stack, renderPass);
}
@Override
public IIcon getIcon(ItemStack stack, int pass) {
if (!stack.hasTagCompound() || !stack.getTagCompound().hasKey(TAG_MODE)) {
return textures[0];
}
return textures[stack.getTagCompound().getInteger(TAG_MODE)];
}
@Override
public String getItemStackDisplayName(ItemStack stack) {
if (!stack.hasTagCompound() || !stack.getTagCompound().hasKey(TAG_MODE)) {
return getDisplayName(0);
}
return getDisplayName(stack.getTagCompound().getInteger(TAG_MODE));
}
@Override
public IIcon getIconIndex(ItemStack stack) {
return getIcon(stack, 0);
}
@Override
public IIcon getIconFromDamage(int damage) {
return textures[damage];
}
private String getDisplayName(int mode) {
return StatCollector.translateToLocal("item.structurelib.constructableTrigger.name") + " - "
+ StatCollector.translateToLocal("item.structurelib.constructableTrigger.modes." + mode);
}
public static TriggerMode getMode(ItemStack held) {
if (held == null || !held.hasTagCompound() || !held.getTagCompound().hasKey(TAG_MODE)) {
// default behavior
return TriggerMode.BUILDING;
}
// get user-defined mode
return TriggerMode.values()[held.getTagCompound().getInteger(TAG_MODE)];
}
@Override
public ItemStack onItemRightClick(ItemStack stack, World world, EntityPlayer player) {
if (getMovingObjectPositionFromPlayer(world, player, true) == null) {
// channel gui is opened from server and config gui from client
if (!world.isRemote && !player.isSneaking()) {
player.openGui(StructureLib.instance(), 0, world, player.inventory.currentItem, 0, 0);
} else if (world.isRemote && player.isSneaking()) {
if (!world.isRemote && getMovingObjectPositionFromPlayer(world, player, true) == null) {
if (player.isSneaking()) {
StructureLib.instance().proxy().displayConfigGUI("registries");
} else {
player.openGui(StructureLib.instance(), 0, world, player.inventory.currentItem, 0, 0);
}
}
return super.onItemRightClick(stack, world, player);
@@ -0,0 +1,188 @@
package com.gtnewhorizon.structurelib.mixins;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.Supplier;
import com.gtnewhorizon.structurelib.core.StructureLibCore;
import cpw.mods.fml.relauncher.FMLLaunchHandler;
public enum Mixins {
BLOCK_CHANGE_NOTIFIER(new Builder("Block Change Notifier").setPhase(Phase.EARLY)
.addMixinClasses("blockChangeNotifier.MixinForgeHooks", "blockChangeNotifier.MixinWorld")
.addTargetedMod(TargetedMod.VANILLA).setSide(Side.BOTH).setApplyIf(() -> true));
private final List<String> mixinClasses;
private final List<TargetedMod> targetedMods;
private final List<TargetedMod> excludedMods;
private final Supplier<Boolean> applyIf;
private final Phase phase;
private final Side side;
Mixins(Builder builder) {
this.mixinClasses = builder.mixinClasses;
this.targetedMods = builder.targetedMods;
this.excludedMods = builder.excludedMods;
this.applyIf = builder.applyIf;
this.phase = builder.phase;
this.side = builder.side;
if (this.mixinClasses.isEmpty()) {
throw new RuntimeException("No mixin class specified for Mixin : " + this.name());
}
if (this.targetedMods.isEmpty()) {
throw new RuntimeException("No targeted mods specified for Mixin : " + this.name());
}
if (this.applyIf == null) {
throw new RuntimeException("No ApplyIf function specified for Mixin : " + this.name());
}
if (this.phase == null) {
throw new RuntimeException("No Phase specified for Mixin : " + this.name());
}
if (this.side == null) {
throw new RuntimeException("No Side function specified for Mixin : " + this.name());
}
}
public static List<String> getEarlyMixins(Set<String> loadedCoreMods) {
final List<String> mixins = new ArrayList<>();
final List<String> notLoading = new ArrayList<>();
for (Mixins mixin : Mixins.values()) {
if (mixin.phase == Phase.EARLY) {
if (mixin.shouldLoad(loadedCoreMods, Collections.emptySet())) {
mixins.addAll(mixin.mixinClasses);
} else {
notLoading.addAll(mixin.mixinClasses);
}
}
}
StructureLibCore.log.info("Not loading the following EARLY mixins: {}", notLoading.toString());
return mixins;
}
public static List<String> getLateMixins(Set<String> loadedMods) {
// NOTE: Any targetmod here needs a modid, not a coremod id
final List<String> mixins = new ArrayList<>();
final List<String> notLoading = new ArrayList<>();
for (Mixins mixin : Mixins.values()) {
if (mixin.phase == Phase.LATE) {
if (mixin.shouldLoad(Collections.emptySet(), loadedMods)) {
mixins.addAll(mixin.mixinClasses);
} else {
notLoading.addAll(mixin.mixinClasses);
}
}
}
StructureLibCore.log.info("Not loading the following LATE mixins: {}", notLoading.toString());
return mixins;
}
private boolean shouldLoadSide() {
return side == Side.BOTH || (side == Side.SERVER && FMLLaunchHandler.side().isServer())
|| (side == Side.CLIENT && FMLLaunchHandler.side().isClient());
}
private boolean allModsLoaded(List<TargetedMod> targetedMods, Set<String> loadedCoreMods, Set<String> loadedMods) {
if (targetedMods.isEmpty()) return false;
for (TargetedMod target : targetedMods) {
if (target == TargetedMod.VANILLA) continue;
// Check coremod first
if (!loadedCoreMods.isEmpty() && target.coreModClass != null
&& !loadedCoreMods.contains(target.coreModClass))
return false;
else if (!loadedMods.isEmpty() && target.modId != null && !loadedMods.contains(target.modId)) return false;
}
return true;
}
private boolean noModsLoaded(List<TargetedMod> targetedMods, Set<String> loadedCoreMods, Set<String> loadedMods) {
if (targetedMods.isEmpty()) return true;
for (TargetedMod target : targetedMods) {
if (target == TargetedMod.VANILLA) continue;
// Check coremod first
if (!loadedCoreMods.isEmpty() && target.coreModClass != null
&& loadedCoreMods.contains(target.coreModClass))
return false;
else if (!loadedMods.isEmpty() && target.modId != null && loadedMods.contains(target.modId)) return false;
}
return true;
}
private boolean shouldLoad(Set<String> loadedCoreMods, Set<String> loadedMods) {
return (shouldLoadSide() && applyIf.get()
&& allModsLoaded(targetedMods, loadedCoreMods, loadedMods)
&& noModsLoaded(excludedMods, loadedCoreMods, loadedMods));
}
private static class Builder {
private final String name;
private final List<String> mixinClasses = new ArrayList<>();
private final List<TargetedMod> targetedMods = new ArrayList<>();
private final List<TargetedMod> excludedMods = new ArrayList<>();
private Supplier<Boolean> applyIf = null;
private Phase phase = null;
private Side side = null;
public Builder(String name) {
this.name = name;
}
public Builder addMixinClasses(String... mixinClasses) {
this.mixinClasses.addAll(Arrays.asList(mixinClasses));
return this;
}
public Builder setPhase(Phase phase) {
if (this.phase != null) {
throw new RuntimeException("Trying to define Phase twice for " + this.name);
}
this.phase = phase;
return this;
}
public Builder setSide(Side side) {
if (this.side != null) {
throw new RuntimeException("Trying to define Side twice for " + this.name);
}
this.side = side;
return this;
}
public Builder setApplyIf(Supplier<Boolean> applyIf) {
this.applyIf = applyIf;
return this;
}
public Builder addTargetedMod(TargetedMod mod) {
this.targetedMods.add(mod);
return this;
}
public Builder addExcludedMod(TargetedMod mod) {
this.excludedMods.add(mod);
return this;
}
}
private enum Side {
BOTH,
CLIENT,
SERVER
}
private enum Phase {
EARLY,
LATE,
}
}
@@ -3,11 +3,8 @@ package com.gtnewhorizon.structurelib.structure;
import static com.gtnewhorizon.structurelib.structure.IStructureWalker.ignoreBlockUnloaded;
import static com.gtnewhorizon.structurelib.structure.IStructureWalker.skipBlockUnloaded;
import java.util.List;
import java.util.function.Function;
import javax.annotation.Nonnull;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.item.ItemStack;
@@ -18,8 +15,6 @@ import com.gtnewhorizon.structurelib.StructureLibAPI;
import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor;
import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
import com.gtnewhorizon.structurelib.coords.Position;
import com.gtnewhorizon.structurelib.coords.StructureDefinitionCoords;
/**
* This is the structure definition of your multi. You will have one of these for each multi.
@@ -324,65 +319,6 @@ public interface IStructureDefinition<T> {
return walker.getBuilt();
}
default void iterate(String piece, World world, ExtendedFacing extendedFacing, int basePositionX, int basePositionY,
int basePositionZ, int basePositionA, int basePositionB, int basePositionC, IStructureWalker<T> predicate) {
StructureUtility.iterateV2(
getStructureFor(piece),
world,
extendedFacing,
basePositionX,
basePositionY,
basePositionZ,
basePositionA,
basePositionB,
basePositionC,
predicate,
"customIterate");
}
/**
* Gets the controller position for a piece. Controller positions are denoted by a tilde.
*
* @param piece The piece name
* @return The controller position, or null if it doesn't exist for the given piece
*/
Position<StructureDefinitionCoords> getControllerPosition(String piece);
/**
* Gets the coordinate system for a piece. This is used to translate structure definition coordinates into structure
* relative coordinates.
*
* @param piece The piece
* @return The coordinate system
* @throws IllegalArgumentException When the piece does not exist
*/
StructureDefinitionCoords getCoordinateSystem(String piece);
/**
* Gets a socket from a piece.
*
* @param piece The piece name
* @param socket The socket character
* @return The socket's position
* @throws IllegalArgumentException When the piece does not exist
* @throws IllegalArgumentException When the socket does not exist
* @throws IllegalStateException When more than one socket exists with this character
* @see StructureDefinition.Builder#addSocket(char, char)
*/
Position<StructureDefinitionCoords> getSocket(String piece, char socket);
/**
* Gets all sockets with a given character for a piece.
*
* @param piece The piece name
* @param socket The socket character
* @return The list of socket positions, or an empty list if the socket is not present
* @throws IllegalArgumentException When the piece does not exist
* @see StructureDefinition.Builder#addSocket(char, char)
*/
@Nonnull
List<Position<StructureDefinitionCoords>> getAllSockets(String piece, char socket);
/**
* Low level utility.
*
@@ -412,77 +348,47 @@ public interface IStructureDefinition<T> {
}
if (checkBlocksIfNotNullForceCheckAllIfTrue != null) {
boolean success;
if (checkBlocksIfNotNullForceCheckAllIfTrue) {
success = StructureUtility.iterateV2(
elements,
world,
extendedFacing,
basePositionX,
basePositionY,
basePositionZ,
basePositionA,
basePositionB,
basePositionC,
(e, w, x, y, z, a, b, c) -> e.check(object, w, x, y, z),
"check");
} else {
success = StructureUtility.iterateV2(
elements,
world,
extendedFacing,
basePositionX,
basePositionY,
basePositionZ,
basePositionA,
basePositionB,
basePositionC,
skipBlockUnloaded((e, w, x, y, z, a, b, c) -> e.check(object, w, x, y, z)),
"check force");
}
boolean success = StructureUtility.iterateV2(
elements,
world,
extendedFacing,
basePositionX,
basePositionY,
basePositionZ,
basePositionA,
basePositionB,
basePositionC,
checkBlocksIfNotNullForceCheckAllIfTrue ? (e, w, x, y, z, a, b, c) -> e.check(object, w, x, y, z)
: skipBlockUnloaded((e, w, x, y, z, a, b, c) -> e.check(object, w, x, y, z)),
checkBlocksIfNotNullForceCheckAllIfTrue ? "check" : "check force");
if (StructureLibAPI.isDebugEnabled() && success) {
StructureLib.LOGGER
.info("Multi [" + basePositionX + ", " + basePositionY + ", " + basePositionZ + "] pass");
}
return success;
} else {
if (hintsOnly) {
StructureUtility.iterateV2(
elements,
world,
extendedFacing,
basePositionX,
basePositionY,
basePositionZ,
basePositionA,
basePositionB,
basePositionC,
ignoreBlockUnloaded((e, w, x, y, z, a, b, c) -> {
e.spawnHint(object, world, x, y, z, trigger);
if (ChannelDataAccessor.hasSubChannel(trigger, StructureLibAPI.CHANNEL_SHOW_ERROR)
&& !e.couldBeValid(object, world, x, y, z, trigger)) {
StructureLibAPI.markHintParticleError(StructureLib.getCurrentPlayer(), world, x, y, z);
}
return true;
}),
"spawnHint");
} else {
StructureUtility.iterateV2(
elements,
world,
extendedFacing,
basePositionX,
basePositionY,
basePositionZ,
basePositionA,
basePositionB,
basePositionC,
ignoreBlockUnloaded((e, w, x, y, z, a, b, c) -> {
e.placeBlock(object, world, x, y, z, trigger);
return true;
}),
"placeBlock");
}
StructureUtility.iterateV2(
elements,
world,
extendedFacing,
basePositionX,
basePositionY,
basePositionZ,
basePositionA,
basePositionB,
basePositionC,
hintsOnly ? ignoreBlockUnloaded((e, w, x, y, z, a, b, c) -> {
e.spawnHint(object, world, x, y, z, trigger);
if (ChannelDataAccessor.hasSubChannel(trigger, "show_error")
&& !e.couldBeValid(object, world, x, y, z, trigger)) {
StructureLibAPI.markHintParticleError(StructureLib.getCurrentPlayer(), world, x, y, z);
}
return true;
}) : ignoreBlockUnloaded((e, w, x, y, z, a, b, c) -> {
e.placeBlock(object, world, x, y, z, trigger);
return true;
}),
hintsOnly ? "spawnHint" : "placeBlock");
}
return true;
}
@@ -2,6 +2,8 @@ package com.gtnewhorizon.structurelib.structure;
import static com.gtnewhorizon.structurelib.StructureLib.LOGGER;
import static com.gtnewhorizon.structurelib.StructureLib.PANIC_MODE;
import static com.gtnewhorizon.structurelib.item.ItemConstructableTrigger.TriggerMode.REMOVING;
import static com.gtnewhorizon.structurelib.structure.StructureUtility.checkForRemoving;
import java.util.Arrays;
import java.util.Collections;
@@ -21,6 +23,7 @@ import net.minecraft.util.IChatComponent;
import net.minecraft.world.World;
import com.gtnewhorizon.structurelib.StructureLibAPI;
import com.gtnewhorizon.structurelib.item.ItemConstructableTrigger;
import com.gtnewhorizon.structurelib.util.ItemStackPredicate;
import com.gtnewhorizon.structurelib.util.ItemStackPredicate.NBTMode;
@@ -137,12 +140,14 @@ public interface IStructureElement<T> {
*/
default PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
AutoPlaceEnvironment env) {
ItemConstructableTrigger.TriggerMode mode = ItemConstructableTrigger.getMode(trigger);
BlocksToPlace e = getBlocksToPlace(t, world, x, y, z, trigger, env);
IItemSource source = env.getSource();
EntityPlayer actor = env.getActor();
Consumer<IChatComponent> chatter = env.getChatter();
if (e != null) {
if (check(t, world, x, y, z)) return PlaceResult.SKIP;
PlaceResult skipOrRejectBasedOnCheck = checkForRemoving(mode, check(t, world, x, y, z), world, x, y, z);
if (skipOrRejectBasedOnCheck != PlaceResult.ACCEPT) return skipOrRejectBasedOnCheck;
if (e.getStacks() == null) {
ItemStack taken = source.takeOne(e.getPredicate(), true);
return StructureUtility.survivalPlaceBlock(
@@ -159,7 +164,7 @@ public interface IStructureElement<T> {
chatter);
}
for (ItemStack stack : e.getStacks()) {
if (!source.takeOne(stack, true)) continue;
if (mode != REMOVING && !source.takeOne(stack, true)) continue;
return StructureUtility.survivalPlaceBlock(
stack,
NBTMode.EXACT,
@@ -2,25 +2,8 @@ package com.gtnewhorizon.structurelib.structure;
import net.minecraft.world.World;
/**
* A functional interface that is used to iterate over an arbitrary structure.
*/
@FunctionalInterface
public interface IStructureWalker<T> {
interface IStructureWalker<T> {
/**
* Called once per structure element.
*
* @param element The current element
* @param world The current world
* @param x The absolute X position of the element in world coordinates
* @param y The absolute Y position of the element in world coordinates
* @param z The absolute Z position of the element in world coordinates
* @param a The relative A position of the element in structure-local coordinates (pre transform)
* @param b The relative B position of the element in structure-local coordinates (pre transform)
* @param c The relative C position of the element in structure-local coordinates (pre transform)
* @return False to stop iterating, true to continue.
*/
boolean visit(IStructureElement<T> element, World world, int x, int y, int z, int a, int b, int c);
default boolean blockNotLoaded(IStructureElement<T> element, World world, int x, int y, int z, int a, int b,
@@ -4,49 +4,33 @@ import static com.gtnewhorizon.structurelib.structure.StructureUtility.isAir;
import static com.gtnewhorizon.structurelib.structure.StructureUtility.notAir;
import static com.gtnewhorizon.structurelib.structure.StructureUtility.step;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.MultimapBuilder;
import com.gtnewhorizon.structurelib.coords.Position;
import com.gtnewhorizon.structurelib.coords.StructureDefinitionCoords;
import com.gtnewhorizon.structurelib.util.Vec3Impl;
import it.unimi.dsi.fastutil.chars.Char2CharOpenHashMap;
import it.unimi.dsi.fastutil.chars.CharOpenHashSet;
public class StructureDefinition<T> implements IStructureDefinition<T> {
private final Map<Character, IStructureElement<T>> elements;
private final Map<String, String> shapes;
private final Map<String, IStructureElement<T>[]> structures;
private final Map<String, Set<Vec3Impl>> occupiedSpaces;
private final Map<String, Position<StructureDefinitionCoords>> controllers;
private final Map<String, ListMultimap<Character, Position<StructureDefinitionCoords>>> socketLocations;
public static <B> Builder<B> builder() {
return new Builder<>();
}
private StructureDefinition(Map<Character, IStructureElement<T>> elements, Map<String, String> shapes,
Map<String, IStructureElement<T>[]> structures, Map<String, Set<Vec3Impl>> occupiedSpaces,
Map<String, Position<StructureDefinitionCoords>> controllers,
Map<String, ListMultimap<Character, Position<StructureDefinitionCoords>>> socketLocations) {
Map<String, IStructureElement<T>[]> structures, Map<String, Set<Vec3Impl>> occupiedSpaces) {
this.elements = elements;
this.shapes = shapes;
this.structures = structures;
this.occupiedSpaces = occupiedSpaces;
this.controllers = controllers;
this.socketLocations = socketLocations;
}
public static class Builder<T> {
@@ -57,18 +41,12 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
private char d = '\uD000';
private final Map<Vec3Impl, Character> navigates;
private final Map<Character, IStructureElement<T>> elements;
private final Map<String, String[][]> uncompiledShapes;
private final Map<String, String> shapes;
private final Map<String, Set<Vec3Impl>> occupiedSpaces;
private final Map<String, Position<StructureDefinitionCoords>> controllers = new HashMap<>();
private final Char2CharOpenHashMap socketTranslations = new Char2CharOpenHashMap();
private final Map<String, ListMultimap<Character, Position<StructureDefinitionCoords>>> socketLocations = new HashMap<>();
private Builder() {
navigates = new HashMap<>();
elements = new HashMap<>();
uncompiledShapes = new HashMap<>();
shapes = new HashMap<>();
occupiedSpaces = new HashMap<>();
}
@@ -104,69 +82,7 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
* @return this builder
*/
public Builder<T> addShape(String name, String[][] structurePiece) {
uncompiledShapes.put(name, structurePiece);
return this;
}
/**
* @deprecated use the unboxed version
*/
@Deprecated
public Builder<T> addElement(Character name, IStructureElement<T> structurePiece) {
elements.putIfAbsent(name, structurePiece);
return this;
}
public Builder<T> addElement(char name, IStructureElement<T> structurePiece) {
elements.putIfAbsent(name, structurePiece);
return this;
}
/**
* Denotes {@code name} as a socket. Sockets are virtual structure elements that record their position in the
* structure for future retrieval. They are transformed into real structure elements and do not need their own
* element (see {@code replacement}).
*
* @param name The socket name character
* @param replacement The replacement character
* @see IStructureDefinition#getSocket(String, char)
* @see IStructureDefinition#getAllSockets(String, char)
*/
public Builder<T> addSocket(char name, char replacement) {
socketTranslations.put(name, replacement);
return this;
}
/**
* Like {@link #addSocket(char, char)} but without the replacement. The structure must have a structure element
* with the given character.
*
* @param name The socket name character
*/
public Builder<T> addSocket(char name) {
// Adding a name -> name entry like this isn't ideal but this is the cleanest solution
socketTranslations.put(name, name);
return this;
}
public IStructureDefinition<T> build() {
uncompiledShapes.forEach(this::compileShape);
Map<String, IStructureElement<T>[]> structures = compileStructureMap();
return new StructureDefinition<>(
new HashMap<>(elements),
new HashMap<>(shapes),
structures,
occupiedSpaces,
controllers,
socketLocations);
}
private Builder<T> compileShape(String name, String[][] structurePiece) {
StringBuilder builder = new StringBuilder();
if (structurePiece.length > 0) {
for (String[] strings : structurePiece) {
if (strings.length > 0) {
@@ -179,11 +95,6 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
}
builder.setLength(builder.length() - 1);
}
// Always create a map for this piece, even if it doesn't have any sockets
// noinspection UnstableApiUsage
socketLocations.put(name, MultimapBuilder.hashKeys().arrayListValues().build());
Set<Vec3Impl> occupiedSpace = new HashSet<>();
// these track the global current location
int aa = 0, bb = 0, cc = 0;
@@ -192,15 +103,6 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
// I do know these are pretty bad variable naming, but I have no better idea
for (int i = 0; i < builder.length(); i++) {
char ch = builder.charAt(i);
// Translate the element char if it's a socket, and record the socket position
if (socketTranslations.containsKey(ch)) {
socketLocations.get(name).put(ch, new Position<>(aa, bb, cc));
ch = socketTranslations.get(ch);
builder.setCharAt(i, ch);
}
if (ch == ' ') {
builder.setCharAt(i, A);
ch = A;
@@ -208,7 +110,6 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
builder.setCharAt(i, A);
ch = A;
occupiedSpace.add(Vec3Impl.getFromPool(aa, bb, cc));
controllers.put(name, new Position<>(aa, bb, cc));
}
if (ch == A) {
aa++;
@@ -247,87 +148,76 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
occupiedSpaces.put(name, occupiedSpace);
boolean hasPlus = false;
boolean hasMinus = false;
String built = builder.toString().replaceAll("[\\uA000\\uB000\\uC000]", "");
int write = 0;
for (int read = 0; read < builder.length(); read++) {
char ch = builder.charAt(read);
if (ch == A || ch == B || ch == C) {
continue;
}
if (ch == '+') {
hasPlus = true;
} else if (ch == '-') {
hasMinus = true;
}
if (read != write) builder.setCharAt(write, ch);
write++;
}
builder.setLength(write);
if (hasPlus) {
if (built.contains("+")) {
addElement('+', notAir());
}
if (hasMinus) {
if (built.contains("-")) {
addElement('-', isAir());
}
shapes.put(name, builder.toString());
shapes.put(name, built);
return this;
}
/**
* @deprecated use the unboxed version
*/
@Deprecated
public Builder<T> addElement(Character name, IStructureElement<T> structurePiece) {
elements.putIfAbsent(name, structurePiece);
return this;
}
public Builder<T> addElement(char name, IStructureElement<T> structurePiece) {
elements.putIfAbsent(name, structurePiece);
return this;
}
public IStructureDefinition<T> build() {
Map<String, IStructureElement<T>[]> structures = compileStructureMap();
return new StructureDefinition<>(
new HashMap<>(elements),
new HashMap<>(shapes),
structures,
occupiedSpaces);
}
@SuppressWarnings("unchecked")
private Map<String, IStructureElement<T>[]> compileElementSetMap() {
Set<Integer> missing = findMissing();
if (missing.isEmpty()) {
return shapes.entrySet().stream().collect(
Collectors.toMap(
Map.Entry::getKey,
e -> e.getValue().chars().mapToObj(c -> (char) c).distinct().map(elements::get)
.toArray(IStructureElement[]::new)));
} else {
throw new RuntimeException(
"Missing Structure Element bindings for (chars as integers): "
+ Arrays.toString(missing.toArray()));
}
}
@SuppressWarnings("unchecked")
private Map<String, IStructureElement<T>[]> compileStructureMap() {
CharOpenHashSet missing = findMissing();
if (!missing.isEmpty()) {
throw new RuntimeException("Missing Structure Element bindings for: " + missing);
Set<Integer> missing = findMissing();
if (missing.isEmpty()) {
return shapes.entrySet().stream().collect(
Collectors.toMap(
Map.Entry::getKey,
e -> e.getValue().chars().mapToObj(c -> elements.get((char) c))
.toArray(IStructureElement[]::new)));
} else {
throw new RuntimeException(
"Missing Structure Element bindings for (chars as integers): "
+ Arrays.toString(missing.toArray()));
}
Map<String, IStructureElement<T>[]> map = new HashMap<>();
for (Entry<String, String> e : shapes.entrySet()) {
char[] chars = e.getValue().toCharArray();
IStructureElement<T>[] compiled = new IStructureElement[chars.length];
int a = 0, b = 0, c = 0;
for (int i = 0; i < chars.length; i++) {
char ch = chars[i];
IStructureElement<T> element = this.elements.get(ch);
compiled[i] = element;
if (element.isNavigating()) {
a = (element.resetA() ? 0 : a) + element.getStepA();
b = (element.resetB() ? 0 : b) + element.getStepB();
c = (element.resetC() ? 0 : c) + element.getStepC();
} else {
a++;
}
}
if (map.put(e.getKey(), compiled) != null) {
throw new IllegalStateException("Duplicate shape: " + e.getKey());
}
}
return map;
}
private CharOpenHashSet findMissing() {
CharOpenHashSet missing = new CharOpenHashSet();
for (String shape : shapes.values()) {
for (char c : shape.toCharArray()) {
if (!elements.containsKey(c)) {
missing.add(c);
}
}
}
return missing;
private Set<Integer> findMissing() {
return shapes.values().stream().flatMapToInt(CharSequence::chars)
.filter(i -> !elements.containsKey((char) i)).boxed().collect(Collectors.toSet());
}
}
@@ -356,61 +246,4 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
if (occupiedSpace == null) throw new NoSuchElementException(name);
return occupiedSpace.contains(new Vec3Impl(offsetA, offsetB, offsetC));
}
@Override
public Position<StructureDefinitionCoords> getControllerPosition(String piece) {
return controllers.get(piece);
}
@Override
public StructureDefinitionCoords getCoordinateSystem(String piece) {
var controller = controllers.get(piece);
if (controller == null) {
throw new IllegalArgumentException("Invalid piece: " + piece);
}
return new StructureDefinitionCoords(controller.x, controller.y, controller.z);
}
@Override
public Position<StructureDefinitionCoords> getSocket(String piece, char socket) {
var piecePositions = socketLocations.get(piece);
if (piecePositions == null) {
throw new IllegalArgumentException("Invalid piece: " + piece);
}
var positions = piecePositions.get(socket);
if (positions.isEmpty()) {
throw new IllegalArgumentException("Socket " + socket + " for piece " + piece + " does not exist");
}
if (positions.size() > 1) {
throw new IllegalStateException(
"Socket " + socket
+ " for piece "
+ piece
+ " has several positions, but expected one and only one");
}
return positions.get(0).copy();
}
@Override
@Nonnull
public List<Position<StructureDefinitionCoords>> getAllSockets(String piece, char socket) {
var piecePositions = socketLocations.get(piece);
if (piecePositions == null) {
throw new IllegalArgumentException("Invalid piece: " + piece);
}
var list = new ArrayList<>(piecePositions.get(socket));
list.replaceAll(Position::copy);
return list;
}
}
@@ -2,6 +2,9 @@ package com.gtnewhorizon.structurelib.structure;
import static com.gtnewhorizon.structurelib.StructureLib.LOGGER;
import static com.gtnewhorizon.structurelib.StructureLib.PANIC_MODE;
import static com.gtnewhorizon.structurelib.item.ItemConstructableTrigger.TriggerMode.BUILDING;
import static com.gtnewhorizon.structurelib.item.ItemConstructableTrigger.TriggerMode.REMOVING;
import static com.gtnewhorizon.structurelib.item.ItemConstructableTrigger.TriggerMode.UPDATING;
import static java.lang.Integer.MIN_VALUE;
import java.util.Arrays;
@@ -28,6 +31,7 @@ import java.util.function.Supplier;
import javax.annotation.Nullable;
import net.minecraft.block.Block;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.init.Blocks;
@@ -36,10 +40,7 @@ import net.minecraft.item.ItemBlock;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.ChatComponentText;
import net.minecraft.util.ChatComponentTranslation;
import net.minecraft.util.ChatStyle;
import net.minecraft.util.EnumChatFormatting;
import net.minecraft.util.IChatComponent;
import net.minecraft.util.IIcon;
import net.minecraft.world.World;
@@ -54,13 +55,16 @@ import com.gtnewhorizon.structurelib.StructureLib;
import com.gtnewhorizon.structurelib.StructureLibAPI;
import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor;
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
import com.gtnewhorizon.structurelib.item.ItemConstructableTrigger;
import com.gtnewhorizon.structurelib.structure.IStructureElement.PlaceResult;
import com.gtnewhorizon.structurelib.structure.adders.IBlockAdder;
import com.gtnewhorizon.structurelib.structure.adders.ITileAdder;
import com.gtnewhorizon.structurelib.util.ItemStackPredicate;
import com.gtnewhorizon.structurelib.util.ItemStackPredicate.NBTMode;
import com.gtnewhorizon.structurelib.util.Vec3Impl;
import com.gtnewhorizon.structurelib.xmod.ServerUtilitiesCompat;
import cpw.mods.fml.common.Loader;
import cpw.mods.fml.common.registry.GameRegistry;
/**
@@ -149,6 +153,8 @@ public class StructureUtility {
private static final String NICE_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz=|!@#$%&()[]{};:<>/?_,.*^'`";
private static final boolean isSULoaded = Loader.isModLoaded("serverutilities");
@SuppressWarnings("rawtypes")
private static final Map<Vec3Impl, IStructureNavigate> STEP = new HashMap<>();
@@ -209,7 +215,11 @@ public class StructureUtility {
@Override
public boolean placeBlock(Object o, World world, int x, int y, int z, ItemStack trigger) {
world.setBlock(x, y, z, StructureLibAPI.getBlockHint(), 14, 2);
placeRemoveOrUpdate(
trigger,
() -> world.setBlock(x, y, z, StructureLibAPI.getBlockHint(), 14, 2),
() -> world.setBlock(x, y, z, StructureLibAPI.getBlockHint(), 14, 2),
() -> world.setBlock(x, y, z, Blocks.air, 0, 2));
return true;
}
@@ -222,11 +232,16 @@ public class StructureUtility {
@Override
public PlaceResult survivalPlaceBlock(Object o, World world, int x, int y, int z, ItemStack trigger,
AutoPlaceEnvironment env) {
if (check(o, world, x, y, z)) return PlaceResult.SKIP;
ItemConstructableTrigger.TriggerMode mode = ItemConstructableTrigger.getMode(trigger);
if (mode != REMOVING && check(o, world, x, y, z)) return PlaceResult.SKIP;
// user should place anything here.
// maybe make this configurable, but for now we try to take some cobble from user
if (env.getSource().takeOne(new ItemStack(Blocks.cobblestone), false)) {
world.setBlock(x, y, z, Blocks.cobblestone, 0, 2);
placeRemoveOrUpdate(
trigger,
() -> world.setBlock(x, y, z, Blocks.cobblestone, 0, 2),
() -> world.setBlock(x, y, z, Blocks.cobblestone, 0, 2),
() -> world.setBlock(x, y, z, Blocks.air, 0, 2));
}
return PlaceResult.REJECT;
}
@@ -333,27 +348,84 @@ public class StructureUtility {
public static PlaceResult survivalPlaceBlock(Block block, int meta, World world, int x, int y, int z, IItemSource s,
EntityPlayer actor, Consumer<IChatComponent> chatter) {
if (block == null) throw new NullPointerException();
if (!StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, actor)) return PlaceResult.REJECT;
ItemConstructableTrigger.TriggerMode mode = BUILDING;
if (actor.getHeldItem() != null && actor.getHeldItem().getItem() instanceof ItemConstructableTrigger) {
// best effort lookup if the item in the player's hand is a trigger, and if it is, get the mode from the
// ...trigger instead of assuming default behavior
mode = ItemConstructableTrigger.getMode(actor.getHeldItem());
}
if (mode == BUILDING && !StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, actor))
return PlaceResult.REJECT;
if (mode == UPDATING) {
if (!world.isAirBlock(x, y, z)) {
if (world.getTileEntity(x, y, z) instanceof TileEntity) {
// We're in updating mode and the block we're looking at might be important, so let's just leave it
// alone
// since it's probably not something we want to replace with something else
return PlaceResult.SKIP;
}
Block blockInWorld = world.getBlock(x, y, z);
if (blockInWorld.isAssociatedBlock(block) && blockInWorld.getDamageValue(world, x, y, z) == meta) {
// This is the block we are looking for, move along
return PlaceResult.SKIP;
}
}
}
Item itemBlock = Item.getItemFromBlock(block);
int itemMeta = itemBlock instanceof ISpecialItemBlock
? ((ISpecialItemBlock) itemBlock).getItemMetaFromBlockMeta(block, meta)
: meta;
ItemStack stack = new ItemStack(itemBlock, 1, itemMeta);
if (!s.takeOne(stack, true)) {
if (chatter != null) chatter.accept(
new ChatComponentTranslation("structurelib.autoplace.missing_block", stack.func_151000_E()));
return PlaceResult.REJECT;
}
if (block instanceof ICustomBlockSetting) {
ICustomBlockSetting blockCustom = (ICustomBlockSetting) block;
blockCustom.setBlock(world, x, y, z, meta);
} else if (!stack.copy()
.tryPlaceItemIntoWorld(actor, world, x, y, z, ForgeDirection.UP.ordinal(), 0.5f, 0.5f, 0.5f)) {
if (mode != REMOVING) {
if (!s.takeOne(stack, true)) {
if (chatter != null) chatter.accept(
new ChatComponentTranslation(
"structurelib.autoplace.error.no_simple_block",
stack.func_151000_E()));
return PlaceResult.REJECT;
}
if (block instanceof ICustomBlockSetting) {
ICustomBlockSetting blockCustom = (ICustomBlockSetting) block;
blockCustom.setBlock(world, x, y, z, meta);
} else {
if (mode == UPDATING && !world.isAirBlock(x, y, z) && checkPermission(world, actor, x, y, z)) {
// We're in updating mode and there's a block in our way (grr), so let's remove whatever is there
// ...and drop it to the player
ItemStack toGive = new ItemStack(world.getBlock(x, y, z));
toGive.setItemDamage(world.getBlock(x, y, z).getDamageValue(world, x, y, z));
if (!world.setBlock(x, y, z, Blocks.air, 0, 4) || !tryGiveOrDropItem(world, actor, toGive)) {
// We either weren't able to give the player the block that was there, or we weren't able to
// remove
// the block, either way, we need to reject the update. Also, since we hid the block update from
// the client in an effort to make the exchange as seamless as possible, we need to now notify
// the client if we did successfully remove the block.
if (world.isAirBlock(x, y, z)) world.notifyBlockChange(x, y, z, Blocks.air);
return PlaceResult.REJECT;
}
}
if (!stack.copy()
.tryPlaceItemIntoWorld(actor, world, x, y, z, ForgeDirection.UP.ordinal(), 0.5f, 0.5f, 0.5f)) {
return PlaceResult.REJECT;
}
if (!s.takeOne(stack, false)) {
// rollback
}
}
if (mode == REMOVING || !s.takeOne(stack, false)) {
// If we're in removing mode, or if the user didn't have the block in their inventory, and we're in some
// other mode, let's check some stuff
if (world.isAirBlock(x, y, z)) {
return PlaceResult.SKIP;
}
ItemStack toGive = new ItemStack(world.getBlock(x, y, z));
toGive.setItemDamage(world.getBlock(x, y, z).getDamageValue(world, x, y, z));
if (mode == REMOVING && !checkPermission(world, actor, x, y, z)) {
return PlaceResult.REJECT;
}
world.setBlockToAir(x, y, z);
if (mode == REMOVING && !tryGiveOrDropItem(world, actor, toGive)) {
// We're in removing mode and something went wrong while trying to give the player the block we removed,
// let's just fail for now
return PlaceResult.REJECT;
}
}
return PlaceResult.ACCEPT;
}
@@ -445,24 +517,82 @@ public class StructureUtility {
if (stack == null) throw new NullPointerException();
if (stack.stackSize != 1) throw new IllegalArgumentException();
if (!(stack.getItem() instanceof ItemBlock)) throw new IllegalArgumentException();
if (!StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, actor)) return PlaceResult.REJECT;
if (!assumeStackPresent && !s.takeOne(stack, true)) {
if (chatter != null) chatter.accept(
new ChatComponentTranslation("structurelib.autoplace.missing_block", stack.func_151000_E()));
return PlaceResult.REJECT;
ItemConstructableTrigger.TriggerMode mode = BUILDING;
if (actor.getHeldItem() != null && actor.getHeldItem().getItem() instanceof ItemConstructableTrigger) {
// best effort lookup if the item in the player's hand is a trigger, and if it is, get the mode from the
// ...trigger instead of assuming default behavior
mode = ItemConstructableTrigger.getMode(actor.getHeldItem());
}
if (!stack.copy().tryPlaceItemIntoWorld(actor, world, x, y, z, ForgeDirection.UP.ordinal(), 0.5f, 0.5f, 0.5f))
return PlaceResult.REJECT;
if (!s.takeOne(stack, false))
// this is bad! probably an exploit somehow. Let's nullify the block we just placed instead
if (mode != REMOVING) {
if (mode == BUILDING) {
if (!StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, actor)) {
return PlaceResult.SKIP;
}
}
if (mode == UPDATING) {
if (!world.isAirBlock(x, y, z)) {
if (world.getTileEntity(x, y, z) instanceof TileEntity) {
return PlaceResult.SKIP;
}
Block blockInWorld = world.getBlock(x, y, z);
ItemStack blockInWorldItem = new ItemStack(blockInWorld);
blockInWorldItem.setItemDamage(blockInWorld.getDamageValue(world, x, y, z));
if (blockInWorldItem.isItemEqual(stack)) {
// This is the block we are looking for, move along
return PlaceResult.SKIP;
}
}
}
if (!assumeStackPresent && !s.takeOne(stack, true)) {
if (chatter != null) chatter.accept(
new ChatComponentTranslation(
"structurelib.autoplace.error.no_item_stack",
stack.func_151000_E()));
return PlaceResult.REJECT;
}
if (mode == UPDATING && !world.isAirBlock(x, y, z) && checkPermission(world, actor, x, y, z)) {
// We're in updating mode and there's a block in our way (grr), so let's remove whatever is there
// ...and drop it to the player
ItemStack toGive = new ItemStack(world.getBlock(x, y, z));
toGive.setItemDamage(world.getBlock(x, y, z).getDamageValue(world, x, y, z));
if (!world.setBlock(x, y, z, Blocks.air, 0, 4) || !tryGiveOrDropItem(world, actor, toGive)) {
// We either weren't able to give the player the block that was there, or we weren't able to remove
// the block, either way, we need to reject the update. Also, since we hid the block update from
// the client in an effort to make the exchange as seamless as possible, we need to now notify
// the client if we did successfully remove the block.
if (world.isAirBlock(x, y, z)) world.notifyBlockChange(x, y, z, Blocks.air);
return PlaceResult.REJECT;
}
}
if (!stack.copy()
.tryPlaceItemIntoWorld(actor, world, x, y, z, ForgeDirection.UP.ordinal(), 0.5f, 0.5f, 0.5f)) {
return PlaceResult.REJECT;
}
}
if (mode == REMOVING || !s.takeOne(stack, false)) {
if (world.isAirBlock(x, y, z)) {
return PlaceResult.SKIP;
}
// If the user didn't have the item in their inventory, or we're in removing mode, we set it to air
ItemStack toGive = new ItemStack(world.getBlock(x, y, z));
toGive.setItemDamage(world.getBlock(x, y, z).getDamageValue(world, x, y, z));
if (mode == REMOVING && !checkPermission(world, actor, x, y, z)) {
return PlaceResult.REJECT;
}
world.setBlockToAir(x, y, z);
if (mode == REMOVING && !tryGiveOrDropItem(world, actor, toGive)) {
// We were in removing mode and something went wrong while trying to give the player the block we
// ...removed, let's just fail for now
return PlaceResult.REJECT;
}
}
return PlaceResult.ACCEPT;
}
/**
* Return a structure element that only allow air (or air equivalents, like Railcraft hidden blocks). You usually
* don't need to call this yourselves. Use {@code -} in shape to automatically use this. Provided nontheless in case
* you want this as a fallback to something else.
* don't need to call this yourselves. Use {@code -} in shape to automatically use this. Provided nonetheless in
* case you want this as a fallback to something else.
*/
@SuppressWarnings("unchecked")
public static <T> IStructureElement<T> isAir() {
@@ -471,8 +601,8 @@ public class StructureUtility {
/**
* Return a structure element that allow anything but air (or air equivalents, like Railcraft hidden blocks). You
* usually don't need to call this yourselves. Use {@code +} in shape to automatically use this. Provided nontheless
* in case you want this as a fallback to something else.
* usually don't need to call this yourselves. Use {@code +} in shape to automatically use this. Provided
* nonetheless in case you want this as a fallback to something else.
*/
@SuppressWarnings("unchecked")
public static <T> IStructureElement<T> notAir() {
@@ -638,7 +768,7 @@ public class StructureUtility {
* given tier, then player will be allowed to use multiple kinds of blocks, but their tier is guaranteed to be the
* same.
* <p>
* <h3>WARNING</h3> <b>You SHOULD NOT return notSet from your tierExtractor.</b> If you do so, we will have this
* <h4>WARNING</h4> <b>You SHOULD NOT return notSet from your tierExtractor.</b> If you do so, we will have this
* chain of events:
* <ul>
* <li>at check start, tier is reset to notSet
@@ -651,7 +781,7 @@ public class StructureUtility {
* doing its business
* <li>Player enjoy a (probably) hilariously shaped multi and (probably) reduced build cost.
* </ul>
* <h3>Example Implementation</h3>
* <h4>Example Implementation</h4>
* <p>
* Assume you have 16 tier, each map to one particular block's 16 different meta. You will usually want something
* like this
@@ -710,7 +840,7 @@ public class StructureUtility {
List<Pair<Block, Integer>> hints = allKnownTiers == null ? Collections.emptyList() : allKnownTiers;
if (hints.stream().anyMatch(Objects::isNull)) throw new IllegalArgumentException();
IStructureElementCheckOnly<T> check = ofBlocksTiered(tierExtractor, notSet, setter, getter);
return new StructureElement_Bridge<T>() {
return new StructureElement_Bridge<>() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
@@ -735,6 +865,11 @@ public class StructureUtility {
return Objects.equals(hintTier, worldTier);
}
private boolean isInAllKnownTiers(Block b) {
return allKnownTiers != null
&& allKnownTiers.stream().anyMatch(pair -> pair.getLeft().isAssociatedBlock(b));
}
@Override
public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
Pair<Block, Integer> hint = getHint(trigger);
@@ -767,13 +902,17 @@ public class StructureUtility {
@Override
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
AutoPlaceEnvironment env) {
ItemConstructableTrigger.TriggerMode mode = ItemConstructableTrigger.getMode(trigger);
Pair<Block, Integer> hint = getHint(trigger);
if (hint == null) return PlaceResult.REJECT; // TODO or SKIP?
Block block = world.getBlock(x, y, z);
int meta = world.getBlockMetadata(x, y, z);
TIER tier = tierExtractor.convert(block, meta);
if (Objects.equals(tier, tierExtractor.convert(hint.getKey(), hint.getValue())))
if (mode != REMOVING && (Objects.equals(tier, tierExtractor.convert(hint.getKey(), hint.getValue()))))
return PlaceResult.SKIP;
if (mode == REMOVING && (!isInAllKnownTiers(block) && !world.isAirBlock(x, y, z))) {
return PlaceResult.REJECT;
}
return StructureUtility.survivalPlaceBlock(
hint.getKey(),
hint.getValue(),
@@ -862,7 +1001,9 @@ public class StructureUtility {
@Override
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
AutoPlaceEnvironment env) {
if (check(t, world, x, y, z)) return PlaceResult.SKIP;
ItemConstructableTrigger.TriggerMode mode = ItemConstructableTrigger.getMode(trigger);
PlaceResult skipOrRejectBasedOnCheck = checkForRemoving(mode, check(t, world, x, y, z), world, x, y, z);
if (skipOrRejectBasedOnCheck != PlaceResult.ACCEPT) return skipOrRejectBasedOnCheck;
if (getBlock() == null) return PlaceResult.REJECT;
return StructureUtility.survivalPlaceBlock(
getBlock(),
@@ -949,7 +1090,9 @@ public class StructureUtility {
@Deprecated
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
IItemSource s, EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
if (check(t, world, x, y, z)) return PlaceResult.SKIP;
ItemConstructableTrigger.TriggerMode mode = ItemConstructableTrigger.getMode(trigger);
PlaceResult skipOrRejectBasedOnCheck = checkForRemoving(mode, check(t, world, x, y, z), world, x, y, z);
if (skipOrRejectBasedOnCheck != PlaceResult.ACCEPT) return skipOrRejectBasedOnCheck;
if (init()) return StructureUtility.survivalPlaceBlock(block, meta, world, x, y, z, s, actor, chatter);
return fallback.survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, chatter);
}
@@ -957,7 +1100,9 @@ public class StructureUtility {
@Override
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
AutoPlaceEnvironment env) {
if (check(t, world, x, y, z)) return PlaceResult.SKIP;
ItemConstructableTrigger.TriggerMode mode = ItemConstructableTrigger.getMode(trigger);
PlaceResult skipOrRejectBasedOnCheck = checkForRemoving(mode, check(t, world, x, y, z), world, x, y, z);
if (skipOrRejectBasedOnCheck != PlaceResult.ACCEPT) return skipOrRejectBasedOnCheck;
if (init()) return StructureUtility.survivalPlaceBlock(
block,
meta,
@@ -1412,53 +1557,6 @@ public class StructureUtility {
} else {
return new IStructureElement<T>() {
@Override
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
AutoPlaceEnvironment env) {
BlocksToPlace e = getBlocksToPlace(t, world, x, y, z, trigger, env);
IItemSource source = env.getSource();
EntityPlayer actor = env.getActor();
Consumer<IChatComponent> chatter = env.getChatter();
if (check(t, world, x, y, z)) return PlaceResult.SKIP;
if (e.getStacks() == null) {
ItemStack taken = source.takeOne(e.getPredicate(), true);
return StructureUtility.survivalPlaceBlock(
taken,
NBTMode.EXACT,
taken.stackTagCompound,
false,
world,
x,
y,
z,
source,
actor,
chatter);
}
for (ItemStack stack : e.getStacks()) {
if (!source.takeOne(stack, true)) {
IChatComponent name = new ChatComponentText(stack.getDisplayName());
name.setChatStyle(new ChatStyle().setColor(EnumChatFormatting.YELLOW));
env.getChatter()
.accept(new ChatComponentTranslation("structurelib.autoplace.missing_block", name));
continue;
}
return StructureUtility.survivalPlaceBlock(
stack,
NBTMode.EXACT,
stack.stackTagCompound,
false,
world,
x,
y,
z,
source,
actor,
chatter);
}
return PlaceResult.REJECT;
}
@Override
public boolean check(T t, World world, int x, int y, int z) {
Block worldBlock = world.getBlock(x, y, z);
@@ -1632,8 +1730,15 @@ public class StructureUtility {
@Deprecated
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
AutoPlaceEnvironment env) {
if (world.getBlock(x, y, z) == defaultBlock && world.getBlockMetadata(x, y, z) == defaultMeta)
return PlaceResult.SKIP;
ItemConstructableTrigger.TriggerMode mode = ItemConstructableTrigger.getMode(trigger);
PlaceResult skipOrRejectBasedOnCheck = checkForRemoving(
mode,
world.getBlock(x, y, z) == defaultBlock && world.getBlockMetadata(x, y, z) == defaultMeta,
world,
x,
y,
z);
if (skipOrRejectBasedOnCheck != PlaceResult.ACCEPT) return skipOrRejectBasedOnCheck;
return StructureUtility.survivalPlaceBlock(
defaultBlock,
defaultMeta,
@@ -1678,8 +1783,15 @@ public class StructureUtility {
@Deprecated
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
AutoPlaceEnvironment env) {
if (world.getBlock(x, y, z) == defaultBlock && world.getBlockMetadata(x, y, z) == defaultMeta)
return PlaceResult.SKIP;
ItemConstructableTrigger.TriggerMode mode = ItemConstructableTrigger.getMode(trigger);
PlaceResult skipOrRejectBasedOnCheck = checkForRemoving(
mode,
world.getBlock(x, y, z) == defaultBlock && world.getBlockMetadata(x, y, z) == defaultMeta,
world,
x,
y,
z);
if (skipOrRejectBasedOnCheck != PlaceResult.ACCEPT) return skipOrRejectBasedOnCheck;
return StructureUtility.survivalPlaceBlock(
defaultBlock,
defaultMeta,
@@ -1938,6 +2050,7 @@ public class StructureUtility {
@Deprecated
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
IItemSource s, EntityPlayerMP actor, Consumer<IChatComponent> chatter) {
ItemConstructableTrigger.TriggerMode mode = ItemConstructableTrigger.getMode(trigger);
if (predicate.test(t))
return downstream.survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, chatter);
return placeResultWhenDisabled;
@@ -1946,6 +2059,7 @@ public class StructureUtility {
@Override
public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
AutoPlaceEnvironment env) {
ItemConstructableTrigger.TriggerMode mode = ItemConstructableTrigger.getMode(trigger);
if (predicate.test(t)) return downstream.survivalPlaceBlock(t, world, x, y, z, trigger, env);
return placeResultWhenDisabled;
}
@@ -3313,4 +3427,57 @@ public class StructureUtility {
}
return shape;
}
/**
* Performs a creation, removal, or update operation at the given position
*
* @param trigger ItemStack for the trigger item
* @param onCreate Operation to be performed when the mode of the trigger is set to building mode
* @param onUpdate Operation to be performed when the mode of the trigger is set to updating mode
* @param onRemove Operation to be performed when the mode of the trigger is set to removing mode
*/
public static void placeRemoveOrUpdate(ItemStack trigger, Runnable onCreate, Runnable onUpdate, Runnable onRemove) {
ItemConstructableTrigger.TriggerMode mode = ItemConstructableTrigger.getMode(trigger);
switch (mode) {
case BUILDING:
onCreate.run();
break;
case UPDATING:
onUpdate.run();
break;
case REMOVING:
onRemove.run();
break;
}
}
public static boolean tryGiveOrDropItem(World world, EntityPlayer actor, ItemStack stack) {
if (actor.inventory.addItemStackToInventory(stack)) {
actor.inventoryContainer.detectAndSendChanges();
return true;
}
EntityItem toSpawn = new EntityItem(world, actor.posX, actor.posY, actor.posZ, stack);
// For some reason these things sometimes spawn with the velocity of an F1 car, so let's prevent that
toSpawn.motionX = 0;
toSpawn.motionY = 0;
toSpawn.motionZ = 0;
return world.spawnEntityInWorld(toSpawn);
}
public static boolean checkPermission(World world, EntityPlayer actor, int x, int y, int z) {
boolean res = true;
if (isSULoaded) {
res = ServerUtilitiesCompat.checkPermission(world, actor, x, z);
}
res = res && !world.getBlock(x, y, z).isAssociatedBlock(Blocks.bedrock);
// maybe check mining level who knows :shrug:
return res;
}
public static PlaceResult checkForRemoving(ItemConstructableTrigger.TriggerMode mode, boolean checkResult,
World world, int x, int y, int z) {
if (mode != REMOVING && checkResult) return PlaceResult.SKIP;
else if (mode != BUILDING && !checkResult && !world.isAirBlock(x, y, z)) return PlaceResult.REJECT;
return PlaceResult.ACCEPT;
}
}
@@ -0,0 +1,29 @@
package com.gtnewhorizon.structurelib.xmod;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.world.World;
import cpw.mods.fml.common.Optional;
import serverutils.data.ClaimedChunks;
import serverutils.lib.data.ForgePlayer;
import serverutils.lib.data.ForgeTeam;
import serverutils.lib.data.Universe;
import serverutils.lib.math.ChunkDimPos;
public class ServerUtilitiesCompat {
@Optional.Method(modid = "serverutilities")
public static boolean checkPermission(World world, EntityPlayer actor, int x, int z) {
final ForgeTeam owningTeam = ClaimedChunks.instance
.getChunkTeam(new ChunkDimPos(x >> 4, z >> 4, world.provider.dimensionId));
if (owningTeam == null) {
return true;
}
final ForgePlayer currentPlayer = new ForgePlayer(
Universe.get(),
actor.getUniqueID(),
actor.getCommandSenderName());
return owningTeam.isAlly(currentPlayer) || owningTeam.isMember(currentPlayer);
}
}
@@ -22,6 +22,9 @@ item.structurelib.frontRotationTool.desc.1=Rotates only the front panel,
item.structurelib.frontRotationTool.desc.2=which allows structure rotation.
item.structurelib.constructableTrigger.name=Multiblock Structure Hologram Projector
item.structurelib.constructableTrigger.modes.0=Building Mode
item.structurelib.constructableTrigger.modes.1=Updating Mode
item.structurelib.constructableTrigger.modes.2=Removing Mode
item.structurelib.constructableTrigger.desc.0=Triggers Constructable Interface
item.structurelib.constructableTrigger.desc.1=Shows multiblock construction schematic,
item.structurelib.constructableTrigger.desc.2=use on the multiblock controller.
@@ -34,11 +37,17 @@ item.structurelib.constructableTrigger.gui.title=Sub Channels
item.structurelib.constructableTrigger.gui.key=Key:
item.structurelib.constructableTrigger.gui.value=Value:
item.structurelib.constructableTrigger.gui.add=Add
item.structurelib.constructableTrigger.gui.set=Update
item.structurelib.constructableTrigger.gui.unset=Remove
item.structurelib.constructableTrigger.gui.wipe=§4Clear
item.structurelib.constructableTrigger.gui.set=Set
item.structurelib.constructableTrigger.gui.unset=Unset
item.structurelib.constructableTrigger.gui.wipe=§4Wipe
item.structurelib.constructableTrigger.gui.error.show=Show Errors
item.structurelib.constructableTrigger.gui.error.hide=Hide Errors
item.structurelib.constructableTrigger.gui.gt_no_hatch.enable=No Hatch
item.structurelib.constructableTrigger.gui.gt_no_hatch.disable=Hatches
item.structurelib.constructableTrigger.keyCategory=Multiblock Structure Hologram Projector Keys
item.structurelib.constructableTrigger.keys.cycleMode=Cycle Projector Mode
item.structurelib.constructableTrigger.gui.channels.from=§rMod: §9§o%s
item.structurelib.constructableTrigger.gui.info=Many multiblocks support some degree of variation in their structure and a sub channel allows indication of which variant to use.\n All sub channels have a sequence of lowercase characters as key and a positive integer as value. Most known sub channels should be available as typeahead completions.\n You can also drag and drop items from NEI to set the corresponding sub channel value. These items all have a tooltip of "§oIndicates§r §ovalue§r §oX§r §ofor§r §osub§r §ochannel§r §oY§r".
item.structurelib.constructableTrigger.gui.info=Many multiblocks support some degree of variation in its structure and use sub channels to indicate which variant to use.\n Sub channels are has a sequence of lower case characters as key and a positive integer as value. Most known sub channels should be available as typeahead completions.\nYou can also drag and drop items from NEI to set the corresponding sub channel value. These items all have a tooltip of §oIndicates value X for sub channel Y§r.
channels.structurelib.show_errors=When set, will highlight parts of the multiblock that is definitely wrong
@@ -51,7 +60,6 @@ structurelib.autoplace.error.not_enabled=§cThis multiblock does not support aut
structurelib.autoplace.error.no_simple_block=§cSuggested to place §r%s§c but none was found
structurelib.autoplace.error.no_item_stack=§cSuggested to place block by using §r%s§c but none was found
structurelib.autoplace.warning.no_explicit_channel=§6This structure depends on nonexistent sub channel §r%s§6. Using master channel instead.
structurelib.autoplace.missing_block=§cMissing block: %s
structurelib.blockhint.desc.0=Helps while building
structurelib.blockhint.0.name=Hint 1 dot
Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 B

After

Width:  |  Height:  |  Size: 273 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 154 B

After

Width:  |  Height:  |  Size: 279 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 B

After

Width:  |  Height:  |  Size: 296 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 160 B

After

Width:  |  Height:  |  Size: 301 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 137 B

After

Width:  |  Height:  |  Size: 282 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 B

After

Width:  |  Height:  |  Size: 281 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 B

After

Width:  |  Height:  |  Size: 285 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 141 B

After

Width:  |  Height:  |  Size: 289 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 B

After

Width:  |  Height:  |  Size: 293 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 138 B

After

Width:  |  Height:  |  Size: 295 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 B

After

Width:  |  Height:  |  Size: 296 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 143 B

After

Width:  |  Height:  |  Size: 297 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 99 B

After

Width:  |  Height:  |  Size: 255 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 132 B

After

Width:  |  Height:  |  Size: 301 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 B

After

Width:  |  Height:  |  Size: 306 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 B

After

Width:  |  Height:  |  Size: 281 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 276 B

After

Width:  |  Height:  |  Size: 367 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 386 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 392 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 365 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 606 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 361 B

After

Width:  |  Height:  |  Size: 439 B

@@ -1,102 +0,0 @@
package com.gtnewhorizon.structurelib.coords;
import java.util.Arrays;
import java.util.Collections;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
import com.gtnewhorizon.structurelib.structure.IStructureDefinition;
import com.gtnewhorizon.structurelib.structure.StructureUtility;
// spotless:off
class CoordinateTests {
private IStructureDefinition<?> getDefinition() {
String[][] main = {
{ " ", " ", " ", " ", " " },
{ " ", " ", " ", " ", " " },
{ " ", " ", " X ", " ", " " },
{ " ", " ", " YY ", " ", " " },
{ " ~ ", " ", " ", " ", " " }
};
return IStructureDefinition.builder()
.addShape("main", StructureUtility.transpose(main))
.addSocket('X', ' ')
.addSocket('Y', ' ')
.build();
}
@Test
void testSocketAndCoordSystemTranslationToWorld() {
IStructureDefinition<?> def = getDefinition();
// Note: the position is the same object between all of these transforms, it is never copied, its generic just
// changes
var p = def.getSocket("main", 'X');
Assertions.assertEquals(new Position<>(2, 2, 2), p);
var p2 = def.getCoordinateSystem("main").translateInverse(p);
Assertions.assertEquals(new Position<>(0, -2, 2), p2);
var p3 = ExtendedFacing.NORTH_NORMAL_NONE.asCoordinateSystem().translateInverse(p2);
Assertions.assertEquals(new Position<>(0, 2, 2), p3);
var p4 = ControllerRelativeCoords.translateInverse(p3, 5, 5, 5);
Assertions.assertEquals(new Position<>(5, 7, 7), p4);
}
@Test
void testSocketAndCoordSystemTranslationToStructureDef() {
IStructureDefinition<?> def = getDefinition();
// Note: the position is the same object between all of these transforms, it is never copied, its generic just
// changes
var p = new Position<WorldCoords>(5, 7, 7);
var p2 = ControllerRelativeCoords.translate(p, 5, 5, 5);
Assertions.assertEquals(new Position<>(0, 2, 2), p2);
var p3 = ExtendedFacing.NORTH_NORMAL_NONE.asCoordinateSystem().translate(p2);
Assertions.assertEquals(new Position<>(0, -2, 2), p3);
var p4 = def.getCoordinateSystem("main").translate(p3);
Assertions.assertEquals(new Position<>(2, 2, 2), p4);
Assertions.assertEquals(def.getSocket("main", 'X'), p4);
}
@Test
void testSocketGetters() {
IStructureDefinition<?> def = getDefinition();
Assertions.assertThrows(
IllegalArgumentException.class,
() -> def.getSocket("invalid", 'X'));
Assertions.assertThrows(
IllegalStateException.class,
() -> def.getSocket("main", 'Y'));
Assertions.assertThrows(
IllegalArgumentException.class,
() -> def.getSocket("main", 'Z'));
Assertions.assertThrows(
IllegalArgumentException.class,
() -> def.getAllSockets("invalid", 'X'));
Assertions.assertEquals(
Collections.emptyList(),
def.getAllSockets("main", 'Z'));
Assertions.assertEquals(
Arrays.asList(new Position<>(2, 2, 2)),
def.getAllSockets("main", 'X'));
Assertions.assertEquals(
Arrays.asList(new Position<>(2, 3, 2), new Position<>(3, 3, 2)),
def.getAllSockets("main", 'Y'));
}
}
// spotless:on