From b2b39c3fa552bd2f76bd8a97da28d742a03c69a2 Mon Sep 17 00:00:00 2001
From: Glease <4586901+Glease@users.noreply.github.com>
Date: Sun, 11 Sep 2022 02:54:11 +0800
Subject: [PATCH] Use argument object for survival autoplace
---
addon.gradle | 7 +
dependencies.gradle | 3 +
.../structurelib/StructureLibAPI.java | 15 +-
.../constructable/ConstructableUtility.java | 4 +-
.../constructable/ISurvivalConstructable.java | 68 ++-
.../alignment/enumerable/Direction.java | 12 +-
.../alignment/enumerable/ExtendedFacing.java | 22 +
.../structure/AutoPlaceEnvironment.java | 218 ++++++++
.../DefaultSurvivalBuildEnvironment.java | 23 +
.../structure/IStructureDefinition.java | 87 ++-
.../structure/IStructureElement.java | 30 +-
.../structure/IStructureElementChain.java | 24 +
.../structure/IStructureElementCheckOnly.java | 6 +
.../IStructureElementNoPlacement.java | 6 +
.../structure/IStructureNavigate.java | 6 +
.../structure/IStructureWalker.java | 23 +-
.../structure/ISurvivalBuildEnvironment.java | 35 ++
.../structure/LazyStructureElement.java | 6 +
.../structure/StructureDefinition.java | 46 +-
.../structure/StructureElement_Bridge.java | 32 ++
.../structure/StructureUtility.java | 514 +++++++++++++-----
.../SurvivalBuildStructureWalker.java | 43 +-
.../structurelib/util/Vec3Impl.java | 12 +
.../enumerable/ExtendedFacingTest.java | 24 +
24 files changed, 1083 insertions(+), 183 deletions(-)
create mode 100644 src/main/java/com/gtnewhorizon/structurelib/structure/AutoPlaceEnvironment.java
create mode 100644 src/main/java/com/gtnewhorizon/structurelib/structure/DefaultSurvivalBuildEnvironment.java
create mode 100644 src/main/java/com/gtnewhorizon/structurelib/structure/ISurvivalBuildEnvironment.java
create mode 100644 src/main/java/com/gtnewhorizon/structurelib/structure/StructureElement_Bridge.java
create mode 100644 src/test/java/com/gtnewhorizon/structurelib/alignment/enumerable/ExtendedFacingTest.java
diff --git a/addon.gradle b/addon.gradle
index a985e11a..ee3a49ec 100644
--- a/addon.gradle
+++ b/addon.gradle
@@ -22,3 +22,10 @@ task javadocJar(type: Jar, dependsOn: javadoc) {
artifacts {
archives javadocJar
}
+
+test {
+ useJUnitPlatform()
+ testLogging {
+ events "passed", "skipped", "failed"
+ }
+}
diff --git a/dependencies.gradle b/dependencies.gradle
index f129e37c..5c7b33e8 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -3,4 +3,7 @@
dependencies {
compile("com.github.GTNewHorizons:GTNHLib:0.0.1:dev")
runtime("com.github.GTNewHorizons:NotEnoughItems:2.2.20-GTNH:dev")
+
+ testImplementation(platform('org.junit:junit-bom:5.8.2'))
+ testImplementation('org.junit.jupiter:junit-jupiter')
}
diff --git a/src/main/java/com/gtnewhorizon/structurelib/StructureLibAPI.java b/src/main/java/com/gtnewhorizon/structurelib/StructureLibAPI.java
index 1c28f51d..0164289e 100644
--- a/src/main/java/com/gtnewhorizon/structurelib/StructureLibAPI.java
+++ b/src/main/java/com/gtnewhorizon/structurelib/StructureLibAPI.java
@@ -6,7 +6,7 @@ import com.gtnewhorizon.structurelib.alignment.IAlignment;
import com.gtnewhorizon.structurelib.alignment.IAlignmentProvider;
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
import com.gtnewhorizon.structurelib.net.AlignmentMessage;
-import com.gtnewhorizon.structurelib.structure.IItemSource;
+import com.gtnewhorizon.structurelib.structure.AutoPlaceEnvironment;
import cpw.mods.fml.common.network.NetworkRegistry;
import net.minecraft.block.Block;
import net.minecraft.entity.player.EntityPlayer;
@@ -219,9 +219,20 @@ public class StructureLibAPI {
* Determines if given block can be replaced without much effort. The exact predicate clauses is not stable and
* will be changed, but the general idea will always stay the same.
*
- * Use this in your {@link com.gtnewhorizon.structurelib.structure.IStructureElement#survivalPlaceBlock(Object, World, int, int, int, ItemStack, IItemSource, EntityPlayerMP, java.util.function.Consumer)}
+ * Use this in your {@link com.gtnewhorizon.structurelib.structure.IStructureElement#survivalPlaceBlock(Object, World, int, int, int, ItemStack, AutoPlaceEnvironment)}
*/
+ @Deprecated
public static boolean isBlockTriviallyReplaceable(World w, int x, int y, int z, EntityPlayerMP actor) {
+ return isBlockTriviallyReplaceable(w, x, y, z, (EntityPlayer) actor);
+ }
+
+ /**
+ * Determines if given block can be replaced without much effort. The exact predicate clauses is not stable and
+ * will be changed, but the general idea will always stay the same.
+ *
+ * Use this in your {@link com.gtnewhorizon.structurelib.structure.IStructureElement#survivalPlaceBlock(Object, World, int, int, int, ItemStack, AutoPlaceEnvironment)}
+ */
+ public static boolean isBlockTriviallyReplaceable(World w, int x, int y, int z, EntityPlayer actor) {
// TODO extend this function a bit
Block block = w.getBlock(x, y, z);
return block.isAir(w, x, y, z) || block.isReplaceable(w, x, y, z);
diff --git a/src/main/java/com/gtnewhorizon/structurelib/alignment/constructable/ConstructableUtility.java b/src/main/java/com/gtnewhorizon/structurelib/alignment/constructable/ConstructableUtility.java
index 5b2c7527..d4f9e0f7 100644
--- a/src/main/java/com/gtnewhorizon/structurelib/alignment/constructable/ConstructableUtility.java
+++ b/src/main/java/com/gtnewhorizon/structurelib/alignment/constructable/ConstructableUtility.java
@@ -6,6 +6,7 @@ import com.gtnewhorizon.structurelib.StructureLibAPI;
import com.gtnewhorizon.structurelib.alignment.IAlignment;
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
import com.gtnewhorizon.structurelib.structure.IItemSource;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
import java.util.WeakHashMap;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
@@ -78,8 +79,7 @@ public class ConstructableUtility {
.survivalConstruct(
aStack,
ConfigurationHandler.INSTANCE.getAutoPlaceBudget(),
- IItemSource.fromPlayer(playerMP),
- playerMP);
+ ISurvivalBuildEnvironment.create(IItemSource.fromPlayer(playerMP), playerMP));
if (built > 0) {
playerMP.addChatMessage(new ChatComponentTranslation("structurelib.autoplace.built_stat", built));
} else if (built == -1) {
diff --git a/src/main/java/com/gtnewhorizon/structurelib/alignment/constructable/ISurvivalConstructable.java b/src/main/java/com/gtnewhorizon/structurelib/alignment/constructable/ISurvivalConstructable.java
index 2d63e2f0..1268081c 100644
--- a/src/main/java/com/gtnewhorizon/structurelib/alignment/constructable/ISurvivalConstructable.java
+++ b/src/main/java/com/gtnewhorizon/structurelib/alignment/constructable/ISurvivalConstructable.java
@@ -1,17 +1,79 @@
package com.gtnewhorizon.structurelib.alignment.constructable;
+import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
+import com.gtnewhorizon.structurelib.structure.AutoPlaceEnvironment;
import com.gtnewhorizon.structurelib.structure.IItemSource;
+import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment;
+import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.item.ItemStack;
import net.minecraft.world.World;
+/**
+ * An extension to {@link IConstructable}. Tile Entities (or delegates returned by {@link IConstructableProvider#getConstructable()}
+ * should implement this interface to allow third party tools and StructureLib builtin constructable tools to trigger
+ * autoplace in survival mode.
+ *
+ * While not originally planned, a creative player can also use this interface to trigger autoplace by using an
+ * {@link IItemSource} with every possible items in the game.
+ */
public interface ISurvivalConstructable extends IConstructable {
+ /**
+ * End player might want to disable this hack if it turns out to be too crashy
+ */
+ boolean DISABLE_HACKY_MIGRATION_CODE = Boolean.getBoolean("structurelib.disable_isc_migration_hack");
+
/**
* Construct the structure using {@link com.gtnewhorizon.structurelib.structure.IStructureElement#survivalPlaceBlock(Object, World, int, int, int, ItemStack, IItemSource, EntityPlayerMP, java.util.function.Consumer)}
+ * or {@link com.gtnewhorizon.structurelib.structure.IStructureDefinition#survivalBuild(Object, ItemStack, String, World, ExtendedFacing, int, int, int, int, int, int, int, IItemSource, EntityPlayerMP, boolean)}
*
- * @param elementBudget The server configured element budget. You can tune this up a bit if the structure is too big, but
- * generally should not be a 4 digits number to not overwhelm the server
+ * @param elementBudget The server configured element budget. The implementor can choose to tune this up a bit if
+ * the structure is too big, but generally should not be a 4 digits number to not overwhelm the
+ * server
* @return -1 if done, otherwise number of elements placed this round
*/
- int survivalConstruct(ItemStack stackSize, int elementBudget, IItemSource source, EntityPlayerMP actor);
+ @Deprecated
+ default int survivalConstruct(ItemStack stackSize, int elementBudget, IItemSource source, EntityPlayerMP actor) {
+ return survivalConstruct(stackSize, elementBudget, ISurvivalBuildEnvironment.create(source, actor));
+ }
+
+ /**
+ * Construct the structure using {@link com.gtnewhorizon.structurelib.structure.IStructureElement#survivalPlaceBlock(Object, World, int, int, int, ItemStack, AutoPlaceEnvironment)}
+ * or {@link com.gtnewhorizon.structurelib.structure.IStructureDefinition#survivalBuild(Object, ItemStack, String, World, ExtendedFacing, int, int, int, int, int, int, int, ISurvivalBuildEnvironment, boolean)}
+ *
+ * @param elementBudget The server configured element budget. The implementor can choose to tune this up a bit if
+ * the structure is too big, but generally should not be a 4 digits number to not overwhelm the
+ * server
+ * @return -1 if done, -2 if not supported (e.g. due to an incompatible change in the API of env, otherwise number
+ * of elements placed this round
+ */
+ default int survivalConstruct(ItemStack stackSize, int elementBudget, ISurvivalBuildEnvironment env) {
+ EntityPlayer actor = env.getActor();
+ if (actor instanceof EntityPlayerMP)
+ return survivalConstruct(stackSize, elementBudget, env.getSource(), (EntityPlayerMP) actor);
+ else if (!DISABLE_HACKY_MIGRATION_CODE && __get_player() == null) {
+ // as far as I know, no implementor would actually do anything to actor beyond passing it down to
+ // IStructureDefinition, so it's probably fine to proceed like this
+ GlobalStates.entityPlayer.set(env.getActor());
+ try {
+ return survivalConstruct(stackSize, elementBudget, env.getSource(), null);
+ } finally {
+ GlobalStates.entityPlayer.remove();
+ }
+ }
+ return -2;
+ }
+
+
+ /**
+ * Internal hack. Do not use.
+ */
+ @Deprecated
+ static EntityPlayer __get_player() {
+ return GlobalStates.entityPlayer.get();
+ }
+}
+
+class GlobalStates {
+ public static final ThreadLocal entityPlayer = new ThreadLocal<>();
}
diff --git a/src/main/java/com/gtnewhorizon/structurelib/alignment/enumerable/Direction.java b/src/main/java/com/gtnewhorizon/structurelib/alignment/enumerable/Direction.java
index 4352cf08..eaae4067 100644
--- a/src/main/java/com/gtnewhorizon/structurelib/alignment/enumerable/Direction.java
+++ b/src/main/java/com/gtnewhorizon/structurelib/alignment/enumerable/Direction.java
@@ -1,6 +1,10 @@
package com.gtnewhorizon.structurelib.alignment.enumerable;
import com.gtnewhorizon.structurelib.util.Vec3Impl;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
import net.minecraftforge.common.util.ForgeDirection;
public enum Direction {
@@ -14,10 +18,12 @@ public enum Direction {
private final ForgeDirection forgeDirection;
private final Vec3Impl axisVector;
public static final Direction[] VALUES = values();
+ private static final Map reverseMap =
+ Arrays.stream(VALUES).collect(Collectors.toMap(d -> d.axisVector, Function.identity()));
Direction(ForgeDirection forgeDirection) {
this.forgeDirection = forgeDirection;
- axisVector = new Vec3Impl(forgeDirection.offsetX, forgeDirection.offsetY, forgeDirection.offsetZ);
+ axisVector = Vec3Impl.getFromPool(forgeDirection.offsetX, forgeDirection.offsetY, forgeDirection.offsetZ);
}
public ForgeDirection getForgeDirection() {
@@ -31,4 +37,8 @@ public enum Direction {
public static Vec3Impl getAxisVector(ForgeDirection forgeDirection) {
return VALUES[forgeDirection.ordinal()].axisVector;
}
+
+ public static Direction getByAxisVector(Vec3Impl vec3) {
+ return reverseMap.get(vec3);
+ }
}
diff --git a/src/main/java/com/gtnewhorizon/structurelib/alignment/enumerable/ExtendedFacing.java b/src/main/java/com/gtnewhorizon/structurelib/alignment/enumerable/ExtendedFacing.java
index 59156d68..ec76db85 100644
--- a/src/main/java/com/gtnewhorizon/structurelib/alignment/enumerable/ExtendedFacing.java
+++ b/src/main/java/com/gtnewhorizon/structurelib/alignment/enumerable/ExtendedFacing.java
@@ -354,6 +354,28 @@ public enum ExtendedFacing {
integerAxisSwap.inverseTranslate(point, out);
}
+ /**
+ * do note the ABC coordinate system is (X, -Y, Z).
+ */
+ public ForgeDirection getWorldDirection(ForgeDirection abcDirection) {
+ switch (abcDirection) {
+ case NORTH:
+ return getRelativeForwardInWorld();
+ case SOUTH:
+ return getRelativeBackInWorld();
+ case UP:
+ return getRelativeDownInWorld();
+ case DOWN:
+ return getRelativeUpInWorld();
+ case EAST:
+ return getRelativeLeftInWorld();
+ case WEST:
+ return getRelativeRightInWorld();
+ default:
+ return ForgeDirection.UNKNOWN;
+ }
+ }
+
/**
* Translates world offset to relative front facing offset
*
diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/AutoPlaceEnvironment.java b/src/main/java/com/gtnewhorizon/structurelib/structure/AutoPlaceEnvironment.java
new file mode 100644
index 00000000..2d834b51
--- /dev/null
+++ b/src/main/java/com/gtnewhorizon/structurelib/structure/AutoPlaceEnvironment.java
@@ -0,0 +1,218 @@
+package com.gtnewhorizon.structurelib.structure;
+
+import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
+import java.util.Map;
+import java.util.function.Consumer;
+import java.util.function.Predicate;
+import javax.annotation.Nonnull;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.IChatComponent;
+
+/**
+ * Represent the environment in which autoplace of a single element took place.
+ */
+public class AutoPlaceEnvironment {
+ private IItemSource source;
+ private final EntityPlayer actor;
+ private final Consumer chatter;
+ private final IStructureDefinition> definition;
+ private final String piece;
+ private final ExtendedFacing facing;
+ final int[] offsetABC;
+ private final int[] baseOffsetABC;
+
+ public static AutoPlaceEnvironment fromLegacy(
+ IItemSource source, EntityPlayerMP actor, Consumer chatter) {
+ if (source instanceof WrappedIItemSource) {
+ AutoPlaceEnvironment original = ((WrappedIItemSource) source).container;
+ // feels like this is extremely likely to cause issues, but this does allow us to recover lost info.
+ // TODO probably should remove this once all addons migrate over
+ if (actor != original.actor || chatter != original.chatter) {
+ AutoPlaceEnvironment newEnv = original;
+ if (actor != original.actor) newEnv = newEnv.withActor(actor);
+ if (chatter != original.chatter) newEnv = newEnv.withChatter(chatter);
+ return newEnv;
+ }
+ return original;
+ }
+ return new AutoPlaceEnvironment(source, actor, chatter, null, null, null, null, null);
+ }
+
+ AutoPlaceEnvironment(
+ EntityPlayer actor,
+ Consumer chatter,
+ IStructureDefinition> definition,
+ String piece,
+ ExtendedFacing facing,
+ int[] baseOffsetABC) {
+ this.source = null;
+ this.actor = actor;
+ this.chatter = chatter;
+ this.definition = definition;
+ this.piece = piece;
+ this.facing = facing;
+ this.offsetABC = new int[3];
+ this.baseOffsetABC = baseOffsetABC;
+ }
+
+ AutoPlaceEnvironment(
+ IItemSource source,
+ EntityPlayer actor,
+ Consumer chatter,
+ IStructureDefinition> definition,
+ String piece,
+ ExtendedFacing facing,
+ int[] offsetABC,
+ int[] baseOffsetABC) {
+ this.source = definition != null && !(source instanceof WrappedIItemSource)
+ ? new WrappedIItemSource(this, source)
+ : source;
+ this.actor = actor;
+ this.chatter = chatter;
+ this.definition = definition;
+ this.piece = piece;
+ this.facing = facing;
+ this.offsetABC = offsetABC;
+ this.baseOffsetABC = baseOffsetABC;
+ }
+
+ protected AutoPlaceEnvironment(AutoPlaceEnvironment parent) {
+ this(
+ parent.getSource(),
+ parent.getActor(),
+ parent.getChatter(),
+ parent.definition,
+ parent.piece,
+ parent.facing,
+ parent.offsetABC,
+ parent.baseOffsetABC);
+ }
+
+ void setSource(IItemSource source) {
+ this.source = definition != null && !(source instanceof WrappedIItemSource)
+ ? new WrappedIItemSource(this, source)
+ : source;
+ }
+
+ public APILevel getAPILevel() {
+ return definition == null ? APILevel.Legacy : APILevel.V2;
+ }
+
+ /**
+ * From where survival autoplace will drain resources.
+ */
+ public IItemSource getSource() {
+ return source;
+ }
+
+ /**
+ * The initiator of actions. for very critical errors you can also just send the error messages here, bypassing
+ * any filter that {@link #getChatter()} might have. You might want to use
+ * {@link com.gtnewhorizon.structurelib.StructureLibAPI#addThrottledChat(Object, EntityPlayer, IChatComponent, short)}
+ * to help reduce spam.
+ */
+ public EntityPlayer getActor() {
+ return actor;
+ }
+
+ /**
+ * send error messages here. Caller will choose an appropriate way to forward it to player if the other fallbacks
+ * also fails.
+ */
+ public Consumer getChatter() {
+ return chatter;
+ }
+
+ /**
+ * Test if given location is contained within the current piece.
+ * @param offsetA offset in A direction relative to current element
+ * @param offsetB offset in B direction relative to current element
+ * @param offsetC offset in C direction relative to current element
+ * @return true if contained, false otherwise.
+ */
+ public boolean isContainedInPiece(int offsetA, int offsetB, int offsetC) {
+ if (definition == null) return false;
+ return definition.isContainedInStructure(
+ piece,
+ offsetABC[0] + offsetA + baseOffsetABC[0],
+ offsetABC[1] + offsetB + baseOffsetABC[1],
+ offsetABC[2] + offsetC + baseOffsetABC[2]);
+ }
+
+ public ExtendedFacing getFacing() {
+ return facing;
+ }
+
+ /**
+ * Return a new instance with source modified to given value.
+ *
+ * @param source new source
+ * @return new instance
+ */
+ public AutoPlaceEnvironment withSource(IItemSource source) {
+ return new AutoPlaceEnvironment(source, actor, chatter, definition, piece, facing, offsetABC, baseOffsetABC);
+ }
+
+ /**
+ * Return a new instance with actor modified to given value.
+ *
+ * @param actor new actor
+ * @return new instance
+ */
+ public AutoPlaceEnvironment withActor(EntityPlayerMP actor) {
+ return new AutoPlaceEnvironment(source, actor, chatter, definition, piece, facing, offsetABC, baseOffsetABC);
+ }
+
+ /**
+ * Return a new instance with chatter modified to given value.
+ *
+ * @param chatter new chatter
+ * @return new instance
+ */
+ public AutoPlaceEnvironment withChatter(Consumer chatter) {
+ return new AutoPlaceEnvironment(source, actor, chatter, definition, piece, facing, offsetABC, baseOffsetABC);
+ }
+
+ private static class WrappedIItemSource implements IItemSource {
+ final AutoPlaceEnvironment container;
+ final IItemSource delegate;
+
+ public WrappedIItemSource(AutoPlaceEnvironment container, IItemSource delegate) {
+ this.container = container;
+ this.delegate = delegate;
+ }
+
+ @Override
+ @Nonnull
+ public Map take(Predicate predicate, boolean simulate, int count) {
+ return delegate.take(predicate, simulate, count);
+ }
+
+ @Override
+ public ItemStack takeOne(Predicate predicate, boolean simulate) {
+ return delegate.takeOne(predicate, simulate);
+ }
+
+ @Override
+ public boolean takeAll(Predicate predicate, boolean simulate, int count) {
+ return delegate.takeAll(predicate, simulate, count);
+ }
+
+ @Override
+ public boolean takeOne(ItemStack stack, boolean simulate) {
+ return delegate.takeOne(stack, simulate);
+ }
+
+ @Override
+ public boolean takeAll(ItemStack stack, boolean simulate) {
+ return delegate.takeAll(stack, simulate);
+ }
+ }
+
+ public enum APILevel {
+ Legacy,
+ V2,
+ }
+}
diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/DefaultSurvivalBuildEnvironment.java b/src/main/java/com/gtnewhorizon/structurelib/structure/DefaultSurvivalBuildEnvironment.java
new file mode 100644
index 00000000..77f571f5
--- /dev/null
+++ b/src/main/java/com/gtnewhorizon/structurelib/structure/DefaultSurvivalBuildEnvironment.java
@@ -0,0 +1,23 @@
+package com.gtnewhorizon.structurelib.structure;
+
+import net.minecraft.entity.player.EntityPlayer;
+
+class DefaultSurvivalBuildEnvironment implements ISurvivalBuildEnvironment {
+ private final IItemSource source;
+ private final EntityPlayer actor;
+
+ public DefaultSurvivalBuildEnvironment(IItemSource source, EntityPlayer actor) {
+ this.source = source;
+ this.actor = actor;
+ }
+
+ @Override
+ public IItemSource getSource() {
+ return source;
+ }
+
+ @Override
+ public EntityPlayer getActor() {
+ return actor;
+ }
+}
diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureDefinition.java b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureDefinition.java
index ed0ac6fd..56a7b6b7 100644
--- a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureDefinition.java
+++ b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureDefinition.java
@@ -5,8 +5,10 @@ import static com.gtnewhorizon.structurelib.structure.IStructureWalker.skipBlock
import com.gtnewhorizon.structurelib.StructureLib;
import com.gtnewhorizon.structurelib.StructureLibAPI;
+import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable;
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
import java.util.function.Function;
+import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.item.ItemStack;
import net.minecraft.world.World;
@@ -55,6 +57,8 @@ public interface IStructureDefinition {
*/
IStructureElement[] getStructureFor(String name);
+ boolean isContainedInStructure(String name, int offsetA, int offsetB, int offsetC);
+
/**
* Run a structure check.
* @param object context object. usually multiblock controller.
@@ -258,6 +262,7 @@ public interface IStructureDefinition {
* @return number of elements built, or -1 if structure done
* @see #build(Object, ItemStack, String, World, ExtendedFacing, int, int, int, int, int, int)
*/
+ @Deprecated
default int survivalBuild(
T object,
ItemStack trigger,
@@ -274,8 +279,78 @@ public interface IStructureDefinition {
IItemSource source,
EntityPlayerMP actor,
boolean check) {
- SurvivalBuildStructureWalker walker =
- new SurvivalBuildStructureWalker<>(object, trigger, source, actor, elementBudget, check);
+ EntityPlayer realActor;
+ if (actor == null) {
+ realActor = ISurvivalConstructable.__get_player();
+ if (realActor == null) throw new IllegalArgumentException();
+ } else {
+ realActor = actor;
+ }
+ return survivalBuild(
+ object,
+ trigger,
+ piece,
+ world,
+ extendedFacing,
+ basePositionX,
+ basePositionY,
+ basePositionZ,
+ basePositionA,
+ basePositionB,
+ basePositionC,
+ elementBudget,
+ ISurvivalBuildEnvironment.create(source, realActor),
+ check);
+ }
+
+ /**
+ * Cause a survival build.
+ *
+ * @param object context object. usually multiblock controller.
+ * @param trigger The trigger item that contains channel data.
+ * @param piece the structure piece's string identifier.
+ * @param world the world object this check takes place in.
+ * @param extendedFacing the current structure's orientation.
+ * @param basePositionX X location of the structure
+ * @param basePositionY Y location of the structure
+ * @param basePositionZ Z location of the structure
+ * @param basePositionA see class javadoc
+ * @param basePositionB see class javadoc
+ * @param basePositionC see class javadoc
+ * @param elementBudget build up to this many elements
+ * @param env build environment.
+ * @param check whether {@link IStructureElement#check(Object, World, int, int, int)} should be called if anything
+ * would be placed. use with caution.
+ * if in doubt, call {@link IStructureDefinition#check(Object, String, World, ExtendedFacing, int, int, int, int, int, int, boolean)}
+ * after this call and set this parameter to false
+ * @return number of elements built, or -1 if structure done
+ * @see #build(Object, ItemStack, String, World, ExtendedFacing, int, int, int, int, int, int)
+ */
+ default int survivalBuild(
+ T object,
+ ItemStack trigger,
+ String piece,
+ World world,
+ ExtendedFacing extendedFacing,
+ int basePositionX,
+ int basePositionY,
+ int basePositionZ,
+ int basePositionA,
+ int basePositionB,
+ int basePositionC,
+ int elementBudget,
+ ISurvivalBuildEnvironment env,
+ boolean check) {
+ SurvivalBuildStructureWalker walker = new SurvivalBuildStructureWalker<>(
+ object,
+ trigger,
+ elementBudget,
+ env,
+ this,
+ piece,
+ extendedFacing,
+ new int[] {basePositionA, basePositionB, basePositionC},
+ check);
StructureUtility.iterateV2(
getStructureFor(piece),
world,
@@ -341,7 +416,7 @@ public interface IStructureDefinition {
basePositionA,
basePositionB,
basePositionC,
- (e, w, x, y, z) -> e.check(object, w, x, y, z),
+ (e, w, x, y, z, a, b, c) -> e.check(object, w, x, y, z),
"check");
} else {
success = StructureUtility.iterateV2(
@@ -354,7 +429,7 @@ public interface IStructureDefinition {
basePositionA,
basePositionB,
basePositionC,
- skipBlockUnloaded((e, w, x, y, z) -> e.check(object, w, x, y, z)),
+ skipBlockUnloaded((e, w, x, y, z, a, b, c) -> e.check(object, w, x, y, z)),
"check force");
}
if (StructureLibAPI.isDebugEnabled() && success) {
@@ -374,7 +449,7 @@ public interface IStructureDefinition {
basePositionA,
basePositionB,
basePositionC,
- ignoreBlockUnloaded((e, w, x, y, z) -> {
+ ignoreBlockUnloaded((e, w, x, y, z, a, b, c) -> {
e.spawnHint(object, world, x, y, z, trigger);
return true;
}),
@@ -390,7 +465,7 @@ public interface IStructureDefinition {
basePositionA,
basePositionB,
basePositionC,
- ignoreBlockUnloaded((e, w, x, y, z) -> {
+ ignoreBlockUnloaded((e, w, x, y, z, a, b, c) -> {
e.placeBlock(object, world, x, y, z, trigger);
return true;
}),
diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElement.java b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElement.java
index a50fffb7..d6102667 100644
--- a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElement.java
+++ b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElement.java
@@ -6,6 +6,7 @@ import static com.gtnewhorizon.structurelib.StructureLib.PANIC_MODE;
import com.gtnewhorizon.structurelib.StructureLibAPI;
import java.util.function.Consumer;
import net.minecraft.block.Block;
+import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.item.ItemStack;
import net.minecraft.util.IChatComponent;
@@ -24,7 +25,7 @@ public interface IStructureElement {
/**
* Try place the block by taking resource from given IItemSource.
*
- * You might want to use {@link StructureUtility#survivalPlaceBlock(Block, int, World, int, int, int, IItemSource, EntityPlayerMP)}
+ * You might want to use {@link StructureUtility#survivalPlaceBlock(Block, int, World, int, int, int, IItemSource, net.minecraft.entity.player.EntityPlayer)}
* or its overloads to implement this.
*
* @param trigger trigger item
@@ -33,7 +34,11 @@ public interface IStructureElement {
* any filter that chatter might have.
* @param chatter send error messages here. Caller will choose an appropriate way to forward it to player if the
* other fallbacks also fails.
+ * @deprecated caller should call the non deprecated overload. implementor for reusable {@link IStructureElement}
+ * should still implement this. implementor for private implementations can freely ignore this as long as you know
+ * the caller will not call this overload.
*/
+ @Deprecated
default PlaceResult survivalPlaceBlock(
T t,
World world,
@@ -53,6 +58,29 @@ public interface IStructureElement {
return PlaceResult.SKIP;
}
+ /**
+ * Try place the block by taking resource from given IItemSource.
+ *
+ * You might want to use {@link StructureUtility#survivalPlaceBlock(Block, int, World, int, int, int, IItemSource, net.minecraft.entity.player.EntityPlayer)}
+ * or its overloads to implement this.
+ *
+ * @param trigger trigger item
+ * @param env autoplacing environment
+ */
+ default PlaceResult survivalPlaceBlock(
+ T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) {
+ EntityPlayer actor = env.getActor();
+ if (actor instanceof EntityPlayerMP)
+ return survivalPlaceBlock(
+ t, world, x, y, z, trigger, env.getSource(), (EntityPlayerMP) actor, env.getChatter());
+ if (PANIC_MODE) throw new RuntimeException("Panic Tripwire hit");
+ if (StructureLibAPI.isDebugEnabled())
+ LOGGER.info(
+ "Fallback shim code of survivalPlaceBlock hit! Things aren't going to work well! IStructureElement class: {}",
+ getClass().getName());
+ return PlaceResult.SKIP;
+ }
+
/**
* Forget the messed up class dependency graph for now. this is just so convenient.
*/
diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElementChain.java b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElementChain.java
index 82a697af..3cd05b6a 100644
--- a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElementChain.java
+++ b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElementChain.java
@@ -74,4 +74,28 @@ public interface IStructureElementChain extends IStructureElement {
// TODO need reconsider to ensure this is the right course of action
return haveSkip ? PlaceResult.SKIP : PlaceResult.REJECT;
}
+
+ @Override
+ default PlaceResult survivalPlaceBlock(
+ T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) {
+ boolean haveSkip = false;
+ List bufferedNoise = new ArrayList<>();
+ for (IStructureElement fallback : fallbacks()) {
+ PlaceResult result =
+ fallback.survivalPlaceBlock(t, world, x, y, z, trigger, env.withChatter(bufferedNoise::add));
+ switch (result) {
+ case REJECT:
+ break;
+ case SKIP:
+ haveSkip = true;
+ break;
+ default:
+ return result;
+ }
+ }
+ // dump all that noise back into upstream
+ bufferedNoise.forEach(env.getChatter());
+ // TODO need reconsider to ensure this is the right course of action
+ return haveSkip ? PlaceResult.SKIP : PlaceResult.REJECT;
+ }
}
diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElementCheckOnly.java b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElementCheckOnly.java
index 854f405b..e874389d 100644
--- a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElementCheckOnly.java
+++ b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElementCheckOnly.java
@@ -30,4 +30,10 @@ public interface IStructureElementCheckOnly extends IStructureElement {
Consumer chatter) {
return PlaceResult.SKIP;
}
+
+ @Override
+ default PlaceResult survivalPlaceBlock(
+ T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) {
+ return PlaceResult.SKIP;
+ }
}
diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElementNoPlacement.java b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElementNoPlacement.java
index db861178..0570e862 100644
--- a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElementNoPlacement.java
+++ b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureElementNoPlacement.java
@@ -26,6 +26,12 @@ public interface IStructureElementNoPlacement extends IStructureElement {
return PlaceResult.REJECT;
}
+ @Override
+ default PlaceResult survivalPlaceBlock(
+ T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) {
+ return PlaceResult.REJECT;
+ }
+
@Override
default IStructureElementNoPlacement noPlacement() {
return this;
diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureNavigate.java b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureNavigate.java
index 00e3a89e..a464b317 100644
--- a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureNavigate.java
+++ b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureNavigate.java
@@ -39,6 +39,12 @@ interface IStructureNavigate extends IStructureElement {
return PlaceResult.SKIP;
}
+ @Override
+ default PlaceResult survivalPlaceBlock(
+ T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) {
+ return PlaceResult.SKIP;
+ }
+
default boolean isNavigating() {
return true;
}
diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureWalker.java b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureWalker.java
index 2e3ec862..9284f3a9 100644
--- a/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureWalker.java
+++ b/src/main/java/com/gtnewhorizon/structurelib/structure/IStructureWalker.java
@@ -2,23 +2,25 @@ package com.gtnewhorizon.structurelib.structure;
import net.minecraft.world.World;
-public interface IStructureWalker {
- boolean visit(IStructureElement element, World world, int x, int y, int z);
+interface IStructureWalker {
+ boolean visit(IStructureElement element, World world, int x, int y, int z, int a, int b, int c);
- default boolean blockNotLoaded(IStructureElement element, World world, int x, int y, int z) {
+ default boolean blockNotLoaded(
+ IStructureElement element, World world, int x, int y, int z, int a, int b, int c) {
return false;
}
static IStructureWalker ignoreBlockUnloaded(IStructureWalker walker) {
return new IStructureWalker() {
@Override
- public boolean visit(IStructureElement element, World world, int x, int y, int z) {
- return walker.visit(element, world, x, y, z);
+ public boolean visit(IStructureElement element, World world, int x, int y, int z, int a, int b, int c) {
+ return walker.visit(element, world, x, y, z, a, b, c);
}
@Override
- public boolean blockNotLoaded(IStructureElement element, World world, int x, int y, int z) {
- return walker.visit(element, world, x, y, z);
+ public boolean blockNotLoaded(
+ IStructureElement element, World world, int x, int y, int z, int a, int b, int c) {
+ return walker.visit(element, world, x, y, z, a, b, c);
}
};
}
@@ -26,12 +28,13 @@ public interface IStructureWalker {
static IStructureWalker skipBlockUnloaded(IStructureWalker walker) {
return new IStructureWalker() {
@Override
- public boolean visit(IStructureElement element, World world, int x, int y, int z) {
- return walker.visit(element, world, x, y, z);
+ public boolean visit(IStructureElement element, World world, int x, int y, int z, int a, int b, int c) {
+ return walker.visit(element, world, x, y, z, a, b, c);
}
@Override
- public boolean blockNotLoaded(IStructureElement element, World world, int x, int y, int z) {
+ public boolean blockNotLoaded(
+ IStructureElement element, World world, int x, int y, int z, int a, int b, int c) {
return true;
}
};
diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/ISurvivalBuildEnvironment.java b/src/main/java/com/gtnewhorizon/structurelib/structure/ISurvivalBuildEnvironment.java
new file mode 100644
index 00000000..bda10471
--- /dev/null
+++ b/src/main/java/com/gtnewhorizon/structurelib/structure/ISurvivalBuildEnvironment.java
@@ -0,0 +1,35 @@
+package com.gtnewhorizon.structurelib.structure;
+
+import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
+import net.minecraft.entity.player.EntityPlayer;
+import net.minecraft.item.ItemStack;
+import net.minecraft.world.World;
+
+/*
+ * Architecture notes: this should never get any callback from survival build. All structure significant code belongs
+ * within the IStructureElement. This will make the user easier to maintain the IStructureDefinition.
+ * Structure Element knows the most about what this structure element should do, not the caller.
+ */
+public interface ISurvivalBuildEnvironment {
+ /**
+ * Get the source of the items. This will be invoked exactly once per structure element build.
+ *
+ * If the call chain ever got into the old API, this will be called at least once per {@link IStructureDefinition#survivalBuild(Object, ItemStack, String, World, ExtendedFacing, int, int, int, int, int, int, int, ISurvivalBuildEnvironment, boolean)} call.
+ */
+ IItemSource getSource();
+
+ /**
+ * Get the origin of action. This will be invoked at least once per {@link IStructureDefinition#survivalBuild(Object, ItemStack, String, World, ExtendedFacing, int, int, int, int, int, int, int, ISurvivalBuildEnvironment, boolean)} call.
+ */
+ EntityPlayer getActor();
+
+ /**
+ * Create a default implementation with three parameters all being constants.
+ *
+ * @param source from where to drain resource
+ * @param actor source of action
+ */
+ static ISurvivalBuildEnvironment create(IItemSource source, EntityPlayer actor) {
+ return new DefaultSurvivalBuildEnvironment(source, actor);
+ }
+}
diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/LazyStructureElement.java b/src/main/java/com/gtnewhorizon/structurelib/structure/LazyStructureElement.java
index 6739673e..907f74ac 100644
--- a/src/main/java/com/gtnewhorizon/structurelib/structure/LazyStructureElement.java
+++ b/src/main/java/com/gtnewhorizon/structurelib/structure/LazyStructureElement.java
@@ -51,4 +51,10 @@ class LazyStructureElement implements IStructureElementDeferred {
Consumer chatter) {
return get(t).survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, chatter);
}
+
+ @Override
+ public PlaceResult survivalPlaceBlock(
+ T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) {
+ return get(t).survivalPlaceBlock(t, world, x, y, z, trigger, env);
+ }
}
diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/StructureDefinition.java b/src/main/java/com/gtnewhorizon/structurelib/structure/StructureDefinition.java
index eb0946ca..c82ff789 100644
--- a/src/main/java/com/gtnewhorizon/structurelib/structure/StructureDefinition.java
+++ b/src/main/java/com/gtnewhorizon/structurelib/structure/StructureDefinition.java
@@ -2,7 +2,6 @@ package com.gtnewhorizon.structurelib.structure;
import static com.gtnewhorizon.structurelib.structure.StructureUtility.*;
-import com.gtnewhorizon.structurelib.StructureLibAPI;
import com.gtnewhorizon.structurelib.util.Vec3Impl;
import java.util.*;
import java.util.stream.Collectors;
@@ -11,6 +10,7 @@ public class StructureDefinition implements IStructureDefinition {
private final Map> elements;
private final Map shapes;
private final Map[]> structures;
+ private final Map> occupiedSpaces;
public static Builder builder() {
return new Builder<>();
@@ -19,10 +19,12 @@ public class StructureDefinition implements IStructureDefinition {
private StructureDefinition(
Map> elements,
Map shapes,
- Map[]> structures) {
+ Map[]> structures,
+ Map> occupiedSpaces) {
this.elements = elements;
this.shapes = shapes;
this.structures = structures;
+ this.occupiedSpaces = occupiedSpaces;
}
public static class Builder {
@@ -33,11 +35,13 @@ public class StructureDefinition implements IStructureDefinition {
private final Map navigates;
private final Map> elements;
private final Map shapes;
+ private final Map> occupiedSpaces;
private Builder() {
navigates = new HashMap<>();
elements = new HashMap<>();
shapes = new HashMap<>();
+ occupiedSpaces = new HashMap<>();
}
public Map> getElements() {
@@ -80,22 +84,37 @@ public class StructureDefinition implements IStructureDefinition {
}
builder.setLength(builder.length() - 1);
}
+ Set occupiedSpace = new HashSet<>();
+ // these track the global current location
+ int aa = 0, bb = 0, cc = 0;
+ // these track the vec3 towards next meaningful element
int a = 0, b = 0, c = 0;
+ // 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);
- if (ch == ' ' || ch == '~') {
+ if (ch == ' ') {
builder.setCharAt(i, A);
ch = A;
+ } else if (ch == '~') {
+ builder.setCharAt(i, A);
+ ch = A;
+ occupiedSpace.add(Vec3Impl.getFromPool(aa, bb, cc));
}
if (ch == A) {
+ aa++;
a++;
} else if (ch == B) {
+ aa = 0;
a = 0;
+ bb++;
b++;
} else if (ch == C) {
+ aa = 0;
+ bb = 0;
a = 0;
b = 0;
c++;
+ cc++;
} else if (a != 0 || b != 0 || c != 0) {
Vec3Impl vec3 = new Vec3Impl(a, b, c);
Character navigate = navigates.get(vec3);
@@ -105,12 +124,19 @@ public class StructureDefinition implements IStructureDefinition {
addElement(navigate, step(vec3));
}
builder.setCharAt(i - 1, navigate);
+ occupiedSpace.add(Vec3Impl.getFromPool(aa, bb, cc));
+ aa++;
a = 0;
b = 0;
c = 0;
+ } else {
+ occupiedSpace.add(Vec3Impl.getFromPool(aa, bb, cc));
+ aa++;
}
}
+ occupiedSpaces.put(name, occupiedSpace);
+
String built = builder.toString().replaceAll("[\\uA000\\uB000\\uC000]", "");
if (built.contains("+")) {
@@ -130,11 +156,8 @@ public class StructureDefinition implements IStructureDefinition {
public IStructureDefinition build() {
Map[]> structures = compileStructureMap();
- if (StructureLibAPI.isDebugEnabled()) {
- return new StructureDefinition<>(new HashMap<>(elements), new HashMap<>(shapes), structures);
- } else {
- return structures::get;
- }
+ return new StructureDefinition<>(
+ new HashMap<>(elements), new HashMap<>(shapes), structures, occupiedSpaces);
}
@SuppressWarnings("unchecked")
@@ -194,4 +217,11 @@ public class StructureDefinition implements IStructureDefinition {
if (elements == null) throw new NoSuchElementException(name);
return elements;
}
+
+ @Override
+ public boolean isContainedInStructure(String name, int offsetA, int offsetB, int offsetC) {
+ Set occupiedSpace = occupiedSpaces.get(name);
+ if (occupiedSpace == null) throw new NoSuchElementException(name);
+ return occupiedSpace.contains(new Vec3Impl(offsetA, offsetB, offsetC));
+ }
}
diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/StructureElement_Bridge.java b/src/main/java/com/gtnewhorizon/structurelib/structure/StructureElement_Bridge.java
new file mode 100644
index 00000000..afbf58bf
--- /dev/null
+++ b/src/main/java/com/gtnewhorizon/structurelib/structure/StructureElement_Bridge.java
@@ -0,0 +1,32 @@
+package com.gtnewhorizon.structurelib.structure;
+
+import java.util.function.Consumer;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.util.IChatComponent;
+import net.minecraft.world.World;
+
+/**
+ * Internal bridge class for easing implementation
+ */
+abstract class StructureElement_Bridge implements IStructureElement {
+ // seal the deprecated method to prevent accidental overrides
+ @Deprecated
+ @Override
+ public final PlaceResult survivalPlaceBlock(
+ T t,
+ World world,
+ int x,
+ int y,
+ int z,
+ ItemStack trigger,
+ IItemSource s,
+ EntityPlayerMP actor,
+ Consumer chatter) {
+ return survivalPlaceBlock(t, world, x, y, z, trigger, AutoPlaceEnvironment.fromLegacy(s, actor, chatter));
+ }
+
+ // clear the default implementation. we don't need that.
+ public abstract PlaceResult survivalPlaceBlock(
+ T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env);
+}
diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/StructureUtility.java b/src/main/java/com/gtnewhorizon/structurelib/structure/StructureUtility.java
index 7c79f8dd..0992e8a7 100644
--- a/src/main/java/com/gtnewhorizon/structurelib/structure/StructureUtility.java
+++ b/src/main/java/com/gtnewhorizon/structurelib/structure/StructureUtility.java
@@ -112,7 +112,7 @@ import org.apache.commons.lang3.tuple.Pair;
* Helper Methods
* These don't construct a {@link IStructureElement}, but is helpful to the instantiation or implementation of these.
*
- * - {@link #survivalPlaceBlock(Block, int, World, int, int, int, IItemSource, EntityPlayerMP, Consumer)} and its overloads:
+ *
- {@link #survivalPlaceBlock(Block, int, World, int, int, int, IItemSource, EntityPlayer, Consumer)} and its overloads:
* Helper method to cut common boilerplate for implementing {@link IStructureElement#survivalPlaceBlock(Object, World, int, int, int, ItemStack, IItemSource, EntityPlayerMP, Consumer)}
* - {@link #getPseudoJavaCode(World, ExtendedFacing, int, int, int, int, int, int, Function, int, int, int, boolean)}:
* Generate a structure code template from existing structure.
@@ -147,6 +147,7 @@ public class StructureUtility {
}
@Override
+ @Deprecated
public PlaceResult survivalPlaceBlock(
Object o,
World world,
@@ -252,6 +253,22 @@ public class StructureUtility {
*/
public static PlaceResult survivalPlaceBlock(
Block block, int meta, World world, int x, int y, int z, IItemSource s, EntityPlayerMP actor) {
+ return survivalPlaceBlock(block, meta, world, x, y, z, s, (EntityPlayer) actor);
+ }
+
+ /**
+ * This is a helper method for implementing {@link IStructureElement#survivalPlaceBlock(Object, World, int, int, int, ItemStack, IItemSource, EntityPlayerMP, Consumer)}
+ *
+ * This method will try to look up an {@link ItemBlock} for given block and meta from {@code s} and place it at given
+ * location.
+ * This method assume block at given coord is NOT acceptable, but may or may not be trivially replaceable
+ * This method might yield error messages to the player only if there is likely a programming error or exploit.
+ *
+ * @param s drain resources from this place
+ * @param actor source of action. cannot be null.
+ */
+ public static PlaceResult survivalPlaceBlock(
+ Block block, int meta, World world, int x, int y, int z, IItemSource s, EntityPlayer actor) {
return survivalPlaceBlock(block, meta, world, x, y, z, s, actor, null);
}
@@ -278,6 +295,32 @@ public class StructureUtility {
IItemSource s,
EntityPlayerMP actor,
Consumer chatter) {
+ return survivalPlaceBlock(block, meta, world, x, y, z, s, (EntityPlayer) actor, chatter);
+ }
+
+ /**
+ * This is a helper method for implementing {@link IStructureElement#survivalPlaceBlock(Object, World, int, int, int, ItemStack, IItemSource, EntityPlayerMP, Consumer)}
+ *
+ * This method will try to look up an {@link ItemBlock} for given block and meta from {@code s} and place it at given
+ * location.
+ * This method assume block at given coord is NOT acceptable, but may or may not be trivially replaceable
+ * This method might yield error messages to the player only if there is likely a programming error or exploit.
+ * This method might yield error messages to the chatter.
+ *
+ * @param s drain resources from this place
+ * @param actor source of action. cannot be null.
+ * @param chatter normal error destination. can be null to suppress them.
+ */
+ public static PlaceResult survivalPlaceBlock(
+ Block block,
+ int meta,
+ World world,
+ int x,
+ int y,
+ int z,
+ IItemSource s,
+ EntityPlayer actor,
+ Consumer chatter) {
if (block == null) throw new NullPointerException();
if (!StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, actor)) return PlaceResult.REJECT;
Item itemBlock = Item.getItemFromBlock(block);
@@ -323,6 +366,32 @@ public class StructureUtility {
int z,
IItemSource s,
EntityPlayerMP actor) {
+ return survivalPlaceBlock(stack, nbtMode, tag, assumeStackPresent, world, x, y, z, s, (EntityPlayer) actor);
+ }
+
+ /**
+ * This is a helper method for implementing {@link IStructureElement#survivalPlaceBlock(Object, World, int, int, int, ItemStack, IItemSource, EntityPlayerMP, Consumer)}
+ *
+ * This method will try to look up an {@link ItemBlock} for given block and meta from {@code s} and place it at given
+ * location.
+ * This method assume block at given coord is NOT acceptable, but may or may not be trivially replaceable
+ * This method might yield error messages to the player only if there is likely a programming error or exploit.
+ *
+ * @param stack a valid stack with stack size of exactly 1. Must be of an ItemBlock!
+ * @param s drain resources from this place
+ * @param actor source of action. cannot be null.
+ */
+ public static PlaceResult survivalPlaceBlock(
+ ItemStack stack,
+ NBTMode nbtMode,
+ NBTTagCompound tag,
+ boolean assumeStackPresent,
+ World world,
+ int x,
+ int y,
+ int z,
+ IItemSource s,
+ EntityPlayer actor) {
return survivalPlaceBlock(stack, nbtMode, tag, assumeStackPresent, world, x, y, z, s, actor, null);
}
@@ -352,6 +421,36 @@ public class StructureUtility {
IItemSource s,
EntityPlayerMP actor,
@Nullable Consumer chatter) {
+ return survivalPlaceBlock(
+ stack, nbtMode, tag, assumeStackPresent, world, x, y, z, s, (EntityPlayer) actor, chatter);
+ }
+
+ /**
+ * This is a helper method for implementing {@link IStructureElement#survivalPlaceBlock(Object, World, int, int, int, ItemStack, IItemSource, EntityPlayerMP, Consumer)}
+ *
+ * This method will try to look up an {@link ItemBlock} for given block and meta from {@code s} and place it at given
+ * location.
+ * This method assume block at given coord is NOT acceptable, but may or may not be trivially replaceable
+ * This method might yield error messages to the player only if there is likely a programming error or exploit.
+ * This method might yield error messages to the chatter.
+ *
+ * @param stack a valid stack with stack size of exactly 1. Must be of an ItemBlock!
+ * @param s drain resources from this place
+ * @param actor source of action. cannot be null.
+ * @param chatter normal error destination. can be null to suppress them.
+ */
+ public static PlaceResult survivalPlaceBlock(
+ ItemStack stack,
+ NBTMode nbtMode,
+ NBTTagCompound tag,
+ boolean assumeStackPresent,
+ World world,
+ int x,
+ int y,
+ int z,
+ IItemSource s,
+ EntityPlayer actor,
+ @Nullable Consumer chatter) {
if (stack == null) throw new NullPointerException();
if (stack.stackSize != 1) throw new IllegalArgumentException();
if (!(stack.getItem() instanceof ItemBlock)) throw new IllegalArgumentException();
@@ -577,9 +676,9 @@ public class StructureUtility {
* @param allKnownTiers A list of all known tiers as of calling. Can be empty or null. No hint will be spawned if empty or null. Cannot have null elements.
* First element denotes the most primitive tier. Last element denotes the most advanced tier.
* If not all tiers are available at definition construction time, use {@link #lazy(Supplier)} or its overloads to delay a bit.
- * This list is only for hint particle spawning and survival build.
- * The hint/autoplace code will choose 1st pair to spawn/place if trigger item has master channel data of 1,
- * 2nd pair if 2, and so on.
+ * This list is only for hint particle spawning and survival build.
+ * The hint/autoplace code will choose 1st pair to spawn/place if trigger item has master channel data of 1,
+ * 2nd pair if 2, and so on.
* @param notSet The value returned from {@code getter} when there were no tier info found in T yet. Can be null.
* @param getter a function to retrieve the current tier from context object
* @param setter a function to set the current tier into context object
@@ -593,7 +692,7 @@ public class StructureUtility {
List> hints = allKnownTiers == null ? Collections.emptyList() : allKnownTiers;
if (hints.stream().anyMatch(Objects::isNull)) throw new IllegalArgumentException();
IStructureElementCheckOnly check = ofBlocksTiered(tierExtractor, notSet, setter, getter);
- return new IStructureElement() {
+ return new StructureElement_Bridge() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
return check.check(t, world, x, y, z);
@@ -626,15 +725,7 @@ public class StructureUtility {
@Override
public PlaceResult survivalPlaceBlock(
- T t,
- World world,
- int x,
- int y,
- int z,
- ItemStack trigger,
- IItemSource s,
- EntityPlayerMP actor,
- Consumer chatter) {
+ T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) {
Pair hint = getHint(trigger);
if (hint == null) return PlaceResult.REJECT; // TODO or SKIP?
Block block = world.getBlock(x, y, z);
@@ -643,7 +734,15 @@ public class StructureUtility {
if (Objects.equals(tier, tierExtractor.convert(hint.getKey(), hint.getValue())))
return PlaceResult.SKIP;
return StructureUtility.survivalPlaceBlock(
- hint.getKey(), hint.getValue(), world, x, y, z, s, actor, chatter);
+ hint.getKey(),
+ hint.getValue(),
+ world,
+ x,
+ y,
+ z,
+ env.getSource(),
+ env.getActor(),
+ env.getChatter());
}
};
}
@@ -676,7 +775,7 @@ public class StructureUtility {
if (meta < 0) throw new IllegalArgumentException();
if (meta > 15) throw new IllegalArgumentException();
if (StringUtils.isBlank(modid)) throw new IllegalArgumentException();
- return new IStructureElement() {
+ return new StructureElement_Bridge() {
private Block block;
private Block getBlock() {
@@ -705,18 +804,11 @@ public class StructureUtility {
@Override
public PlaceResult survivalPlaceBlock(
- T t,
- World world,
- int x,
- int y,
- int z,
- ItemStack trigger,
- IItemSource s,
- EntityPlayerMP actor,
- Consumer chatter) {
+ T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) {
if (check(t, world, x, y, z)) return PlaceResult.SKIP;
if (getBlock() == null) return PlaceResult.REJECT;
- return StructureUtility.survivalPlaceBlock(getBlock(), meta, world, x, y, z, s, actor, chatter);
+ return StructureUtility.survivalPlaceBlock(
+ getBlock(), meta, world, x, y, z, env.getSource(), env.getActor(), env.getChatter());
}
};
}
@@ -773,6 +865,7 @@ public class StructureUtility {
}
@Override
+ @Deprecated
public PlaceResult survivalPlaceBlock(
T t,
World world,
@@ -787,6 +880,16 @@ public class StructureUtility {
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);
}
+
+ @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;
+ if (init())
+ return StructureUtility.survivalPlaceBlock(
+ block, meta, world, x, y, z, env.getSource(), env.getActor(), env.getChatter());
+ return fallback.survivalPlaceBlock(t, world, x, y, z, trigger, env);
+ }
};
}
@@ -928,7 +1031,7 @@ public class StructureUtility {
throw new IllegalArgumentException();
}
if (defaultBlock instanceof ICustomBlockSetting) {
- return new IStructureElement() {
+ return new StructureElement_Bridge() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
Block worldBlock = world.getBlock(x, y, z);
@@ -948,23 +1051,24 @@ public class StructureUtility {
}
@Override
+ @Deprecated
public PlaceResult survivalPlaceBlock(
- T t,
- World world,
- int x,
- int y,
- int z,
- ItemStack trigger,
- IItemSource s,
- EntityPlayerMP actor,
- Consumer chatter) {
+ T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) {
if (check(t, world, x, y, z)) return PlaceResult.SKIP;
return StructureUtility.survivalPlaceBlock(
- defaultBlock, defaultMeta, world, x, y, z, s, actor, chatter);
+ defaultBlock,
+ defaultMeta,
+ world,
+ x,
+ y,
+ z,
+ env.getSource(),
+ env.getActor(),
+ env.getChatter());
}
};
} else {
- return new IStructureElement() {
+ return new StructureElement_Bridge() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
Block worldBlock = world.getBlock(x, y, z);
@@ -984,19 +1088,20 @@ public class StructureUtility {
}
@Override
+ @Deprecated
public PlaceResult survivalPlaceBlock(
- T t,
- World world,
- int x,
- int y,
- int z,
- ItemStack trigger,
- IItemSource s,
- EntityPlayerMP actor,
- Consumer chatter) {
+ T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) {
if (check(t, world, x, y, z)) return PlaceResult.SKIP;
return StructureUtility.survivalPlaceBlock(
- defaultBlock, defaultMeta, world, x, y, z, s, actor, chatter);
+ defaultBlock,
+ defaultMeta,
+ world,
+ x,
+ y,
+ z,
+ env.getSource(),
+ env.getActor(),
+ env.getChatter());
}
};
}
@@ -1021,7 +1126,7 @@ public class StructureUtility {
}
}
if (defaultBlock instanceof ICustomBlockSetting) {
- return new IStructureElement() {
+ return new StructureElement_Bridge() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
Block worldBlock = world.getBlock(x, y, z);
@@ -1042,23 +1147,24 @@ public class StructureUtility {
}
@Override
+ @Deprecated
public PlaceResult survivalPlaceBlock(
- T t,
- World world,
- int x,
- int y,
- int z,
- ItemStack trigger,
- IItemSource s,
- EntityPlayerMP actor,
- Consumer chatter) {
+ T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) {
if (check(t, world, x, y, z)) return PlaceResult.SKIP;
return StructureUtility.survivalPlaceBlock(
- defaultBlock, defaultMeta, world, x, y, z, s, actor, chatter);
+ defaultBlock,
+ defaultMeta,
+ world,
+ x,
+ y,
+ z,
+ env.getSource(),
+ env.getActor(),
+ env.getChatter());
}
};
} else {
- return new IStructureElement() {
+ return new StructureElement_Bridge() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
Block worldBlock = world.getBlock(x, y, z);
@@ -1079,19 +1185,20 @@ public class StructureUtility {
}
@Override
+ @Deprecated
public PlaceResult survivalPlaceBlock(
- T t,
- World world,
- int x,
- int y,
- int z,
- ItemStack trigger,
- IItemSource s,
- EntityPlayerMP actor,
- Consumer chatter) {
+ T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) {
if (check(t, world, x, y, z)) return PlaceResult.SKIP;
return StructureUtility.survivalPlaceBlock(
- defaultBlock, defaultMeta, world, x, y, z, s, actor, chatter);
+ defaultBlock,
+ defaultMeta,
+ world,
+ x,
+ y,
+ z,
+ env.getSource(),
+ env.getActor(),
+ env.getChatter());
}
};
}
@@ -1110,7 +1217,7 @@ public class StructureUtility {
throw new IllegalArgumentException();
}
if (block instanceof ICustomBlockSetting) {
- return new IStructureElement() {
+ return new StructureElement_Bridge() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
Block worldBlock = world.getBlock(x, y, z);
@@ -1130,23 +1237,24 @@ public class StructureUtility {
}
@Override
+ @Deprecated
public PlaceResult survivalPlaceBlock(
- T t,
- World world,
- int x,
- int y,
- int z,
- ItemStack trigger,
- IItemSource s,
- EntityPlayerMP actor,
- Consumer chatter) {
+ T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) {
if (check(t, world, x, y, z)) return PlaceResult.SKIP;
return StructureUtility.survivalPlaceBlock(
- defaultBlock, defaultMeta, world, x, y, z, s, actor, chatter);
+ defaultBlock,
+ defaultMeta,
+ world,
+ x,
+ y,
+ z,
+ env.getSource(),
+ env.getActor(),
+ env.getChatter());
}
};
} else {
- return new IStructureElement() {
+ return new StructureElement_Bridge() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
Block worldBlock = world.getBlock(x, y, z);
@@ -1166,19 +1274,20 @@ public class StructureUtility {
}
@Override
+ @Deprecated
public PlaceResult survivalPlaceBlock(
- T t,
- World world,
- int x,
- int y,
- int z,
- ItemStack trigger,
- IItemSource s,
- EntityPlayerMP actor,
- Consumer chatter) {
+ T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) {
if (check(t, world, x, y, z)) return PlaceResult.SKIP;
return StructureUtility.survivalPlaceBlock(
- defaultBlock, defaultMeta, world, x, y, z, s, actor, chatter);
+ defaultBlock,
+ defaultMeta,
+ world,
+ x,
+ y,
+ z,
+ env.getSource(),
+ env.getActor(),
+ env.getChatter());
}
};
}
@@ -1192,7 +1301,7 @@ public class StructureUtility {
throw new IllegalArgumentException();
}
if (block instanceof ICustomBlockSetting) {
- return new IStructureElement() {
+ return new StructureElement_Bridge() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
return block == world.getBlock(x, y, z);
@@ -1211,23 +1320,24 @@ public class StructureUtility {
}
@Override
+ @Deprecated
public PlaceResult survivalPlaceBlock(
- T t,
- World world,
- int x,
- int y,
- int z,
- ItemStack trigger,
- IItemSource s,
- EntityPlayerMP actor,
- Consumer chatter) {
+ T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) {
if (check(t, world, x, y, z)) return PlaceResult.SKIP;
return StructureUtility.survivalPlaceBlock(
- defaultBlock, defaultMeta, world, x, y, z, s, actor, chatter);
+ defaultBlock,
+ defaultMeta,
+ world,
+ x,
+ y,
+ z,
+ env.getSource(),
+ env.getActor(),
+ env.getChatter());
}
};
} else {
- return new IStructureElement() {
+ return new StructureElement_Bridge() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
return block == world.getBlock(x, y, z);
@@ -1246,19 +1356,20 @@ public class StructureUtility {
}
@Override
+ @Deprecated
public PlaceResult survivalPlaceBlock(
- T t,
- World world,
- int x,
- int y,
- int z,
- ItemStack trigger,
- IItemSource s,
- EntityPlayerMP actor,
- Consumer chatter) {
+ T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) {
if (check(t, world, x, y, z)) return PlaceResult.SKIP;
return StructureUtility.survivalPlaceBlock(
- defaultBlock, defaultMeta, world, x, y, z, s, actor, chatter);
+ defaultBlock,
+ defaultMeta,
+ world,
+ x,
+ y,
+ z,
+ env.getSource(),
+ env.getActor(),
+ env.getChatter());
}
};
}
@@ -1300,7 +1411,7 @@ public class StructureUtility {
throw new IllegalArgumentException();
}
if (defaultBlock instanceof ICustomBlockSetting) {
- return new IStructureElement() {
+ return new StructureElement_Bridge() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
Block worldBlock = world.getBlock(x, y, z);
@@ -1320,24 +1431,25 @@ public class StructureUtility {
}
@Override
+ @Deprecated
public PlaceResult survivalPlaceBlock(
- T t,
- World world,
- int x,
- int y,
- int z,
- ItemStack trigger,
- IItemSource s,
- EntityPlayerMP actor,
- Consumer chatter) {
+ 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;
return StructureUtility.survivalPlaceBlock(
- defaultBlock, defaultMeta, world, x, y, z, s, actor, chatter);
+ defaultBlock,
+ defaultMeta,
+ world,
+ x,
+ y,
+ z,
+ env.getSource(),
+ env.getActor(),
+ env.getChatter());
}
};
} else {
- return new IStructureElement() {
+ return new StructureElement_Bridge() {
@Override
public boolean check(T t, World world, int x, int y, int z) {
Block worldBlock = world.getBlock(x, y, z);
@@ -1357,20 +1469,21 @@ public class StructureUtility {
}
@Override
+ @Deprecated
public PlaceResult survivalPlaceBlock(
- T t,
- World world,
- int x,
- int y,
- int z,
- ItemStack trigger,
- IItemSource s,
- EntityPlayerMP actor,
- Consumer chatter) {
+ 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;
return StructureUtility.survivalPlaceBlock(
- defaultBlock, defaultMeta, world, x, y, z, s, actor, chatter);
+ defaultBlock,
+ defaultMeta,
+ world,
+ x,
+ y,
+ z,
+ env.getSource(),
+ env.getActor(),
+ env.getChatter());
}
};
}
@@ -1465,6 +1578,7 @@ public class StructureUtility {
}
@Override
+ @Deprecated
public PlaceResult survivalPlaceBlock(
T t,
World world,
@@ -1477,6 +1591,12 @@ public class StructureUtility {
Consumer chatter) {
return element.survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, chatter);
}
+
+ @Override
+ public PlaceResult survivalPlaceBlock(
+ T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) {
+ return element.survivalPlaceBlock(t, world, x, y, z, trigger, env);
+ }
};
}
@@ -1509,6 +1629,7 @@ public class StructureUtility {
}
@Override
+ @Deprecated
public PlaceResult survivalPlaceBlock(
T t,
World world,
@@ -1521,6 +1642,12 @@ public class StructureUtility {
Consumer chatter) {
return element.survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, chatter);
}
+
+ @Override
+ public PlaceResult survivalPlaceBlock(
+ T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) {
+ return element.survivalPlaceBlock(t, world, x, y, z, trigger, env);
+ }
};
}
@@ -1562,6 +1689,7 @@ public class StructureUtility {
}
@Override
+ @Deprecated
public PlaceResult survivalPlaceBlock(
T t,
World world,
@@ -1576,6 +1704,13 @@ public class StructureUtility {
return downstream.survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, chatter);
return placeResultWhenDisabled;
}
+
+ @Override
+ public PlaceResult survivalPlaceBlock(
+ T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) {
+ if (predicate.test(t)) return downstream.survivalPlaceBlock(t, world, x, y, z, trigger, env);
+ return placeResultWhenDisabled;
+ }
};
}
@@ -1650,6 +1785,7 @@ public class StructureUtility {
}
@Override
+ @Deprecated
public PlaceResult survivalPlaceBlock(
T t,
World world,
@@ -1662,6 +1798,12 @@ public class StructureUtility {
Consumer chatter) {
return elem.survivalPlaceBlock(t.getCurrentContext(), world, x, y, z, trigger, s, actor, chatter);
}
+
+ @Override
+ public PlaceResult survivalPlaceBlock(
+ T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) {
+ return elem.survivalPlaceBlock(t.getCurrentContext(), world, x, y, z, trigger, env);
+ }
};
}
// endregion
@@ -1732,6 +1874,7 @@ public class StructureUtility {
}
@Override
+ @Deprecated
public PlaceResult survivalPlaceBlock(
T t,
World world,
@@ -1744,6 +1887,12 @@ public class StructureUtility {
Consumer chatter) {
return to.get().survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, chatter);
}
+
+ @Override
+ public PlaceResult survivalPlaceBlock(
+ T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) {
+ return to.get().survivalPlaceBlock(t, world, x, y, z, trigger, env);
+ }
};
}
@@ -1791,6 +1940,12 @@ public class StructureUtility {
Consumer chatter) {
return to.apply(t).survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, chatter);
}
+
+ @Override
+ public PlaceResult survivalPlaceBlock(
+ T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) {
+ return to.apply(t).survivalPlaceBlock(t, world, x, y, z, trigger, env);
+ }
};
}
@@ -1857,6 +2012,12 @@ public class StructureUtility {
Consumer chatter) {
return map.get(keyExtractor.apply(t)).survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, chatter);
}
+
+ @Override
+ public PlaceResult survivalPlaceBlock(
+ T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) {
+ return map.get(keyExtractor.apply(t)).survivalPlaceBlock(t, world, x, y, z, trigger, env);
+ }
};
}
@@ -1930,6 +2091,13 @@ public class StructureUtility {
return map.getOrDefault(keyExtractor.apply(t), defaultElem)
.survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, chatter);
}
+
+ @Override
+ public PlaceResult survivalPlaceBlock(
+ T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) {
+ return map.getOrDefault(keyExtractor.apply(t), defaultElem)
+ .survivalPlaceBlock(t, world, x, y, z, trigger, env);
+ }
};
}
@@ -2000,6 +2168,12 @@ public class StructureUtility {
Consumer chatter) {
return array[keyExtractor.apply(t)].survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, chatter);
}
+
+ @Override
+ public PlaceResult survivalPlaceBlock(
+ T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) {
+ return array[keyExtractor.apply(t)].survivalPlaceBlock(t, world, x, y, z, trigger, env);
+ }
};
}
@@ -2086,6 +2260,12 @@ public class StructureUtility {
Consumer chatter) {
return to.apply(t, trigger).survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, chatter);
}
+
+ @Override
+ public PlaceResult survivalPlaceBlock(
+ T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) {
+ return to.apply(t, trigger).survivalPlaceBlock(t, world, x, y, z, trigger, env);
+ }
};
}
@@ -2161,6 +2341,12 @@ public class StructureUtility {
return map.get(keyExtractor.apply(t, trigger))
.survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, chatter);
}
+
+ @Override
+ public PlaceResult survivalPlaceBlock(
+ T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) {
+ return map.get(keyExtractor.apply(t, trigger)).survivalPlaceBlock(t, world, x, y, z, trigger, env);
+ }
};
}
@@ -2245,6 +2431,13 @@ public class StructureUtility {
return map.getOrDefault(keyExtractor.apply(t, trigger), defaultElem)
.survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, chatter);
}
+
+ @Override
+ public PlaceResult survivalPlaceBlock(
+ T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) {
+ return map.getOrDefault(keyExtractor.apply(t, trigger), defaultElem)
+ .survivalPlaceBlock(t, world, x, y, z, trigger, env);
+ }
};
}
@@ -2298,6 +2491,12 @@ public class StructureUtility {
return array[keyExtractor.apply(t, trigger)].survivalPlaceBlock(
t, world, x, y, z, trigger, s, actor, chatter);
}
+
+ @Override
+ public PlaceResult survivalPlaceBlock(
+ T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) {
+ return array[keyExtractor.apply(t, trigger)].survivalPlaceBlock(t, world, x, y, z, trigger, env);
+ }
};
}
@@ -2370,6 +2569,12 @@ public class StructureUtility {
Consumer chatter) {
return to.apply(t, trigger).survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, chatter);
}
+
+ @Override
+ public PlaceResult survivalPlaceBlock(
+ T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) {
+ return to.apply(t, trigger).survivalPlaceBlock(t, world, x, y, z, trigger, env);
+ }
};
}
@@ -2457,6 +2662,12 @@ public class StructureUtility {
return map.get(keyExtractor.apply(t, trigger))
.survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, chatter);
}
+
+ @Override
+ public PlaceResult survivalPlaceBlock(
+ T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) {
+ return map.get(keyExtractor.apply(t, trigger)).survivalPlaceBlock(t, world, x, y, z, trigger, env);
+ }
};
}
@@ -2546,6 +2757,13 @@ public class StructureUtility {
return map.getOrDefault(keyExtractor.apply(t, trigger), defaultElem)
.survivalPlaceBlock(t, world, x, y, z, trigger, s, actor, chatter);
}
+
+ @Override
+ public PlaceResult survivalPlaceBlock(
+ T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) {
+ return map.getOrDefault(keyExtractor.apply(t, trigger), defaultElem)
+ .survivalPlaceBlock(t, world, x, y, z, trigger, env);
+ }
};
}
@@ -2620,6 +2838,7 @@ public class StructureUtility {
}
@Override
+ @Deprecated
public PlaceResult survivalPlaceBlock(
T t,
World world,
@@ -2633,6 +2852,12 @@ public class StructureUtility {
return array[keyExtractor.apply(t, trigger)].survivalPlaceBlock(
t, world, x, y, z, trigger, s, actor, chatter);
}
+
+ @Override
+ public PlaceResult survivalPlaceBlock(
+ T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) {
+ return array[keyExtractor.apply(t, trigger)].survivalPlaceBlock(t, world, x, y, z, trigger, env);
+ }
};
}
@@ -2723,6 +2948,7 @@ public class StructureUtility {
return backing.placeBlock(t, world, x, y, z, ChannelDataAccessor.withChannel(trigger, channel));
}
+ @Deprecated
public PlaceResult survivalPlaceBlock(
T t,
World world,
@@ -2740,6 +2966,17 @@ public class StructureUtility {
warnNoExplicitSubChannel(actor);
return backing.survivalPlaceBlock(t, world, x, y, z, newTrigger, s, actor, chatter);
}
+
+ @Override
+ public PlaceResult survivalPlaceBlock(
+ T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) {
+ ItemStack newTrigger = ChannelDataAccessor.withChannel(trigger, channel);
+ if (newTrigger == trigger)
+ // we will bypass the chatter filter here, as this is a warning that player definitively want to see
+ // instead of some false positive error messages like item not find
+ warnNoExplicitSubChannel(env.getActor());
+ return backing.survivalPlaceBlock(t, world, x, y, z, trigger, env);
+ }
};
}
// endregion
@@ -3076,7 +3313,7 @@ public class StructureUtility {
Arrays.toString(abc));
if (world.blockExists(xyz[0], xyz[1], xyz[2])) {
- if (!predicate.visit(element, world, xyz[0], xyz[1], xyz[2])) {
+ if (!predicate.visit(element, world, xyz[0], xyz[1], xyz[2], abc[0], abc[1], abc[2])) {
if (StructureLibAPI.isDebugEnabled()) {
StructureLib.LOGGER.info(
"Multi [{}, {}, {}] {} stop @ {} {}",
@@ -3100,7 +3337,8 @@ public class StructureUtility {
Arrays.toString(xyz),
Arrays.toString(abc));
}
- if (!predicate.blockNotLoaded(element, world, xyz[0], xyz[1], xyz[2])) return false;
+ if (!predicate.blockNotLoaded(element, world, xyz[0], xyz[1], xyz[2], abc[0], abc[1], abc[2]))
+ return false;
}
abc[0] += 1;
}
diff --git a/src/main/java/com/gtnewhorizon/structurelib/structure/SurvivalBuildStructureWalker.java b/src/main/java/com/gtnewhorizon/structurelib/structure/SurvivalBuildStructureWalker.java
index a8051cad..f7835f59 100644
--- a/src/main/java/com/gtnewhorizon/structurelib/structure/SurvivalBuildStructureWalker.java
+++ b/src/main/java/com/gtnewhorizon/structurelib/structure/SurvivalBuildStructureWalker.java
@@ -1,34 +1,53 @@
package com.gtnewhorizon.structurelib.structure;
import com.gtnewhorizon.structurelib.StructureLibAPI;
+import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
import com.gtnewhorizon.structurelib.structure.IStructureElement.PlaceResult;
-import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.item.ItemStack;
import net.minecraft.world.World;
class SurvivalBuildStructureWalker implements IStructureWalker {
- private final T object;
- private final ItemStack trigger;
- private final IItemSource source;
- private final EntityPlayerMP actor;
+ final T object;
+ final ItemStack trigger;
private final int elementBudget;
+ private final ISurvivalBuildEnvironment params;
private final boolean check;
private int built = -1;
+ private final AutoPlaceEnvironment env;
+
public SurvivalBuildStructureWalker(
- T object, ItemStack trigger, IItemSource source, EntityPlayerMP actor, int elementBudget, boolean check) {
+ T object,
+ ItemStack trigger,
+ int elementBudget,
+ ISurvivalBuildEnvironment params,
+ IStructureDefinition> definition,
+ String piece,
+ ExtendedFacing facing,
+ int[] baseOffsetABC,
+ boolean check) {
this.object = object;
this.trigger = trigger;
- this.source = source;
- this.actor = actor;
this.elementBudget = elementBudget;
+ this.params = params;
this.check = check;
+
+ env = new AutoPlaceEnvironment(
+ params.getActor(),
+ params.getActor()::addChatComponentMessage,
+ definition,
+ piece,
+ facing,
+ baseOffsetABC);
}
@Override
- public boolean visit(IStructureElement element, World world, int x, int y, int z) {
- PlaceResult placeResult = element.survivalPlaceBlock(
- object, world, x, y, z, trigger, source, actor, actor::addChatComponentMessage);
+ public boolean visit(IStructureElement element, World world, int x, int y, int z, int a, int b, int c) {
+ env.offsetABC[0] = a;
+ env.offsetABC[1] = b;
+ env.offsetABC[2] = c;
+ env.setSource(params.getSource());
+ PlaceResult placeResult = element.survivalPlaceBlock(object, world, x, y, z, trigger, env);
if (placeResult != PlaceResult.SKIP && built == -1) built = 0;
switch (placeResult) {
case SKIP:
@@ -37,7 +56,7 @@ class SurvivalBuildStructureWalker implements IStructureWalker {
if (check) element.check(object, world, x, y, z);
return ++built < elementBudget;
case REJECT:
- StructureLibAPI.markHintParticleError(actor, world, x, y, z);
+ StructureLibAPI.markHintParticleError(params.getActor(), world, x, y, z);
return false;
case ACCEPT_STOP:
if (check) element.check(object, world, x, y, z);
diff --git a/src/main/java/com/gtnewhorizon/structurelib/util/Vec3Impl.java b/src/main/java/com/gtnewhorizon/structurelib/util/Vec3Impl.java
index e52b47d0..c3be106b 100644
--- a/src/main/java/com/gtnewhorizon/structurelib/util/Vec3Impl.java
+++ b/src/main/java/com/gtnewhorizon/structurelib/util/Vec3Impl.java
@@ -1,14 +1,26 @@
package com.gtnewhorizon.structurelib.util;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.function.Function;
import net.minecraft.dispenser.IPosition;
import net.minecraftforge.common.util.ForgeDirection;
public class Vec3Impl implements Comparable {
public static final Vec3Impl NULL_VECTOR = new Vec3Impl(0, 0, 0);
+ private static final Map pool = new HashMap<>();
private final int val0;
private final int val1;
private final int val2;
+ /**
+ * THIS WILL CAUSE THIS VECTOR TO PERMANENTLY RESIDE IN STATIC MEMORY. DO NOT USE THIS UNLESS YOU KNOW WHAT YOU ARE
+ * DOING!!!
+ */
+ public static Vec3Impl getFromPool(int in0, int in1, int in2) {
+ return pool.computeIfAbsent(new Vec3Impl(in0, in1, in2), Function.identity());
+ }
+
public Vec3Impl(int in0, int in1, int in2) {
this.val0 = in0;
this.val1 = in1;
diff --git a/src/test/java/com/gtnewhorizon/structurelib/alignment/enumerable/ExtendedFacingTest.java b/src/test/java/com/gtnewhorizon/structurelib/alignment/enumerable/ExtendedFacingTest.java
new file mode 100644
index 00000000..5ac10b89
--- /dev/null
+++ b/src/test/java/com/gtnewhorizon/structurelib/alignment/enumerable/ExtendedFacingTest.java
@@ -0,0 +1,24 @@
+package com.gtnewhorizon.structurelib.alignment.enumerable;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import net.minecraftforge.common.util.ForgeDirection;
+import org.junit.jupiter.api.Test;
+
+class ExtendedFacingTest {
+
+ @Test
+ void ensureGetWorldDirectionDoesNotCrashNorReturnNull() {
+ for (ExtendedFacing ef : ExtendedFacing.VALUES) {
+ for (ForgeDirection src : ForgeDirection.VALID_DIRECTIONS) {
+ assertNotNull(ef.getWorldDirection(src));
+ }
+ }
+ }
+
+ @Test
+ void ensureGetWorldDirectionIsCorrect() {
+ assertEquals(ForgeDirection.UP, ExtendedFacing.EAST_NORMAL_NONE.getWorldDirection(ForgeDirection.UP));
+ }
+}