aboutsummaryrefslogtreecommitdiff
path: root/src/main/java/gregtech/api/util/HatchElementBuilder.java
diff options
context:
space:
mode:
authorNotAPenguin <michiel.vandeginste@gmail.com>2024-09-02 23:17:17 +0200
committerGitHub <noreply@github.com>2024-09-02 23:17:17 +0200
commit1b820de08a05070909a267e17f033fcf58ac8710 (patch)
tree02831a025986a06b20f87e5bcc69d1e0c639a342 /src/main/java/gregtech/api/util/HatchElementBuilder.java
parentafd3fd92b6a6ab9ab0d0dc3214e6bc8ff7a86c9b (diff)
downloadGT5-Unofficial-1b820de08a05070909a267e17f033fcf58ac8710.tar.gz
GT5-Unofficial-1b820de08a05070909a267e17f033fcf58ac8710.tar.bz2
GT5-Unofficial-1b820de08a05070909a267e17f033fcf58ac8710.zip
The Great Renaming (#3014)
* move kekztech to a single root dir * move detrav to a single root dir * move gtnh-lanthanides to a single root dir * move tectech and delete some gross reflection in gt++ * remove more reflection inside gt5u * delete more reflection in gt++ * fix imports * move bartworks and bwcrossmod * fix proxies * move galactigreg and ggfab * move gtneioreplugin * try to fix gt++ bee loader * apply the rename rules to BW * apply rename rules to bwcrossmod * apply rename rules to detrav scanner mod * apply rename rules to galacticgreg * apply rename rules to ggfab * apply rename rules to goodgenerator * apply rename rules to gtnh-lanthanides * apply rename rules to gt++ * apply rename rules to kekztech * apply rename rules to kubatech * apply rename rules to tectech * apply rename rules to gt apply the rename rules to gt * fix tt import * fix mui hopefully * fix coremod except intergalactic * rename assline recipe class * fix a class name i stumbled on * rename StructureUtility to GTStructureUtility to prevent conflict with structurelib * temporary rename of GTTooltipDataCache to old name * fix gt client/server proxy names
Diffstat (limited to 'src/main/java/gregtech/api/util/HatchElementBuilder.java')
-rw-r--r--src/main/java/gregtech/api/util/HatchElementBuilder.java546
1 files changed, 546 insertions, 0 deletions
diff --git a/src/main/java/gregtech/api/util/HatchElementBuilder.java b/src/main/java/gregtech/api/util/HatchElementBuilder.java
new file mode 100644
index 0000000000..8b93861114
--- /dev/null
+++ b/src/main/java/gregtech/api/util/HatchElementBuilder.java
@@ -0,0 +1,546 @@
+package gregtech.api.util;
+
+import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlock;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.EnumSet;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.BiFunction;
+import java.util.function.BiPredicate;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.function.Supplier;
+import java.util.stream.Collectors;
+
+import net.minecraft.block.Block;
+import net.minecraft.entity.player.EntityPlayerMP;
+import net.minecraft.item.ItemStack;
+import net.minecraft.tileentity.TileEntity;
+import net.minecraft.util.ChatComponentTranslation;
+import net.minecraft.util.IChatComponent;
+import net.minecraft.world.World;
+import net.minecraftforge.common.util.ForgeDirection;
+
+import com.gtnewhorizon.structurelib.StructureLibAPI;
+import com.gtnewhorizon.structurelib.alignment.constructable.ChannelDataAccessor;
+import com.gtnewhorizon.structurelib.structure.AutoPlaceEnvironment;
+import com.gtnewhorizon.structurelib.structure.IItemSource;
+import com.gtnewhorizon.structurelib.structure.IStructureElement;
+import com.gtnewhorizon.structurelib.structure.IStructureElementChain;
+import com.gtnewhorizon.structurelib.structure.IStructureElementNoPlacement;
+import com.gtnewhorizon.structurelib.util.ItemStackPredicate;
+
+import gnu.trove.TIntCollection;
+import gnu.trove.list.array.TIntArrayList;
+import gnu.trove.set.hash.TIntHashSet;
+import gregtech.api.interfaces.IHatchElement;
+import gregtech.api.interfaces.metatileentity.IMetaTileEntity;
+import gregtech.api.interfaces.tileentity.IGregTechTileEntity;
+import gregtech.common.blocks.ItemMachines;
+
+public class HatchElementBuilder<T> {
+
+ private interface Builtin {
+ }
+
+ private IGTHatchAdder<? super T> mAdder;
+ private int mCasingIndex = -1;
+ private int mDot = -1;
+ private BiPredicate<? super T, ? super IGregTechTileEntity> mShouldSkip;
+ private BiFunction<? super T, ItemStack, ? extends Predicate<ItemStack>> mHatchItemFilter;
+ private Supplier<String> mHatchItemType;
+ private Predicate<? super T> mReject;
+ private boolean mCacheHint;
+ private boolean mNoStop;
+ private boolean mExclusive;
+ private EnumSet<ForgeDirection> mDisallowedDirection = EnumSet.noneOf(ForgeDirection.class);
+
+ private HatchElementBuilder() {}
+
+ public static <T> HatchElementBuilder<T> builder() {
+ return new HatchElementBuilder<>();
+ }
+
+ // region composite
+
+ /**
+ * Set all of adder, hint and hatchItemFilter. Provide a reasonable default for shouldSkip. TODO add doc
+ */
+ @SafeVarargs
+ public final HatchElementBuilder<T> anyOf(IHatchElement<? super T>... elements) {
+ if (elements == null || elements.length == 0) throw new IllegalArgumentException();
+ return adder(
+ Arrays.stream(elements)
+ .map(
+ e -> e.adder()
+ .rebrand())
+ .reduce(IGTHatchAdder::orElse)
+ .get()).hatchClasses(
+ Arrays.stream(elements)
+ .map(IHatchElement::mteClasses)
+ .flatMap(Collection::stream)
+ .collect(Collectors.toList()))
+ .cacheHint(
+ () -> Arrays.stream(elements)
+ .map(IHatchElement::name)
+ .sorted()
+ .collect(Collectors.joining(" or ", "of type ", "")));
+ }
+
+ /**
+ * Set all of adder, hint and hatchItemFilter. Provide a reasonable default for shouldSkip.
+ * <p>
+ * Will rotate through all elements TODO add doc
+ */
+ @SafeVarargs
+ public final HatchElementBuilder<T> atLeast(IHatchElement<? super T>... elements) {
+ if (elements == null || elements.length == 0) throw new IllegalArgumentException();
+ return atLeast(
+ Arrays.stream(elements)
+ .collect(Collectors.groupingBy(Function.identity(), LinkedHashMap::new, Collectors.counting())));
+ }
+
+ /**
+ * Set all of adder, hint and hatchItemFilter. Provide a reasonable default for shouldSkip.
+ * <p>
+ * Will rotate through all elements TODO add doc
+ */
+ public final HatchElementBuilder<T> atLeastList(List<IHatchElement<? super T>> elements) {
+ if (elements == null || elements.isEmpty()) throw new IllegalArgumentException();
+ return atLeast(
+ elements.stream()
+ .collect(Collectors.groupingBy(Function.identity(), LinkedHashMap::new, Collectors.counting())));
+ }
+
+ /**
+ * Set all of adder, hint and hatchItemFilter. Provide a reasonable default for shouldSkip. TODO add doc
+ */
+ public final HatchElementBuilder<T> atLeast(Map<IHatchElement<? super T>, ? extends Number> elements) {
+ if (elements == null || elements.isEmpty() || elements.containsKey(null) || elements.containsValue(null))
+ throw new IllegalArgumentException();
+ List<Class<? extends IMetaTileEntity>> list = elements.keySet()
+ .stream()
+ .map(IHatchElement::mteClasses)
+ .flatMap(Collection::stream)
+ .collect(Collectors.toList());
+ // map cannot be null or empty, so assert Optional isPresent
+ return adder(
+ elements.keySet()
+ .stream()
+ .map(
+ e -> e.adder()
+ .rebrand())
+ .reduce(IGTHatchAdder::orElse)
+ .orElseThrow(AssertionError::new))
+ .hatchItemFilter(
+ obj -> GTStructureUtility.filterByMTEClass(
+ elements.entrySet()
+ .stream()
+ .filter(
+ entry -> entry.getKey()
+ .count(obj)
+ < entry.getValue()
+ .longValue())
+ .flatMap(
+ entry -> entry.getKey()
+ .mteClasses()
+ .stream())
+ .collect(Collectors.toList())))
+ .shouldReject(
+ obj -> elements.entrySet()
+ .stream()
+ .allMatch(
+ e -> e.getKey()
+ .count(obj)
+ >= e.getValue()
+ .longValue()))
+ .shouldSkip(
+ (BiPredicate<? super T, ? super IGregTechTileEntity> & Builtin) (c,
+ t) -> t != null && list.stream()
+ .anyMatch(clazz -> clazz.isInstance(t.getMetaTileEntity())))
+ .cacheHint(
+ () -> elements.keySet()
+ .stream()
+ .map(IHatchElement::name)
+ .sorted()
+ .collect(Collectors.joining(" or ", "of type ", "")));
+ }
+ // endregion
+
+ // region primitives
+
+ /**
+ * Mark this hatch element as the only candidate of given structure element. (e.g. muffler hatch on top of EBF)
+ * Currently, this will make the built IStructureElement to ignore gt_no_hatch directive from player
+ *
+ * Do note that {@link #buildAndChain(IStructureElement[])} and its overloads will force the resulting structure
+ * element
+ * to be non-exclusive.
+ */
+ public HatchElementBuilder<T> exclusive() {
+ mExclusive = true;
+ return this;
+ }
+
+ public HatchElementBuilder<T> adder(IGTHatchAdder<? super T> aAdder) {
+ if (aAdder == null) throw new IllegalArgumentException();
+ mAdder = aAdder;
+ return this;
+ }
+
+ public HatchElementBuilder<T> casingIndex(int aCasingIndex) {
+ if (aCasingIndex <= 0) throw new IllegalArgumentException();
+ mCasingIndex = aCasingIndex;
+ return this;
+ }
+
+ public HatchElementBuilder<T> dot(int aDot) {
+ if (aDot <= 0) throw new IllegalArgumentException();
+ mDot = aDot;
+ return this;
+ }
+
+ public HatchElementBuilder<T> shouldSkip(BiPredicate<? super T, ? super IGregTechTileEntity> aShouldSkip) {
+ if (!(aShouldSkip instanceof Builtin) || mShouldSkip != null) {
+ if (!(mShouldSkip instanceof Builtin) && mShouldSkip != null) throw new IllegalStateException();
+ if (aShouldSkip == null) throw new IllegalArgumentException();
+ }
+ mShouldSkip = aShouldSkip;
+ return this;
+ }
+
+ public HatchElementBuilder<T> shouldReject(Predicate<? super T> aShouldReject) {
+ if (aShouldReject == null) throw new IllegalArgumentException();
+ mReject = aShouldReject;
+ return this;
+ }
+
+ public HatchElementBuilder<T> hatchItemFilter(
+ Function<? super T, ? extends Predicate<ItemStack>> aHatchItemFilter) {
+ if (aHatchItemFilter == null) throw new IllegalArgumentException();
+ mHatchItemFilter = (t, s) -> aHatchItemFilter.apply(t);
+ return this;
+ }
+
+ public HatchElementBuilder<T> hatchItemFilterAnd(
+ Function<? super T, ? extends Predicate<ItemStack>> aHatchItemFilter) {
+ if (aHatchItemFilter == null) throw new IllegalArgumentException();
+ BiFunction<? super T, ItemStack, ? extends Predicate<ItemStack>> tOldFilter = mHatchItemFilter;
+ mHatchItemFilter = (t, s) -> tOldFilter.apply(t, s)
+ .and(aHatchItemFilter.apply(t));
+ return this;
+ }
+
+ public HatchElementBuilder<T> hatchItemFilter(
+ BiFunction<? super T, ItemStack, ? extends Predicate<ItemStack>> aHatchItemFilter) {
+ if (aHatchItemFilter == null) throw new IllegalArgumentException();
+ mHatchItemFilter = aHatchItemFilter;
+ return this;
+ }
+
+ public HatchElementBuilder<T> hatchItemFilterAnd(
+ BiFunction<? super T, ItemStack, ? extends Predicate<ItemStack>> aHatchItemFilter) {
+ if (aHatchItemFilter == null) throw new IllegalArgumentException();
+ BiFunction<? super T, ItemStack, ? extends Predicate<ItemStack>> tOldFilter = mHatchItemFilter;
+ mHatchItemFilter = (t, s) -> tOldFilter.apply(t, s)
+ .and(aHatchItemFilter.apply(t, s));
+ return this;
+ }
+
+ // region hint
+ public HatchElementBuilder<T> hint(Supplier<String> aSupplier) {
+ if (aSupplier == null) throw new IllegalArgumentException();
+ mHatchItemType = aSupplier;
+ mCacheHint = false;
+ return this;
+ }
+
+ public HatchElementBuilder<T> cacheHint(Supplier<String> aSupplier) {
+ if (aSupplier == null) throw new IllegalArgumentException();
+ mHatchItemType = aSupplier;
+ mCacheHint = true;
+ return this;
+ }
+
+ public HatchElementBuilder<T> cacheHint() {
+ if (mHatchItemType == null) throw new IllegalStateException();
+ mCacheHint = true;
+ return this;
+ }
+ // endregion
+
+ public HatchElementBuilder<T> continueIfSuccess() {
+ mNoStop = true;
+ return this;
+ }
+
+ public HatchElementBuilder<T> stopIfSuccess() {
+ mNoStop = false;
+ return this;
+ }
+
+ /**
+ * Help automatic hatch side determination code by ruling out some directions. Note the automatic hatch side
+ * determination code will choose to use the default facing if the final allowed facing set is empty.
+ * <p>
+ * This will clear the sides set by previous call to this or {@link #allowOnly(ForgeDirection...)}
+ * <p>
+ * Usually mandatory for multis with multiple slices, and otherwise not needed if it contains a single slice only.
+ *
+ * @param facings disallowed direction in ABC coordinate system
+ */
+ public HatchElementBuilder<T> disallowOnly(ForgeDirection... facings) {
+ if (facings == null) throw new IllegalArgumentException();
+ mDisallowedDirection = EnumSet.copyOf(Arrays.asList(facings));
+ return this;
+ }
+
+ /**
+ * Help automatic hatch side determination code by allowing only some directions. Note the automatic hatch side
+ * determination code will choose to use the default facing if the final allowed facing set is empty.
+ * <p>
+ * This will clear the sides set by previous call to this or {@link #disallowOnly(ForgeDirection...)}
+ * <p>
+ * Usually mandatory for multis with multiple slices, and otherwise not needed if it contains a single slice only.
+ *
+ * @param facings allowed direction in ABC coordinate system
+ */
+ public HatchElementBuilder<T> allowOnly(ForgeDirection... facings) {
+ if (facings == null) throw new IllegalArgumentException();
+ mDisallowedDirection = EnumSet.complementOf(EnumSet.copyOf(Arrays.asList(facings)));
+ mDisallowedDirection.remove(ForgeDirection.UNKNOWN);
+ return this;
+ }
+ // endregion
+
+ // region intermediate
+ public HatchElementBuilder<T> hatchClass(Class<? extends IMetaTileEntity> clazz) {
+ return hatchItemFilter(c -> is -> clazz.isInstance(ItemMachines.getMetaTileEntity(is)))
+ .cacheHint(() -> "of class " + clazz.getSimpleName())
+ .shouldSkip(
+ (BiPredicate<? super T, ? super IGregTechTileEntity> & Builtin) (c, t) -> clazz
+ .isInstance(t.getMetaTileEntity()));
+ }
+
+ @SafeVarargs
+ public final HatchElementBuilder<T> hatchClasses(Class<? extends IMetaTileEntity>... classes) {
+ return hatchClasses(Arrays.asList(classes));
+ }
+
+ public final HatchElementBuilder<T> hatchClasses(List<? extends Class<? extends IMetaTileEntity>> classes) {
+ List<? extends Class<? extends IMetaTileEntity>> list = new ArrayList<>(classes);
+ return hatchItemFilter(obj -> GTStructureUtility.filterByMTEClass(list)).cacheHint(
+ () -> list.stream()
+ .map(Class::getSimpleName)
+ .sorted()
+ .collect(Collectors.joining(" or ", "of class ", "")))
+ .shouldSkip(
+ (BiPredicate<? super T, ? super IGregTechTileEntity> & Builtin) (c, t) -> t != null && list.stream()
+ .anyMatch(clazz -> clazz.isInstance(t.getMetaTileEntity())));
+ }
+
+ public HatchElementBuilder<T> hatchId(int aId) {
+ return hatchItemFilter(
+ c -> is -> GTUtility.isStackValid(is) && is.getItem() instanceof ItemMachines && is.getItemDamage() == aId)
+ .cacheHint(() -> "of id " + aId)
+ .shouldSkip(
+ (BiPredicate<? super T, ? super IGregTechTileEntity> & Builtin) (c, t) -> t != null
+ && t.getMetaTileID() == aId);
+ }
+
+ public HatchElementBuilder<T> hatchIds(int... aIds) {
+ if (aIds == null || aIds.length == 0) throw new IllegalArgumentException();
+ if (aIds.length == 1) return hatchId(aIds[0]);
+ TIntCollection coll = aIds.length < 16 ? new TIntArrayList(aIds) : new TIntHashSet(aIds);
+ return hatchItemFilter(
+ c -> is -> GTUtility.isStackValid(is) && is.getItem() instanceof ItemMachines
+ && coll.contains(is.getItemDamage())).cacheHint(
+ () -> Arrays.stream(coll.toArray())
+ .sorted()
+ .mapToObj(String::valueOf)
+ .collect(Collectors.joining(" or ", "of id ", "")))
+ .shouldSkip(
+ (BiPredicate<? super T, ? super IGregTechTileEntity> & Builtin) (c, t) -> t != null
+ && coll.contains(t.getMetaTileID()));
+ }
+
+ // endregion
+
+ @SuppressWarnings("unchecked")
+ @SafeVarargs
+ public final IStructureElementChain<T> buildAndChain(IStructureElement<T>... elements) {
+ // just in case
+ mExclusive = false;
+ List<IStructureElement<T>> l = new ArrayList<>();
+ l.add(build());
+ l.addAll(Arrays.asList(elements));
+ IStructureElement<T>[] array = l.toArray(new IStructureElement[0]);
+ return () -> array;
+ }
+
+ public final IStructureElementChain<T> buildAndChain(Block block, int meta) {
+ return buildAndChain(ofBlock(block, meta));
+ }
+
+ public IStructureElement<T> build() {
+ if (mAdder == null || mCasingIndex == -1 || mDot == -1) {
+ throw new IllegalArgumentException();
+ }
+ if (mHatchItemFilter == null) {
+ // no item filter -> no placement
+ return new IStructureElementNoPlacement<>() {
+
+ @Override
+ public boolean check(T t, World world, int x, int y, int z) {
+ TileEntity tileEntity = world.getTileEntity(x, y, z);
+ return tileEntity instanceof IGregTechTileEntity
+ && mAdder.apply(t, (IGregTechTileEntity) tileEntity, (short) mCasingIndex);
+ }
+
+ @Override
+ public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
+ StructureLibAPI.hintParticle(world, x, y, z, StructureLibAPI.getBlockHint(), mDot - 1);
+ return true;
+ }
+ };
+ }
+ return new IStructureElement<>() {
+
+ private String mHint = mHatchItemType == null ? "unspecified GT hatch" : mHatchItemType.get();
+
+ @Override
+ public boolean check(T t, World world, int x, int y, int z) {
+ TileEntity tileEntity = world.getTileEntity(x, y, z);
+ return tileEntity instanceof IGregTechTileEntity
+ && mAdder.apply(t, (IGregTechTileEntity) tileEntity, (short) mCasingIndex);
+ }
+
+ @Override
+ public boolean spawnHint(T t, World world, int x, int y, int z, ItemStack trigger) {
+ StructureLibAPI.hintParticle(world, x, y, z, StructureLibAPI.getBlockHint(), mDot - 1);
+ return true;
+ }
+
+ @Override
+ public boolean placeBlock(T t, World world, int i, int i1, int i2, ItemStack itemStack) {
+ // TODO
+ return false;
+ }
+
+ private String getHint() {
+ if (mHint != null) return mHint;
+ String tHint = mHatchItemType.get();
+ if (tHint == null) return "?";
+ // TODO move this to some .lang instead of half ass it into the crappy gt lang file
+ tHint = GTLanguageManager.addStringLocalization("Hatch_Type_" + tHint.replace(' ', '_'), tHint);
+ if (mCacheHint) {
+ mHint = tHint;
+ if (mHint != null)
+ // yeet the getter, since its product is retrieved and cached
+ mHatchItemType = null;
+ }
+ return tHint;
+ }
+
+ @Override
+ public BlocksToPlace getBlocksToPlace(T t, World world, int x, int y, int z, ItemStack trigger,
+ AutoPlaceEnvironment env) {
+ return BlocksToPlace.create(mHatchItemFilter.apply(t, trigger));
+ }
+
+ @Deprecated
+ @Override
+ public 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));
+ }
+
+ @Override
+ public PlaceResult survivalPlaceBlock(T t, World world, int x, int y, int z, ItemStack trigger,
+ AutoPlaceEnvironment env) {
+ if (mShouldSkip != null) {
+ TileEntity tileEntity = world.getTileEntity(x, y, z);
+ if (tileEntity instanceof IGregTechTileEntity
+ && mShouldSkip.test(t, (IGregTechTileEntity) tileEntity)) return PlaceResult.SKIP;
+ }
+ if (!StructureLibAPI.isBlockTriviallyReplaceable(world, x, y, z, env.getActor()))
+ return PlaceResult.REJECT;
+ if (mReject != null && mReject.test(t)) return PlaceResult.REJECT;
+ if (ChannelDataAccessor.hasSubChannel(trigger, "gt_no_hatch") && !mExclusive) {
+ String type = getHint();
+ env.getChatter()
+ .accept(new ChatComponentTranslation("GT5U.autoplace.error.no_hatch", type));
+ return PlaceResult.REJECT;
+ }
+ ItemStack taken = env.getSource()
+ .takeOne(mHatchItemFilter.apply(t, trigger), true);
+ if (GTUtility.isStackInvalid(taken)) {
+ String type = getHint();
+ env.getChatter()
+ .accept(new ChatComponentTranslation("GT5U.autoplace.error.no_hatch", type));
+ return PlaceResult.REJECT;
+ }
+ if (com.gtnewhorizon.structurelib.structure.StructureUtility.survivalPlaceBlock(
+ taken,
+ ItemStackPredicate.NBTMode.IGNORE,
+ null,
+ true,
+ world,
+ x,
+ y,
+ z,
+ env.getSource(),
+ env.getActor()) != PlaceResult.ACCEPT) {
+ return PlaceResult.REJECT;
+ }
+ // try to infer facing
+ EnumSet<ForgeDirection> allowed = EnumSet.noneOf(ForgeDirection.class);
+ // first find which face of block is not contained in structure
+ if (env.getAPILevel() == AutoPlaceEnvironment.APILevel.Legacy) {
+ // a legacy decorator isn't passing down necessary information
+ // in that case, we just assume all facing is allowed
+ allowed.addAll(Arrays.asList(ForgeDirection.VALID_DIRECTIONS));
+ } else {
+ for (ForgeDirection direction : ForgeDirection.VALID_DIRECTIONS) {
+ // as noted on getWorldDirection Y axis should be flipped before use
+ if (env.isContainedInPiece(direction.offsetX, -direction.offsetY, direction.offsetZ)) continue;
+ // explicitly rejected, probably obstructed by another slice
+ if (mDisallowedDirection.contains(direction)) continue;
+ ForgeDirection rotated = env.getFacing()
+ .getWorldDirection(
+ (direction.flag & (ForgeDirection.UP.flag | ForgeDirection.DOWN.flag)) != 0
+ ? direction.getOpposite()
+ : direction);
+ allowed.add(rotated);
+ }
+ }
+ if (!allowed.isEmpty()) {
+ TileEntity tileEntity = world.getTileEntity(x, y, z);
+ if (tileEntity instanceof IGregTechTileEntity) {
+ ForgeDirection result = null;
+ // find the first facing available, but prefer a facing that isn't up/down
+ for (ForgeDirection facing : allowed) {
+ result = facing;
+ if ((facing.flag & (ForgeDirection.UP.flag | ForgeDirection.DOWN.flag)) == 0) break; // Horizontal
+ }
+ assert result != null;
+ ((IGregTechTileEntity) tileEntity).setFrontFacing(result);
+ }
+ }
+ return mNoStop ? PlaceResult.ACCEPT : PlaceResult.ACCEPT_STOP;
+ }
+ };
+ }
+}