Use argument object for survival autoplace

This commit is contained in:
Glease committed 2022-09-16 18:57:04 +08:00
1 parent 23605fc22d
commit c0814274ec
24 files changed
+1083 -183

No files matched your search

+7
View File
@@ -22,3 +22,10 @@ task javadocJar(type: Jar, dependsOn: javadoc) {
artifacts {
archives javadocJar
}
test {
useJUnitPlatform()
testLogging {
events "passed", "skipped", "failed"
}
}
+3
View File
@@ -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')
}
@@ -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.
* <p>
* 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.
* <p>
* 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);
@@ -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) {
@@ -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.
* <p>
* 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> entityPlayer = new ThreadLocal<>();
}
@@ -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<Vec3Impl, Direction> 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);
}
}
@@ -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
*
@@ -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<IChatComponent> 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<IChatComponent> 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<IChatComponent> 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<IChatComponent> 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<IChatComponent> getChatter() {
return chatter;
}
/**
* Test if given location is contained within the current piece.
* @param offsetA offset in A direction <b>relative to current element</b>
* @param offsetB offset in B direction <b>relative to current element</b>
* @param offsetC offset in C direction <b>relative to current element</b>
* @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<IChatComponent> 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<ItemStack, Integer> take(Predicate<ItemStack> predicate, boolean simulate, int count) {
return delegate.take(predicate, simulate, count);
}
@Override
public ItemStack takeOne(Predicate<ItemStack> predicate, boolean simulate) {
return delegate.takeOne(predicate, simulate);
}
@Override
public boolean takeAll(Predicate<ItemStack> 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,
}
}
@@ -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;
}
}
@@ -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<T> {
*/
IStructureElement<T>[] 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<T> {
* @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<T> {
IItemSource source,
EntityPlayerMP actor,
boolean check) {
SurvivalBuildStructureWalker<T> 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<T> 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<T> {
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<T> {
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<T> {
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<T> {
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;
}),
@@ -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<T> {
/**
* Try place the block by taking resource from given IItemSource.
* <p>
* 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<T> {
* 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<T> {
return PlaceResult.SKIP;
}
/**
* Try place the block by taking resource from given IItemSource.
* <p>
* 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.
*/
@@ -74,4 +74,28 @@ public interface IStructureElementChain<T> extends IStructureElement<T> {
// 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<IChatComponent> bufferedNoise = new ArrayList<>();
for (IStructureElement<T> 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;
}
}
@@ -30,4 +30,10 @@ public interface IStructureElementCheckOnly<T> extends IStructureElement<T> {
Consumer<IChatComponent> 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;
}
}
@@ -26,6 +26,12 @@ public interface IStructureElementNoPlacement<T> extends IStructureElement<T> {
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<T> noPlacement() {
return this;
@@ -39,6 +39,12 @@ interface IStructureNavigate<T> extends IStructureElement<T> {
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;
}
@@ -2,23 +2,25 @@ package com.gtnewhorizon.structurelib.structure;
import net.minecraft.world.World;
public interface IStructureWalker<T> {
boolean visit(IStructureElement<T> element, World world, int x, int y, int z);
interface IStructureWalker<T> {
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) {
default boolean blockNotLoaded(
IStructureElement<T> element, World world, int x, int y, int z, int a, int b, int c) {
return false;
}
static <T> IStructureWalker<T> ignoreBlockUnloaded(IStructureWalker<T> walker) {
return new IStructureWalker<T>() {
@Override
public boolean visit(IStructureElement<T> element, World world, int x, int y, int z) {
return walker.visit(element, world, x, y, z);
public boolean visit(IStructureElement<T> 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<T> element, World world, int x, int y, int z) {
return walker.visit(element, world, x, y, z);
public boolean blockNotLoaded(
IStructureElement<T> 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<T> {
static <T> IStructureWalker<T> skipBlockUnloaded(IStructureWalker<T> walker) {
return new IStructureWalker<T>() {
@Override
public boolean visit(IStructureElement<T> element, World world, int x, int y, int z) {
return walker.visit(element, world, x, y, z);
public boolean visit(IStructureElement<T> 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<T> element, World world, int x, int y, int z) {
public boolean blockNotLoaded(
IStructureElement<T> element, World world, int x, int y, int z, int a, int b, int c) {
return true;
}
};
@@ -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.
* <p>
* 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);
}
}
@@ -51,4 +51,10 @@ class LazyStructureElement<T> implements IStructureElementDeferred<T> {
Consumer<IChatComponent> 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);
}
}
@@ -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<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;
public static <B> Builder<B> builder() {
return new Builder<>();
@@ -19,10 +19,12 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
private StructureDefinition(
Map<Character, IStructureElement<T>> elements,
Map<String, String> shapes,
Map<String, IStructureElement<T>[]> structures) {
Map<String, IStructureElement<T>[]> structures,
Map<String, Set<Vec3Impl>> occupiedSpaces) {
this.elements = elements;
this.shapes = shapes;
this.structures = structures;
this.occupiedSpaces = occupiedSpaces;
}
public static class Builder<T> {
@@ -33,11 +35,13 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
private final Map<Vec3Impl, Character> navigates;
private final Map<Character, IStructureElement<T>> elements;
private final Map<String, String> shapes;
private final Map<String, Set<Vec3Impl>> occupiedSpaces;
private Builder() {
navigates = new HashMap<>();
elements = new HashMap<>();
shapes = new HashMap<>();
occupiedSpaces = new HashMap<>();
}
public Map<Character, IStructureElement<T>> getElements() {
@@ -80,22 +84,37 @@ public class StructureDefinition<T> implements IStructureDefinition<T> {
}
builder.setLength(builder.length() - 1);
}
Set<Vec3Impl> 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<T> implements IStructureDefinition<T> {
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<T> implements IStructureDefinition<T> {
public IStructureDefinition<T> build() {
Map<String, IStructureElement<T>[]> 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<T> implements IStructureDefinition<T> {
if (elements == null) throw new NoSuchElementException(name);
return elements;
}
@Override
public boolean isContainedInStructure(String name, int offsetA, int offsetB, int offsetC) {
Set<Vec3Impl> occupiedSpace = occupiedSpaces.get(name);
if (occupiedSpace == null) throw new NoSuchElementException(name);
return occupiedSpace.contains(new Vec3Impl(offsetA, offsetB, offsetC));
}
}
@@ -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<T> implements IStructureElement<T> {
// 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<IChatComponent> 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);
}
@@ -112,7 +112,7 @@ import org.apache.commons.lang3.tuple.Pair;
* <h1>Helper Methods</h1>
* These don't construct a {@link IStructureElement}, but is helpful to the instantiation or implementation of these.
* <ul>
* <li>{@link #survivalPlaceBlock(Block, int, World, int, int, int, IItemSource, EntityPlayerMP, Consumer)} and its overloads:
* <li>{@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)}</li>
* <li>{@link #getPseudoJavaCode(World, ExtendedFacing, int, int, int, int, int, int, Function, int, int, int, boolean)}:
* Generate a structure code template from existing structure.</li>
@@ -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)}
* <p>
* 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<IChatComponent> 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)}
* <p>
* 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<IChatComponent> 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)}
* <p>
* 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<IChatComponent> 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)}
* <p>
* 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<IChatComponent> 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<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 IStructureElement<T>() {
return new StructureElement_Bridge<T>() {
@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<IChatComponent> chatter) {
T t, World world, int x, int y, int z, ItemStack trigger, AutoPlaceEnvironment env) {
Pair<Block, Integer> 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<T>() {
return new StructureElement_Bridge<T>() {
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<IChatComponent> 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<T>() {
return new StructureElement_Bridge<T>() {
@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<IChatComponent> 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<T>() {
return new StructureElement_Bridge<T>() {
@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<IChatComponent> 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<T>() {
return new StructureElement_Bridge<T>() {
@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<IChatComponent> 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<T>() {
return new StructureElement_Bridge<T>() {
@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<IChatComponent> 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<T>() {
return new StructureElement_Bridge<T>() {
@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<IChatComponent> 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<T>() {
return new StructureElement_Bridge<T>() {
@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<IChatComponent> 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<T>() {
return new StructureElement_Bridge<T>() {
@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<IChatComponent> 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<T>() {
return new StructureElement_Bridge<T>() {
@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<IChatComponent> 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<T>() {
return new StructureElement_Bridge<T>() {
@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<IChatComponent> 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<T>() {
return new StructureElement_Bridge<T>() {
@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<IChatComponent> 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<IChatComponent> 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<IChatComponent> 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<IChatComponent> 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<IChatComponent> 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<IChatComponent> 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<IChatComponent> 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<IChatComponent> 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<IChatComponent> 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<IChatComponent> 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;
}
@@ -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<T> implements IStructureWalker<T> {
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<T> 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<T> 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<T> implements IStructureWalker<T> {
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);
@@ -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<Vec3Impl> {
public static final Vec3Impl NULL_VECTOR = new Vec3Impl(0, 0, 0);
private static final Map<Vec3Impl, Vec3Impl> 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;
@@ -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));
}
}