destination, Transaction tx) {
-
- for (var view : this.storage) {
- var is = view.getResource();
- if (is.isBlank()) {
- continue;
- }
-
- if (!filter.isEmpty() && !ItemComparisonHelper.isFuzzyEqualItem(is.toStack(), filter, fuzzyMode)) {
- continue; // Doesn't match ItemStack template
- }
-
- if (destination != null && !destination.test(is.toStack())) {
- continue; // Doesn't match filter
- }
-
- long actualAmount = view.extract(is, amount, tx);
- if (actualAmount <= 0) {
- continue; // Apparently not extractable
- }
-
- // If any of the slots returned more than what we requested, it'll be voided here
- if (actualAmount > amount) {
- // TODO AELog.warn(
- // "An inventory returned more (%d) than we requested (%d) during extraction. Excess will be voided.",
-// actualAmount, amount);
- actualAmount = amount;
- }
-
- return is.toStack((int) actualAmount);
- }
-
- return ItemStack.EMPTY;
+ @Override
+ public ItemStack insertItem(int slot, ItemStack stack, boolean simulate) {
+ return handler.insertItem(slot, stack, simulate);
}
- @NotNull
@Override
- public ItemStack addItems(ItemStack itemsToAdd, boolean simulate) {
- if (itemsToAdd.isEmpty()) {
- return ItemStack.EMPTY;
- }
-
- try (var tx = Platform.openOrJoinTx()) {
- ItemStack remainder = itemsToAdd.copy();
-
- var inserted = storage.insert(ItemVariant.of(itemsToAdd), itemsToAdd.getCount(), tx);
-
- if (!simulate) {
- tx.commit();
- }
-
- remainder.shrink((int) inserted);
- return remainder.isEmpty() ? ItemStack.EMPTY : remainder;
- }
+ public ItemStack extractItem(int slot, int amount, boolean simulate) {
+ return handler.extractItem(slot, amount, simulate);
}
}
diff --git a/src/main/java/appeng/api/networking/GridHelper.java b/src/main/java/appeng/api/networking/GridHelper.java
index c0273a3fe36..e410c815608 100644
--- a/src/main/java/appeng/api/networking/GridHelper.java
+++ b/src/main/java/appeng/api/networking/GridHelper.java
@@ -34,6 +34,7 @@
import net.minecraft.world.level.block.entity.BlockEntity;
import appeng.api.networking.events.GridEvent;
+import appeng.capabilities.Capabilities;
import appeng.hooks.ticking.TickHandler;
import appeng.me.GridConnection;
import appeng.me.GridEventBus;
@@ -133,7 +134,7 @@ public static IInWorldGridNodeHost getNodeHost(Level level, BlockPos pos) {
if (be instanceof IInWorldGridNodeHost host) {
return host;
}
- return IInWorldGridNodeHost.LOOKUP.find(level, pos, null, be, null);
+ return be != null ? be.getCapability(Capabilities.IN_WORLD_GRID_NODE_HOST).orElse(null) : null;
}
/**
diff --git a/src/main/java/appeng/api/networking/IInWorldGridNodeHost.java b/src/main/java/appeng/api/networking/IInWorldGridNodeHost.java
index 60fc6fccd20..35ce332727b 100644
--- a/src/main/java/appeng/api/networking/IInWorldGridNodeHost.java
+++ b/src/main/java/appeng/api/networking/IInWorldGridNodeHost.java
@@ -25,12 +25,11 @@
import org.jetbrains.annotations.Nullable;
-import net.fabricmc.fabric.api.lookup.v1.block.BlockApiLookup;
import net.minecraft.core.Direction;
import net.minecraft.world.level.block.entity.BlockEntity;
+import net.neoforged.neoforge.common.capabilities.AutoRegisterCapability;
import appeng.api.util.AECableType;
-import appeng.core.AppEng;
/**
* Implement to create a networked {@link BlockEntity}. Must be implemented for a block entity to be available for
@@ -38,10 +37,8 @@
*
* Can either be implemented by the block entity itself, or provided via a lookup/capability with null direction.
*/
+@AutoRegisterCapability
public interface IInWorldGridNodeHost {
- BlockApiLookup LOOKUP = BlockApiLookup.get(AppEng.makeId("iinworldgridnodehost"),
- IInWorldGridNodeHost.class, Void.class);
-
/**
* get the grid node for a particular side of a block, you can return null, by returning a valid node later and
* calling updateState, you can join the Grid when your block is ready.
diff --git a/src/main/java/appeng/api/networking/crafting/UnsuitableCpus.java b/src/main/java/appeng/api/networking/crafting/UnsuitableCpus.java
index 6d678d75d14..a92be5bdfac 100644
--- a/src/main/java/appeng/api/networking/crafting/UnsuitableCpus.java
+++ b/src/main/java/appeng/api/networking/crafting/UnsuitableCpus.java
@@ -2,7 +2,7 @@
/**
* Details about unsuitable crafting CPUs unavailable for a job. Detail for
- * {@link appeng.api.networking.crafting.CraftingSubmitErrorCode#NO_SUITABLE_CPU_FOUND}.
+ * {@link CraftingSubmitErrorCode#NO_SUITABLE_CPU_FOUND}.
*/
public record UnsuitableCpus(int offline, int busy, int tooSmall, int excluded) {
}
diff --git a/src/main/java/appeng/api/networking/storage/IStorageService.java b/src/main/java/appeng/api/networking/storage/IStorageService.java
index 3232b52264a..06e4ee74a4b 100644
--- a/src/main/java/appeng/api/networking/storage/IStorageService.java
+++ b/src/main/java/appeng/api/networking/storage/IStorageService.java
@@ -52,11 +52,11 @@ public interface IStorageService extends IGridService {
KeyCounter getCachedInventory();
/**
- * Adds a {@link IStorageProvider} that is not associated with a specific {@link appeng.api.networking.IGridNode }.
- * This is for adding storage provided by {@link IGridService}s for examples.
+ * Adds a {@link IStorageProvider} that is not associated with a specific {@link IGridNode }. This is for adding
+ * storage provided by {@link IGridService}s for examples.
*
- * THIS IT NOT FOR USE BY {@link appeng.api.networking.IGridNode NODES} THAT PROVIDE THE {@link IStorageProvider}
- * SERVICE. Those are automatically handled by the storage system.
+ * THIS IT NOT FOR USE BY {@link IGridNode NODES} THAT PROVIDE THE {@link IStorageProvider} SERVICE. Those are
+ * automatically handled by the storage system.
*
* @param cc to be added cell provider
*/
diff --git a/src/main/java/appeng/api/parts/IPart.java b/src/main/java/appeng/api/parts/IPart.java
index 0a27b7bc673..b3634139637 100644
--- a/src/main/java/appeng/api/parts/IPart.java
+++ b/src/main/java/appeng/api/parts/IPart.java
@@ -32,8 +32,6 @@
import org.jetbrains.annotations.MustBeInvokedByOverriders;
import org.jetbrains.annotations.Nullable;
-import net.fabricmc.api.EnvType;
-import net.fabricmc.api.Environment;
import net.minecraft.CrashReportCategory;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.core.BlockPos;
@@ -51,6 +49,12 @@
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.phys.Vec3;
+import net.neoforged.api.distmarker.Dist;
+import net.neoforged.api.distmarker.OnlyIn;
+import net.neoforged.neoforge.client.model.data.ModelData;
+import net.neoforged.neoforge.common.capabilities.Capability;
+import net.neoforged.neoforge.common.capabilities.CapabilityProvider;
+import net.neoforged.neoforge.common.util.LazyOptional;
import appeng.api.networking.IGridNode;
import appeng.api.networking.IManagedGridNode;
@@ -73,7 +77,7 @@ public interface IPart extends ICustomCableConnection, Clearable {
* Render dynamic portions of this part, as part of the cable bus TESR. This part has to return true for
* {@link #requireDynamicRender()} in order for this method to be called.
*/
- @Environment(EnvType.CLIENT)
+ @OnlyIn(Dist.CLIENT)
default void renderDynamic(float partialTicks, PoseStack poseStack, MultiBufferSource buffers,
int combinedLightIn, int combinedOverlayIn) {
}
@@ -418,14 +422,25 @@ default IPartModel getStaticModels() {
};
}
+ /**
+ * Implement this method if your part exposes capabilitys. Any requests for capabilities on the cable bus will be
+ * forwarded to parts on the appropriate side.
+ *
+ * @see CapabilityProvider#getCapability(Capability, Direction)
+ *
+ * @return The capability
+ */
+ default LazyOptional getCapability(Capability capabilityClass) {
+ return LazyOptional.empty();
+ }
+
/**
* Additional model data to be passed to the models for rendering this part.
*
* @return The model data to pass to the model. Only useful if custom models are used.
*/
- @Nullable
- default Object getRenderAttachmentData() {
- return null;
+ default ModelData getModelData() {
+ return ModelData.EMPTY;
}
/**
diff --git a/src/main/java/appeng/api/parts/PartModels.java b/src/main/java/appeng/api/parts/PartModels.java
index 5d30ff32eda..157aec00bf0 100644
--- a/src/main/java/appeng/api/parts/PartModels.java
+++ b/src/main/java/appeng/api/parts/PartModels.java
@@ -29,7 +29,7 @@
* Allows registration of part models that can then be used in {@link IPart#getStaticModels()}.
*
* The models will automatically be added as dependencies to the model of the cable bus, and registered with
- * {@link ModelLoader#addSpecialModel(net.minecraft.resources.ResourceLocation)}.
+ * {@link ModelLoader#addSpecialModel(ResourceLocation)}.
*/
public final class PartModels {
diff --git a/src/main/java/appeng/api/stacks/AEFluidKey.java b/src/main/java/appeng/api/stacks/AEFluidKey.java
index 87501e42b28..98d64d5a55c 100644
--- a/src/main/java/appeng/api/stacks/AEFluidKey.java
+++ b/src/main/java/appeng/api/stacks/AEFluidKey.java
@@ -5,8 +5,6 @@
import org.jetbrains.annotations.Nullable;
-import net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants;
-import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant;
import net.minecraft.core.BlockPos;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.nbt.CompoundTag;
@@ -17,14 +15,15 @@
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.material.Fluid;
+import net.neoforged.neoforge.fluids.FluidStack;
import appeng.api.storage.AEKeyFilter;
import appeng.core.AELog;
import appeng.util.Platform;
public final class AEFluidKey extends AEKey {
- public static final int AMOUNT_BUCKET = (int) FluidConstants.BUCKET;
- public static final int AMOUNT_BLOCK = (int) FluidConstants.BLOCK;
+ public static final int AMOUNT_BUCKET = 1000;
+ public static final int AMOUNT_BLOCK = 1000;
private final Fluid fluid;
@Nullable
@@ -47,14 +46,14 @@ public static AEFluidKey of(Fluid fluid) {
}
@Nullable
- public static AEFluidKey of(FluidVariant fluidVariant) {
- if (fluidVariant.isBlank()) {
+ public static AEFluidKey of(FluidStack fluidVariant) {
+ if (fluidVariant.isEmpty()) {
return null;
}
- return of(fluidVariant.getFluid(), fluidVariant.getNbt());
+ return of(fluidVariant.getFluid(), fluidVariant.getTag());
}
- public static boolean matches(AEKey what, FluidVariant fluid) {
+ public static boolean matches(AEKey what, FluidStack fluid) {
return what instanceof AEFluidKey fluidKey && fluidKey.matches(fluid);
}
@@ -66,8 +65,8 @@ public static AEKeyFilter filter() {
return AEFluidKey::is;
}
- public boolean matches(FluidVariant variant) {
- return !variant.isBlank() && fluid.isSame(variant.getFluid()) && variant.nbtMatches(tag);
+ public boolean matches(FluidStack variant) {
+ return !variant.isEmpty() && fluid.isSame(variant.getFluid()) && Objects.equals(tag, variant.getTag());
}
@Override
@@ -147,8 +146,8 @@ public boolean isTagged(TagKey> tag) {
return fluid.builtInRegistryHolder().is((TagKey) tag);
}
- public FluidVariant toVariant() {
- return FluidVariant.of(fluid, tag);
+ public FluidStack toStack(int amount) {
+ return new FluidStack(fluid, amount, tag);
}
public Fluid getFluid() {
diff --git a/src/main/java/appeng/api/stacks/AEItemKey.java b/src/main/java/appeng/api/stacks/AEItemKey.java
index 78ffba09739..583c91a2f0b 100644
--- a/src/main/java/appeng/api/stacks/AEItemKey.java
+++ b/src/main/java/appeng/api/stacks/AEItemKey.java
@@ -1,5 +1,7 @@
package appeng.api.stacks;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
import java.lang.ref.WeakReference;
import java.util.List;
import java.util.Objects;
@@ -7,7 +9,6 @@
import org.jetbrains.annotations.Nullable;
-import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant;
import net.minecraft.core.BlockPos;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.nbt.CompoundTag;
@@ -20,21 +21,46 @@
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.ItemLike;
import net.minecraft.world.level.Level;
+import net.neoforged.neoforge.common.capabilities.CapabilityProvider;
import appeng.api.storage.AEKeyFilter;
import appeng.core.AELog;
import appeng.util.Platform;
public final class AEItemKey extends AEKey {
+ private static final MethodHandle SERIALIZE_CAPS_HANDLE;
+ static {
+ try {
+ var method = CapabilityProvider.class.getDeclaredMethod("serializeCaps");
+ method.setAccessible(true);
+ SERIALIZE_CAPS_HANDLE = MethodHandles.lookup().unreflect(method);
+ } catch (Exception exception) {
+ throw new RuntimeException("Failed to create serializeCaps method handle", exception);
+ }
+ }
+
+ @Nullable
+ private static CompoundTag serializeStackCaps(ItemStack stack) {
+ try {
+ var caps = (CompoundTag) SERIALIZE_CAPS_HANDLE.invokeExact((CapabilityProvider) stack);
+ // Ensure stacks with no serializable cap providers are treated the same as stacks with no caps!
+ return caps == null || caps.isEmpty() ? null : caps;
+ } catch (Throwable ex) {
+ throw new RuntimeException("Failed to call serializeCaps", ex);
+ }
+ }
+
private final Item item;
private final InternedTag internedTag;
+ private final InternedTag internedCaps;
private final int hashCode;
private final int cachedDamage;
- private AEItemKey(Item item, InternedTag internedTag) {
+ private AEItemKey(Item item, InternedTag internedTag, InternedTag internedCaps) {
this.item = item;
this.internedTag = internedTag;
- this.hashCode = item.hashCode() * 31 + internedTag.hashCode;
+ this.internedCaps = internedCaps;
+ this.hashCode = Objects.hash(item, internedTag, internedCaps);
if (internedTag.tag != null && internedTag.tag.get("Damage") instanceof NumericTag numericTag) {
this.cachedDamage = numericTag.getAsInt();
} else {
@@ -42,20 +68,12 @@ private AEItemKey(Item item, InternedTag internedTag) {
}
}
- @Nullable
- public static AEItemKey of(ItemVariant variant) {
- if (variant.isBlank()) {
- return null;
- }
- return of(variant.getItem(), variant.getNbt());
- }
-
@Nullable
public static AEItemKey of(ItemStack stack) {
if (stack.isEmpty()) {
return null;
}
- return of(stack.getItem(), stack.getTag());
+ return of(stack.getItem(), stack.getTag(), serializeStackCaps(stack));
}
public static boolean matches(AEKey what, ItemStack itemStack) {
@@ -87,7 +105,7 @@ public boolean equals(Object o) {
if (o == null || getClass() != o.getClass())
return false;
AEItemKey aeItemKey = (AEItemKey) o;
- return item == aeItemKey.item && internedTag == aeItemKey.internedTag;
+ return item == aeItemKey.item && internedTag == aeItemKey.internedTag && internedCaps == aeItemKey.internedCaps;
}
@Override
@@ -100,11 +118,17 @@ public static AEItemKey of(ItemLike item) {
}
public static AEItemKey of(ItemLike item, @Nullable CompoundTag tag) {
- return new AEItemKey(item.asItem(), InternedTag.of(tag, false));
+ return of(item, tag, null);
+ }
+
+ private static AEItemKey of(ItemLike item, @Nullable CompoundTag tag, @Nullable CompoundTag caps) {
+ return new AEItemKey(item.asItem(), InternedTag.of(tag, false), InternedTag.of(caps, false));
}
public boolean matches(ItemStack stack) {
- return !stack.isEmpty() && stack.is(item) && Objects.equals(stack.getTag(), internedTag.tag);
+ // TODO: remove or optimize cap check if it becomes too slow >:-(
+ return !stack.isEmpty() && stack.is(item) && Objects.equals(stack.getTag(), internedTag.tag)
+ && Objects.equals(serializeStackCaps(stack), internedCaps.tag);
}
public ItemStack toStack() {
@@ -116,9 +140,8 @@ public ItemStack toStack(int count) {
return ItemStack.EMPTY;
}
- var result = new ItemStack(item);
+ var result = new ItemStack(item, count, internedCaps.tag);
result.setTag(copyTag());
- result.setCount(count);
return result;
}
@@ -132,7 +155,8 @@ public static AEItemKey fromTag(CompoundTag tag) {
var item = BuiltInRegistries.ITEM.getOptional(new ResourceLocation(tag.getString("id")))
.orElseThrow(() -> new IllegalArgumentException("Unknown item id."));
var extraTag = tag.contains("tag") ? tag.getCompound("tag") : null;
- return of(item, extraTag);
+ var extraCaps = tag.contains("caps") ? tag.getCompound("caps") : null;
+ return of(item, extraTag, extraCaps);
} catch (Exception e) {
AELog.debug("Tried to load an invalid item key from NBT: %s", tag, e);
return null;
@@ -147,6 +171,9 @@ public CompoundTag toTag() {
if (internedTag.tag != null) {
result.put("tag", internedTag.tag.copy());
}
+ if (internedCaps.tag != null) {
+ result.put("caps", internedCaps.tag.copy());
+ }
return result;
}
@@ -177,10 +204,6 @@ public ResourceLocation getId() {
return BuiltInRegistries.ITEM.getKey(item);
}
- public ItemVariant toVariant() {
- return ItemVariant.of(item, internedTag.tag);
- }
-
/**
* @return NEVER MODIFY THE RETURNED TAG
*/
@@ -241,7 +264,7 @@ public void writeToPacket(FriendlyByteBuf data) {
data.writeVarInt(Item.getId(item));
CompoundTag compoundTag = null;
if (item.canBeDepleted() || item.shouldOverrideMultiplayerNbt()) {
- compoundTag = internedTag.tag;
+ compoundTag = item.getShareTag(toStack());
}
data.writeNbt(compoundTag);
}
@@ -249,8 +272,11 @@ public void writeToPacket(FriendlyByteBuf data) {
public static AEItemKey fromPacket(FriendlyByteBuf data) {
int i = data.readVarInt();
var item = Item.byId(i);
- var tag = data.readNbt();
- return new AEItemKey(item, InternedTag.of(tag, true));
+ var shareTag = data.readNbt();
+ var stack = new ItemStack(item);
+ stack.readShareTag(shareTag);
+ return new AEItemKey(item, InternedTag.of(stack.getTag(), true),
+ InternedTag.of(serializeStackCaps(stack), true));
}
@Override
diff --git a/src/main/java/appeng/api/stacks/AEKey.java b/src/main/java/appeng/api/stacks/AEKey.java
index f228fb3aef6..9b7c42605db 100644
--- a/src/main/java/appeng/api/stacks/AEKey.java
+++ b/src/main/java/appeng/api/stacks/AEKey.java
@@ -22,8 +22,8 @@
* Uniquely identifies something that "stacks" within an ME inventory.
*
* For example for items, this is the combination of an {@link net.minecraft.world.item.Item} and optional
- * {@link net.minecraft.nbt.CompoundTag}. To account for common indexing scenarios, a key is (optionally) split into a
- * primary and secondary component, which serves two purposes:
+ * {@link CompoundTag}. To account for common indexing scenarios, a key is (optionally) split into a primary and
+ * secondary component, which serves two purposes:
*
* - Fuzzy cards allow setting filters for the primary component of a key, i.e. for an
* {@link net.minecraft.world.item.Item}, while disregarding the compound tag.
diff --git a/src/main/java/appeng/api/stacks/AEKeyType.java b/src/main/java/appeng/api/stacks/AEKeyType.java
index 3822f4891e5..68fab28c909 100644
--- a/src/main/java/appeng/api/stacks/AEKeyType.java
+++ b/src/main/java/appeng/api/stacks/AEKeyType.java
@@ -72,7 +72,7 @@ public static AEKeyType items() {
@Nullable
public static AEKeyType fromRawId(int id) {
Preconditions.checkArgument(id >= 0 && id <= Byte.MAX_VALUE, "id out of range: %d", id);
- return AEKeyTypesInternal.getRegistry().byId(id);
+ return AEKeyTypesInternal.getRegistry().getValue(id);
}
/**
@@ -95,7 +95,7 @@ public final Class extends AEKey> getKeyClass() {
}
public final byte getRawId() {
- var id = AEKeyTypesInternal.getRegistry().getId(this);
+ var id = AEKeyTypesInternal.getRegistry().getID(this);
if (id < 0 || id > 127) {
throw new IllegalStateException("Key type " + this + " has an invalid numeric id: " + id);
}
@@ -231,7 +231,7 @@ private String formatShortAmount(long amount, int maxWidth) {
/**
* Returns all tags that apply to keys of this type. Is an optional operation is keys of this type cannot have tags,
* and {@link AEKey#isTagged(TagKey)} is not implemented for this key type.
- *
+ *
* @see Registry#getTagNames()
*/
public Stream> getTagNames() {
diff --git a/src/main/java/appeng/api/stacks/AEKeyTypes.java b/src/main/java/appeng/api/stacks/AEKeyTypes.java
index 7911e1285ff..a57377d3a2d 100644
--- a/src/main/java/appeng/api/stacks/AEKeyTypes.java
+++ b/src/main/java/appeng/api/stacks/AEKeyTypes.java
@@ -66,7 +66,7 @@ public static synchronized void register(AEKeyType keyType) {
*/
public static AEKeyType get(ResourceLocation id) {
- var result = AEKeyTypesInternal.getRegistry().get(id);
+ var result = AEKeyTypesInternal.getRegistry().getValue(id);
if (result == null) {
throw new IllegalArgumentException("No key type registered for id " + id);
}
diff --git a/src/main/java/appeng/api/stacks/AEKeyTypesInternal.java b/src/main/java/appeng/api/stacks/AEKeyTypesInternal.java
index 40298e629b3..8b59fa8e4b7 100644
--- a/src/main/java/appeng/api/stacks/AEKeyTypesInternal.java
+++ b/src/main/java/appeng/api/stacks/AEKeyTypesInternal.java
@@ -1,35 +1,36 @@
package appeng.api.stacks;
+import java.util.function.Supplier;
+
import com.google.common.base.Preconditions;
import org.jetbrains.annotations.ApiStatus;
-import org.jetbrains.annotations.Nullable;
-import net.minecraft.core.MappedRegistry;
-import net.minecraft.core.Registry;
+import net.neoforged.neoforge.registries.ForgeRegistry;
+import net.neoforged.neoforge.registries.IForgeRegistry;
/**
* Manages the registry used to synchronize key spaces to the client.
*/
@ApiStatus.Internal
public final class AEKeyTypesInternal {
- @Nullable
- private static MappedRegistry registry;
+ private static Supplier> registry;
private AEKeyTypesInternal() {
}
- public static Registry getRegistry() {
+ public static ForgeRegistry getRegistry() {
+ var registry = AEKeyTypesInternal.registry.get();
Preconditions.checkState(registry != null, "AE2 isn't initialized yet.");
- return registry;
+ return (ForgeRegistry) registry;
}
- public static void setRegistry(MappedRegistry registry) {
+ public static void setRegistry(Supplier> registry) {
Preconditions.checkState(AEKeyTypesInternal.registry == null);
AEKeyTypesInternal.registry = registry;
}
public static void register(AEKeyType keyType) {
- Registry.register(getRegistry(), keyType.getId(), keyType);
+ getRegistry().register(keyType.getId(), keyType);
}
}
diff --git a/src/main/java/appeng/api/stacks/GenericStack.java b/src/main/java/appeng/api/stacks/GenericStack.java
index 89d5c6b405d..94a36f9eff5 100644
--- a/src/main/java/appeng/api/stacks/GenericStack.java
+++ b/src/main/java/appeng/api/stacks/GenericStack.java
@@ -7,6 +7,7 @@
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.world.item.ItemStack;
+import net.neoforged.neoforge.fluids.FluidStack;
import appeng.items.misc.WrappedGenericStack;
@@ -82,6 +83,18 @@ public static GenericStack fromItemStack(ItemStack stack) {
return new GenericStack(key, stack.getCount());
}
+ /**
+ * Converts a given fluid stack into a generic stack. If the fluid stack is empty, null is returned.
+ */
+ @Nullable
+ public static GenericStack fromFluidStack(FluidStack stack) {
+ var key = AEFluidKey.of(stack);
+ if (key == null) {
+ return null;
+ }
+ return new GenericStack(key, stack.getAmount());
+ }
+
public static long getStackSizeOrZero(@Nullable GenericStack stack) {
return stack == null ? 0 : stack.amount;
}
diff --git a/src/main/java/appeng/api/storage/MEStorage.java b/src/main/java/appeng/api/storage/MEStorage.java
index 3284a9b8447..65c6253ce81 100644
--- a/src/main/java/appeng/api/storage/MEStorage.java
+++ b/src/main/java/appeng/api/storage/MEStorage.java
@@ -27,8 +27,6 @@
import com.google.common.base.Preconditions;
-import net.fabricmc.fabric.api.lookup.v1.block.BlockApiLookup;
-import net.minecraft.core.Direction;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.ItemStack;
@@ -36,7 +34,6 @@
import appeng.api.networking.security.IActionSource;
import appeng.api.stacks.AEKey;
import appeng.api.stacks.KeyCounter;
-import appeng.core.AppEng;
/**
* AE's Equivalent to IInventory, used to reading contents, and manipulating contents of ME Inventories.
@@ -53,9 +50,6 @@
* want to control their {@code MEStorage}.
*/
public interface MEStorage {
- BlockApiLookup SIDED = BlockApiLookup.get(
- AppEng.makeId("storage"), MEStorage.class, Direction.class);
-
/**
* Returns whether this inventory is the preferred storage location for the given stack when being compared to other
* inventories of the same overall priority.
diff --git a/src/main/java/appeng/api/storage/StorageCells.java b/src/main/java/appeng/api/storage/StorageCells.java
index efb1167090d..537371f6f90 100644
--- a/src/main/java/appeng/api/storage/StorageCells.java
+++ b/src/main/java/appeng/api/storage/StorageCells.java
@@ -54,8 +54,8 @@ private StorageCells() {
/**
* Register a new handler.
*
- * Never be call before {@link net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent} was handled by AE2. Will
- * throw an exception otherwise.
+ * Never be call before {@link net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent} was handled by AE2. Will throw
+ * an exception otherwise.
*
* @param handler cell handler
*/
diff --git a/src/main/java/appeng/api/upgrades/EmptyUpgradeInventory.java b/src/main/java/appeng/api/upgrades/EmptyUpgradeInventory.java
index 8852363e7ad..13862289627 100644
--- a/src/main/java/appeng/api/upgrades/EmptyUpgradeInventory.java
+++ b/src/main/java/appeng/api/upgrades/EmptyUpgradeInventory.java
@@ -21,12 +21,12 @@
import java.util.Collections;
import java.util.Iterator;
-import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant;
-import net.fabricmc.fabric.api.transfer.v1.storage.Storage;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.ItemLike;
+import net.neoforged.neoforge.items.IItemHandler;
+import net.neoforged.neoforge.items.wrapper.EmptyHandler;
final class EmptyUpgradeInventory implements IUpgradeInventory {
public static final EmptyUpgradeInventory INSTANCE = new EmptyUpgradeInventory();
@@ -57,8 +57,8 @@ public boolean isEmpty() {
}
@Override
- public Storage toStorage() {
- return Storage.empty();
+ public IItemHandler toItemHandler() {
+ return EmptyHandler.INSTANCE;
}
@Override
diff --git a/src/main/java/appeng/api/util/UnsupportedSettingException.java b/src/main/java/appeng/api/util/UnsupportedSettingException.java
index 6dac6d843d3..63840854146 100644
--- a/src/main/java/appeng/api/util/UnsupportedSettingException.java
+++ b/src/main/java/appeng/api/util/UnsupportedSettingException.java
@@ -3,7 +3,7 @@
import appeng.api.config.Setting;
/**
- * Thrown if {@link appeng.util.ConfigManager} is used with a {@link appeng.api.config.Setting} that was not previously
+ * Thrown if {@link appeng.util.ConfigManager} is used with a {@link Setting} that was not previously
* {@link appeng.util.ConfigManager#registerSetting(Setting, Enum) registered}.
*/
public class UnsupportedSettingException extends RuntimeException {
diff --git a/src/main/java/appeng/block/AEBaseBlock.java b/src/main/java/appeng/block/AEBaseBlock.java
index fe7c476a19b..c11221ebdf8 100644
--- a/src/main/java/appeng/block/AEBaseBlock.java
+++ b/src/main/java/appeng/block/AEBaseBlock.java
@@ -26,7 +26,6 @@
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.SoundType;
-import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.material.MapColor;
@@ -38,7 +37,7 @@
public abstract class AEBaseBlock extends Block implements IOrientableBlock {
- protected AEBaseBlock(BlockBehaviour.Properties props) {
+ protected AEBaseBlock(Properties props) {
super(props);
}
@@ -58,27 +57,27 @@ protected void createBlockStateDefinition(StateDefinition.Builder toolTip,
TooltipFlag advancedTooltips) {
this.addCheckedInformation(itemStack, level, toolTip, advancedTooltips);
}
- @Environment(EnvType.CLIENT)
+ @OnlyIn(Dist.CLIENT)
public void addCheckedInformation(ItemStack itemStack, Level level, List toolTip,
TooltipFlag advancedTooltips) {
this.blockType.appendHoverText(itemStack, level, toolTip, advancedTooltips);
}
+ @Override
+ public boolean isBookEnchantable(final ItemStack itemstack1, final ItemStack itemstack2) {
+ return false;
+ }
+
@Override
public String getDescriptionId(ItemStack is) {
return this.blockType.getDescriptionId();
diff --git a/src/main/java/appeng/block/AEBaseEntityBlock.java b/src/main/java/appeng/block/AEBaseEntityBlock.java
index 4e34f98d7c3..d0e2ec1308f 100644
--- a/src/main/java/appeng/block/AEBaseEntityBlock.java
+++ b/src/main/java/appeng/block/AEBaseEntityBlock.java
@@ -38,7 +38,6 @@
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityTicker;
import net.minecraft.world.level.block.entity.BlockEntityType;
-import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.storage.loot.LootParams;
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
@@ -72,7 +71,7 @@ public abstract class AEBaseEntityBlock extends AEB
@Nullable
private BlockEntityTicker clientTicker;
- public AEBaseEntityBlock(BlockBehaviour.Properties props) {
+ public AEBaseEntityBlock(Properties props) {
super(props);
}
diff --git a/src/main/java/appeng/block/crafting/AbstractCraftingUnitBlock.java b/src/main/java/appeng/block/crafting/AbstractCraftingUnitBlock.java
index 28b1897a9df..f5e4c846b8b 100644
--- a/src/main/java/appeng/block/crafting/AbstractCraftingUnitBlock.java
+++ b/src/main/java/appeng/block/crafting/AbstractCraftingUnitBlock.java
@@ -19,12 +19,14 @@
package appeng.block.crafting;
import net.minecraft.core.BlockPos;
+import net.minecraft.core.Direction;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
+import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.Block;
-import net.minecraft.world.level.block.state.BlockBehaviour;
+import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition.Builder;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
@@ -43,7 +45,7 @@ public abstract class AbstractCraftingUnitBlock e
public final ICraftingUnitType type;
- public AbstractCraftingUnitBlock(BlockBehaviour.Properties props, ICraftingUnitType type) {
+ public AbstractCraftingUnitBlock(Properties props, ICraftingUnitType type) {
super(props);
this.type = type;
this.registerDefaultState(defaultBlockState().setValue(FORMED, false).setValue(POWERED, false));
@@ -56,6 +58,16 @@ protected void createBlockStateDefinition(Builder builder) {
builder.add(FORMED);
}
+ @Override
+ public BlockState updateShape(BlockState stateIn, Direction facing, BlockState facingState, LevelAccessor level,
+ BlockPos currentPos, BlockPos facingPos) {
+ BlockEntity te = level.getBlockEntity(currentPos);
+ if (te != null) {
+ te.requestModelDataUpdate();
+ }
+ return super.updateShape(stateIn, facing, facingState, level, currentPos, facingPos);
+ }
+
@Override
public void neighborChanged(BlockState state, Level level, BlockPos pos, Block blockIn,
BlockPos fromPos, boolean isMoving) {
diff --git a/src/main/java/appeng/block/crafting/CraftingBlockItem.java b/src/main/java/appeng/block/crafting/CraftingBlockItem.java
index 59ce08a9800..13d6e4b800a 100644
--- a/src/main/java/appeng/block/crafting/CraftingBlockItem.java
+++ b/src/main/java/appeng/block/crafting/CraftingBlockItem.java
@@ -23,7 +23,6 @@
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResultHolder;
import net.minecraft.world.entity.player.Player;
-import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.ItemLike;
import net.minecraft.world.level.Level;
@@ -43,7 +42,7 @@ public class CraftingBlockItem extends AEBaseBlockItem {
*/
protected final Supplier disassemblyExtra;
- public CraftingBlockItem(Block id, Item.Properties props, Supplier disassemblyExtra) {
+ public CraftingBlockItem(Block id, Properties props, Supplier disassemblyExtra) {
super(id, props);
this.disassemblyExtra = disassemblyExtra;
}
diff --git a/src/main/java/appeng/block/crafting/MolecularAssemblerBlock.java b/src/main/java/appeng/block/crafting/MolecularAssemblerBlock.java
index 15407ba0177..bbcb108aaec 100644
--- a/src/main/java/appeng/block/crafting/MolecularAssemblerBlock.java
+++ b/src/main/java/appeng/block/crafting/MolecularAssemblerBlock.java
@@ -24,7 +24,6 @@
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
-import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition.Builder;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
@@ -41,7 +40,7 @@ public class MolecularAssemblerBlock extends AEBaseEntityBlock impl
private static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED;
- public InscriberBlock(BlockBehaviour.Properties props) {
+ public InscriberBlock(Properties props) {
super(props);
this.registerDefaultState(this.defaultBlockState().setValue(WATERLOGGED, false));
}
diff --git a/src/main/java/appeng/block/misc/LightDetectorBlock.java b/src/main/java/appeng/block/misc/LightDetectorBlock.java
index 86b7a7b6843..b27e59778ab 100644
--- a/src/main/java/appeng/block/misc/LightDetectorBlock.java
+++ b/src/main/java/appeng/block/misc/LightDetectorBlock.java
@@ -43,10 +43,8 @@
import appeng.block.AEBaseEntityBlock;
import appeng.blockentity.misc.LightDetectorBlockEntity;
-import appeng.hooks.INeighborChangeSensitive;
-public class LightDetectorBlock extends AEBaseEntityBlock
- implements INeighborChangeSensitive {
+public class LightDetectorBlock extends AEBaseEntityBlock {
// Cache VoxelShapes for each facing
private static final Map SHAPES;
diff --git a/src/main/java/appeng/block/misc/QuartzFixtureBlock.java b/src/main/java/appeng/block/misc/QuartzFixtureBlock.java
index d3068de32e2..c8cfe6bd102 100644
--- a/src/main/java/appeng/block/misc/QuartzFixtureBlock.java
+++ b/src/main/java/appeng/block/misc/QuartzFixtureBlock.java
@@ -23,8 +23,6 @@
import org.jetbrains.annotations.Nullable;
-import net.fabricmc.api.EnvType;
-import net.fabricmc.api.Environment;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.util.RandomSource;
@@ -47,6 +45,8 @@
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
+import net.neoforged.api.distmarker.Dist;
+import net.neoforged.api.distmarker.OnlyIn;
import appeng.api.orientation.RelativeSide;
import appeng.block.AEBaseBlock;
@@ -139,7 +139,7 @@ public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, Co
}
@Override
- @Environment(EnvType.CLIENT)
+ @OnlyIn(Dist.CLIENT)
public void animateTick(BlockState state, Level level, BlockPos pos, RandomSource r) {
if (!AEConfig.instance().isEnableEffects()) {
return;
diff --git a/src/main/java/appeng/block/misc/TinyTNTBlock.java b/src/main/java/appeng/block/misc/TinyTNTBlock.java
index 10cb6cf18ce..2d59d2ef01d 100644
--- a/src/main/java/appeng/block/misc/TinyTNTBlock.java
+++ b/src/main/java/appeng/block/misc/TinyTNTBlock.java
@@ -33,7 +33,6 @@
import net.minecraft.world.level.Explosion;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
-import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.BlockHitResult;
@@ -49,7 +48,7 @@ public class TinyTNTBlock extends AEBaseBlock {
private static final VoxelShape SHAPE = Shapes
.create(new AABB(0.25f, 0.0f, 0.25f, 0.75f, 0.5f, 0.75f));
- public TinyTNTBlock(BlockBehaviour.Properties props) {
+ public TinyTNTBlock(Properties props) {
super(props);
}
diff --git a/src/main/java/appeng/block/networking/CableBusBlock.java b/src/main/java/appeng/block/networking/CableBusBlock.java
index bf13d0716ce..758947a96ed 100644
--- a/src/main/java/appeng/block/networking/CableBusBlock.java
+++ b/src/main/java/appeng/block/networking/CableBusBlock.java
@@ -20,12 +20,11 @@
import java.util.ArrayList;
import java.util.List;
+import java.util.Objects;
+import java.util.function.Consumer;
import org.jetbrains.annotations.Nullable;
-import net.fabricmc.api.EnvType;
-import net.fabricmc.api.Environment;
-import net.fabricmc.fabric.api.rendering.data.v1.RenderAttachedBlockView;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.particle.Particle;
@@ -69,6 +68,8 @@
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
+import net.neoforged.neoforge.client.extensions.common.IClientBlockExtensions;
+import net.neoforged.neoforge.client.model.data.ModelData;
import appeng.api.parts.IFacadeContainer;
import appeng.api.parts.IFacadePart;
@@ -78,18 +79,12 @@
import appeng.client.render.cablebus.CableBusBakedModel;
import appeng.client.render.cablebus.CableBusBreakingParticle;
import appeng.client.render.cablebus.CableBusRenderState;
-import appeng.hooks.ICustomBlockDestroyEffect;
-import appeng.hooks.ICustomBlockHitEffect;
-import appeng.hooks.ICustomPickBlock;
-import appeng.hooks.IDynamicLadder;
-import appeng.hooks.INeighborChangeSensitive;
import appeng.integration.abstraction.IAEFacade;
import appeng.parts.ICableBusContainer;
import appeng.parts.NullCableBusContainer;
import appeng.util.Platform;
-public class CableBusBlock extends AEBaseEntityBlock implements IAEFacade, SimpleWaterloggedBlock,
- ICustomBlockHitEffect, ICustomBlockDestroyEffect, INeighborChangeSensitive, IDynamicLadder, ICustomPickBlock {
+public class CableBusBlock extends AEBaseEntityBlock implements IAEFacade, SimpleWaterloggedBlock {
private static final ICableBusContainer NULL_CABLE_BUS = new NullCableBusContainer();
@@ -176,9 +171,9 @@ public boolean canBeReplaced(BlockState state, BlockPlaceContext useContext) {
&& this.cb(useContext.getLevel(), useContext.getClickedPos()).isEmpty();
}
- // TODO-1.17 This hook was removed from Forge with replacement and may be unnecessary
- public boolean canConnectRedstone(BlockGetter level, BlockPos pos, Direction side) {
- // TODO: Verify this.
+ @Override
+ public boolean canConnectRedstone(BlockState state, BlockGetter level, BlockPos pos,
+ @Nullable Direction side) {
if (side == null) {
return false;
}
@@ -187,7 +182,7 @@ public boolean canConnectRedstone(BlockGetter level, BlockPos pos, Direction sid
}
@Override
- public ItemStack getPickBlock(BlockState state, HitResult target, BlockGetter level, BlockPos pos,
+ public ItemStack getCloneItemStack(BlockState state, HitResult target, BlockGetter level, BlockPos pos,
Player player) {
var v3 = target.getLocation().subtract(pos.getX(), pos.getY(), pos.getZ());
var sp = this.cb(level, pos).selectPartLocal(v3);
@@ -328,93 +323,98 @@ public BlockState updateShape(BlockState blockState, Direction facing, BlockStat
return super.updateShape(blockState, facing, facingState, level, currentPos, facingPos);
}
- @Environment(EnvType.CLIENT)
@Override
- public boolean addHitEffects(BlockState state, Level level, HitResult target,
- ParticleEngine effectRenderer) {
-
- // Half the particle rate. Since we're spawning concentrated on a specific spot,
- // our particle effect otherwise looks too strong
- if (Platform.getRandom().nextBoolean()) {
- return true;
- }
-
- if (target.getType() != Type.BLOCK) {
- return false;
- }
- BlockPos blockPos = BlockPos.containing(target.getLocation().x, target.getLocation().y, target.getLocation().z);
-
- ICableBusContainer cb = cb(level, blockPos);
+ public void initializeClient(Consumer consumer) {
+ consumer.accept(new IClientBlockExtensions() {
- // Our built-in model has the actual baked sprites we need
- BakedModel model = Minecraft.getInstance().getBlockRenderer()
- .getBlockModel(defaultBlockState());
+ @Override
+ public boolean addHitEffects(BlockState state, Level level, HitResult target,
+ ParticleEngine effectRenderer) {
- // We cannot add the effect if we don't have the model
- if (!(model instanceof CableBusBakedModel cableBusModel)) {
- return true;
- }
+ // Half the particle rate. Since we're spawning concentrated on a specific spot,
+ // our particle effect otherwise looks too strong
+ if (Platform.getRandom().nextBoolean()) {
+ return true;
+ }
- CableBusRenderState renderState = cb.getRenderState();
-
- // Spawn a particle for one of the particle textures
- TextureAtlasSprite texture = Platform.pickRandom(cableBusModel.getParticleTextures(renderState));
- if (texture != null) {
- double x = target.getLocation().x;
- double y = target.getLocation().y;
- double z = target.getLocation().z;
- // FIXME: Check how this looks, probably like shit, maybe provide parts the
- // ability to supply particle textures???
- effectRenderer.add(
- new CableBusBreakingParticle((ClientLevel) level, x, y, z, texture).scale(0.8F));
- }
+ if (target.getType() != Type.BLOCK) {
+ return false;
+ }
+ BlockPos blockPos = BlockPos.containing(target.getLocation().x, target.getLocation().y,
+ target.getLocation().z);
- return true;
- }
+ ICableBusContainer cb = cb(level, blockPos);
- @Environment(EnvType.CLIENT)
- @Override
- public boolean addDestroyEffects(BlockState state, Level level, BlockPos pos,
- ParticleEngine effectRenderer) {
- ICableBusContainer cb = cb(level, pos);
+ // Our built-in model has the actual baked sprites we need
+ BakedModel model = Minecraft.getInstance().getBlockRenderer()
+ .getBlockModel(defaultBlockState());
- // Our built-in model has the actual baked sprites we need
- BakedModel model = Minecraft.getInstance().getBlockRenderer()
- .getBlockModel(defaultBlockState());
+ // We cannot add the effect if we don't have the model
+ if (!(model instanceof CableBusBakedModel cableBusModel)) {
+ return true;
+ }
- // We cannot add the effect if we dont have the model
- if (!(model instanceof CableBusBakedModel cableBusModel)) {
- return true;
- }
+ CableBusRenderState renderState = cb.getRenderState();
+
+ // Spawn a particle for one of the particle textures
+ TextureAtlasSprite texture = Platform.pickRandom(cableBusModel.getParticleTextures(renderState));
+ if (texture != null) {
+ double x = target.getLocation().x;
+ double y = target.getLocation().y;
+ double z = target.getLocation().z;
+ // FIXME: Check how this looks, probably like shit, maybe provide parts the
+ // ability to supply particle textures???
+ effectRenderer.add(
+ new CableBusBreakingParticle((ClientLevel) level, x, y, z, texture).scale(0.8F));
+ }
- CableBusRenderState renderState = cb.getRenderState();
+ return true;
+ }
- List textures = cableBusModel.getParticleTextures(renderState);
+ @Override
+ public boolean addDestroyEffects(BlockState state, Level level, BlockPos pos,
+ ParticleEngine effectRenderer) {
+ ICableBusContainer cb = cb(level, pos);
- if (!textures.isEmpty()) {
- // Shamelessly inspired by ParticleManager.addBlockDestroyEffects
- for (int j = 0; j < 4; ++j) {
- for (int k = 0; k < 4; ++k) {
- for (int l = 0; l < 4; ++l) {
- // Randomly select one of the textures if the cable bus has more than just one
- // possibility here
- final TextureAtlasSprite texture = Platform.pickRandom(textures);
+ // Our built-in model has the actual baked sprites we need
+ BakedModel model = Minecraft.getInstance().getBlockRenderer()
+ .getBlockModel(defaultBlockState());
- final double x = pos.getX() + (j + 0.5D) / 4.0D;
- final double y = pos.getY() + (k + 0.5D) / 4.0D;
- final double z = pos.getZ() + (l + 0.5D) / 4.0D;
+ // We cannot add the effect if we dont have the model
+ if (!(model instanceof CableBusBakedModel cableBusModel)) {
+ return true;
+ }
- // FIXME: Check how this looks, probably like shit, maybe provide parts the
- // ability to supply particle textures???
- Particle effect = new CableBusBreakingParticle((ClientLevel) level, x, y, z,
- x - pos.getX() - 0.5D, y - pos.getY() - 0.5D, z - pos.getZ() - 0.5D, texture);
- effectRenderer.add(effect);
+ CableBusRenderState renderState = cb.getRenderState();
+
+ List textures = cableBusModel.getParticleTextures(renderState);
+
+ if (!textures.isEmpty()) {
+ // Shamelessly inspired by ParticleManager.addBlockDestroyEffects
+ for (int j = 0; j < 4; ++j) {
+ for (int k = 0; k < 4; ++k) {
+ for (int l = 0; l < 4; ++l) {
+ // Randomly select one of the textures if the cable bus has more than just one
+ // possibility here
+ final TextureAtlasSprite texture = Platform.pickRandom(textures);
+
+ final double x = pos.getX() + (j + 0.5D) / 4.0D;
+ final double y = pos.getY() + (k + 0.5D) / 4.0D;
+ final double z = pos.getZ() + (l + 0.5D) / 4.0D;
+
+ // FIXME: Check how this looks, probably like shit, maybe provide parts the
+ // ability to supply particle textures???
+ Particle effect = new CableBusBreakingParticle((ClientLevel) level, x, y, z,
+ x - pos.getX() - 0.5D, y - pos.getY() - 0.5D, z - pos.getZ() - 0.5D, texture);
+ effectRenderer.add(effect);
+ }
+ }
}
}
- }
- }
- return true;
+ return true;
+ }
+ });
}
/**
@@ -435,8 +435,18 @@ public boolean addDestroyEffects(BlockState state, Level level, BlockPos pos,
@Override
public BlockState getAppearance(BlockState state, BlockAndTintGetter renderView, BlockPos pos, Direction side,
@Nullable BlockState sourceState, @Nullable BlockPos sourcePos) {
- if (((RenderAttachedBlockView) renderView)
- .getBlockEntityRenderAttachment(pos) instanceof CableBusRenderState cableBusRenderState) {
+ ModelData modelData;
+ var modelDataManager = renderView.getModelDataManager();
+ if (modelDataManager == null) {
+ // We're on the server, use BE directly
+ BlockEntity be = renderView.getBlockEntity(pos);
+ modelData = be != null ? be.getModelData() : ModelData.EMPTY;
+ } else {
+ modelData = Objects.requireNonNullElse(modelDataManager.getAt(pos), ModelData.EMPTY);
+ }
+
+ CableBusRenderState cableBusRenderState = modelData.get(CableBusRenderState.PROPERTY);
+ if (cableBusRenderState != null) {
var renderingFacadeDir = RENDERING_FACADE_DIRECTION.get();
var facades = cableBusRenderState.getFacades();
diff --git a/src/main/java/appeng/block/networking/CableBusColor.java b/src/main/java/appeng/block/networking/CableBusColor.java
index 27a44babeec..66ea37c3ba6 100644
--- a/src/main/java/appeng/block/networking/CableBusColor.java
+++ b/src/main/java/appeng/block/networking/CableBusColor.java
@@ -20,13 +20,13 @@
import org.jetbrains.annotations.Nullable;
-import net.fabricmc.api.EnvType;
-import net.fabricmc.api.Environment;
import net.minecraft.client.color.block.BlockColor;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
+import net.neoforged.api.distmarker.Dist;
+import net.neoforged.api.distmarker.OnlyIn;
import appeng.api.util.AEColor;
import appeng.blockentity.networking.CableBusBlockEntity;
@@ -35,7 +35,7 @@
/**
* Exposes the cable bus color as tint indices 0 (dark variant), 1 (medium variant) and 2 (bright variant).
*/
-@Environment(EnvType.CLIENT)
+@OnlyIn(Dist.CLIENT)
public class CableBusColor implements BlockColor {
@Override
diff --git a/src/main/java/appeng/block/networking/ControllerBlock.java b/src/main/java/appeng/block/networking/ControllerBlock.java
index f1f77408618..5a57da904b5 100644
--- a/src/main/java/appeng/block/networking/ControllerBlock.java
+++ b/src/main/java/appeng/block/networking/ControllerBlock.java
@@ -150,7 +150,7 @@ private static boolean isController(LevelAccessor level, int x, int y, int z) {
@Override
public InteractionResult onActivated(Level level, BlockPos pos, Player player, InteractionHand hand,
- @org.jetbrains.annotations.Nullable ItemStack heldItem, BlockHitResult hit) {
+ @Nullable ItemStack heldItem, BlockHitResult hit) {
var controller = getBlockEntity(level, pos);
if (controller != null) {
if (!level.isClientSide) {
diff --git a/src/main/java/appeng/block/networking/EnergyCellBlockItem.java b/src/main/java/appeng/block/networking/EnergyCellBlockItem.java
index 3d12716b0d1..cb182b5dbb8 100644
--- a/src/main/java/appeng/block/networking/EnergyCellBlockItem.java
+++ b/src/main/java/appeng/block/networking/EnergyCellBlockItem.java
@@ -20,15 +20,14 @@
import java.util.List;
-import net.fabricmc.api.EnvType;
-import net.fabricmc.api.Environment;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
-import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
+import net.neoforged.api.distmarker.Dist;
+import net.neoforged.api.distmarker.OnlyIn;
import appeng.api.config.AccessRestriction;
import appeng.api.config.Actionable;
@@ -38,12 +37,12 @@
public class EnergyCellBlockItem extends AEBaseBlockItem implements IAEItemPowerStorage {
- public EnergyCellBlockItem(Block block, Item.Properties props) {
+ public EnergyCellBlockItem(Block block, Properties props) {
super(block, props);
}
@Override
- @Environment(EnvType.CLIENT)
+ @OnlyIn(Dist.CLIENT)
public void addCheckedInformation(ItemStack stack, Level level, List lines,
TooltipFlag advancedTooltips) {
double internalCurrentPower = 0;
diff --git a/src/main/java/appeng/block/paint/PaintSplotchesBakedModel.java b/src/main/java/appeng/block/paint/PaintSplotchesBakedModel.java
index b8ba6307bc6..a0b4f30b2b1 100644
--- a/src/main/java/appeng/block/paint/PaintSplotchesBakedModel.java
+++ b/src/main/java/appeng/block/paint/PaintSplotchesBakedModel.java
@@ -18,33 +18,29 @@
package appeng.block.paint;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
-import java.util.function.Supplier;
import com.google.common.collect.ImmutableList;
import org.jetbrains.annotations.Nullable;
-import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
-import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
-import net.fabricmc.fabric.api.rendering.data.v1.RenderAttachedBlockView;
+import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.block.model.ItemOverrides;
-import net.minecraft.client.renderer.block.model.ItemTransforms;
import net.minecraft.client.renderer.texture.TextureAtlas;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
-import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.client.resources.model.Material;
-import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.RandomSource;
-import net.minecraft.world.item.ItemStack;
-import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.block.state.BlockState;
+import net.neoforged.neoforge.client.model.IDynamicBakedModel;
+import net.neoforged.neoforge.client.model.data.ModelData;
+import appeng.blockentity.misc.PaintSplotchesBlockEntity;
import appeng.client.render.cablebus.CubeBuilder;
import appeng.core.AppEng;
import appeng.helpers.Splotch;
@@ -53,7 +49,7 @@
* Renders paint blocks, which render multiple "splotches" that have been applied to the sides of adjacent blocks using
* a matter cannon with paint balls.
*/
-class PaintSplotchesBakedModel implements BakedModel, FabricBakedModel {
+class PaintSplotchesBakedModel implements IDynamicBakedModel {
private static final Material TEXTURE_PAINT1 = new Material(TextureAtlas.LOCATION_BLOCKS,
new ResourceLocation(AppEng.MOD_ID, "block/paint1"));
@@ -70,23 +66,28 @@ class PaintSplotchesBakedModel implements BakedModel, FabricBakedModel {
}
@Override
- public boolean isVanillaAdapter() {
- return false;
- }
+ public List getQuads(@Nullable BlockState state, @Nullable Direction side, RandomSource rand,
+ ModelData extraData, RenderType renderType) {
- @Override
- public void emitBlockQuads(BlockAndTintGetter blockView, BlockState state, BlockPos pos,
- Supplier randomSupplier, RenderContext context) {
+ if (side != null) {
+ return Collections.emptyList();
+ }
- Object renderAttachment = ((RenderAttachedBlockView) blockView).getBlockEntityRenderAttachment(pos);
- if (!(renderAttachment instanceof PaintSplotches)) {
- return;
+ PaintSplotches splotchesState = extraData.get(PaintSplotchesBlockEntity.SPLOTCHES);
+
+ if (splotchesState == null) {
+ // This is the inventory model which should usually not be used other than in
+ // special cases
+ List quads = new ArrayList<>(1);
+ CubeBuilder builder = new CubeBuilder(quads);
+ builder.setTexture(this.textures[0]);
+ builder.addCube(0, 0, 0, 16, 16, 16);
+ return quads;
}
- PaintSplotches splotchesState = (PaintSplotches) renderAttachment;
List splotches = splotchesState.getSplotches();
- CubeBuilder builder = new CubeBuilder(context.getEmitter());
+ CubeBuilder builder = new CubeBuilder();
float offsetConstant = 0.001f;
for (Splotch s : splotches) {
@@ -151,20 +152,8 @@ public void emitBlockQuads(BlockAndTintGetter blockView, BlockState state, Block
default:
}
}
- }
- @Override
- public void emitItemQuads(ItemStack stack, Supplier randomSupplier, RenderContext context) {
- }
-
- @Override
- public List getQuads(@Nullable BlockState state, @Nullable Direction face, RandomSource random) {
- return Collections.emptyList();
- }
-
- @Override
- public ItemTransforms getTransforms() {
- return ItemTransforms.NO_TRANSFORMS;
+ return builder.getOutput();
}
@Override
diff --git a/src/main/java/appeng/block/qnb/QnbFormedBakedModel.java b/src/main/java/appeng/block/qnb/QnbFormedBakedModel.java
index 1562b062629..e15cb6494b1 100644
--- a/src/main/java/appeng/block/qnb/QnbFormedBakedModel.java
+++ b/src/main/java/appeng/block/qnb/QnbFormedBakedModel.java
@@ -18,41 +18,37 @@
package appeng.block.qnb;
+import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
-import java.util.function.Supplier;
import com.google.common.collect.ImmutableList;
import org.jetbrains.annotations.Nullable;
-import net.fabricmc.fabric.api.renderer.v1.mesh.QuadEmitter;
-import net.fabricmc.fabric.api.renderer.v1.model.FabricBakedModel;
-import net.fabricmc.fabric.api.renderer.v1.render.RenderContext;
-import net.fabricmc.fabric.api.rendering.data.v1.RenderAttachedBlockView;
+import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.block.model.ItemOverrides;
-import net.minecraft.client.renderer.block.model.ItemTransforms;
import net.minecraft.client.renderer.texture.TextureAtlas;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.client.resources.model.BakedModel;
import net.minecraft.client.resources.model.Material;
-import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.RandomSource;
-import net.minecraft.world.item.ItemStack;
-import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
+import net.neoforged.neoforge.client.model.IDynamicBakedModel;
+import net.neoforged.neoforge.client.model.data.ModelData;
+import appeng.blockentity.qnb.QuantumBridgeBlockEntity;
import appeng.client.render.cablebus.CubeBuilder;
import appeng.core.AppEng;
import appeng.core.definitions.AEBlocks;
-class QnbFormedBakedModel implements BakedModel, FabricBakedModel {
+class QnbFormedBakedModel implements IDynamicBakedModel {
private static final Material TEXTURE_LINK = new Material(TextureAtlas.LOCATION_BLOCKS,
new ResourceLocation(AppEng.MOD_ID, "block/quantum_link"));
@@ -100,51 +96,23 @@ public QnbFormedBakedModel(BakedModel baseModel, Function randomSupplier, RenderContext context) {
-
- }
-
- @Override
- public void emitBlockQuads(BlockAndTintGetter blockView, BlockState state, BlockPos pos,
- Supplier randomSupplier, RenderContext context) {
- QnbFormedState formedState = getState(blockView, pos);
+ public List getQuads(@Nullable BlockState state, @Nullable Direction side, RandomSource rand,
+ ModelData modelData, RenderType renderType) {
+ QnbFormedState formedState = modelData.get(QuantumBridgeBlockEntity.FORMED_STATE);
if (formedState == null) {
- context.bakedModelConsumer().accept(this.baseModel);
- return;
+ return this.baseModel.getQuads(state, side, rand);
}
- buildQuads(context.getEmitter(), formedState, state);
-
- }
-
- @Override
- public List getQuads(@Nullable BlockState state, @Nullable Direction face, RandomSource random) {
- return baseModel.getQuads(state, face, random);
- }
-
- @Override
- public ItemTransforms getTransforms() {
- return ItemTransforms.NO_TRANSFORMS;
- }
-
- private static QnbFormedState getState(BlockAndTintGetter view, BlockPos pos) {
- if (view instanceof RenderAttachedBlockView renderAttachedBlockView) {
- Object attachment = renderAttachedBlockView.getBlockEntityRenderAttachment(pos);
- if (attachment instanceof QnbFormedState qnbFormedState) {
- return qnbFormedState;
- }
+ if (side != null) {
+ return Collections.emptyList();
}
- return null;
+
+ return this.getQuads(formedState, state);
}
- private void buildQuads(QuadEmitter emitter, QnbFormedState formedState, BlockState state) {
- CubeBuilder builder = new CubeBuilder(emitter);
+ private List getQuads(QnbFormedState formedState, BlockState state) {
+ CubeBuilder builder = new CubeBuilder();
if (state.getBlock() == this.linkBlock) {
Set sides = formedState.getAdjacentQuantumBridges();
@@ -207,6 +175,8 @@ private void buildQuads(QuadEmitter emitter, QnbFormedState formedState, BlockSt
}
}
}
+
+ return builder.getOutput();
}
private void renderCableAt(CubeBuilder builder, float thickness, TextureAtlasSprite texture, float pull,
diff --git a/src/main/java/appeng/block/qnb/QuantumBaseBlock.java b/src/main/java/appeng/block/qnb/QuantumBaseBlock.java
index 7846830909a..a908e93b7ea 100644
--- a/src/main/java/appeng/block/qnb/QuantumBaseBlock.java
+++ b/src/main/java/appeng/block/qnb/QuantumBaseBlock.java
@@ -28,7 +28,6 @@
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.SimpleWaterloggedBlock;
-import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition.Builder;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
@@ -56,7 +55,7 @@ public abstract class QuantumBaseBlock extends AEBaseEntityBlock, Item> REPRESENTATIVE_ITEMS = new HashMap<>();
@Nullable
@@ -90,7 +85,7 @@ protected void onChunkUnloaded() {
private boolean setChangedQueued = false;
/**
* For diagnosing issues with the delayed block entity initialization, this tracks how often this BE has been queued
- * for defered initializiation using {@link appeng.api.networking.GridHelper#onFirstTick}.
+ * for defered initializiation using {@link GridHelper#onFirstTick}.
*/
private byte queuedForReady = 0;
/**
@@ -100,7 +95,7 @@ protected void onChunkUnloaded() {
private byte readyInvoked = 0;
// Remove in 1.20.1+: Convert legacy NBT orientation to blockstate
- @org.jetbrains.annotations.Nullable
+ @Nullable
private BlockOrientation pendingOrientationChange;
public AEBaseBlockEntity(BlockEntityType> blockEntityType, BlockPos pos, BlockState blockState) {
@@ -139,6 +134,7 @@ public final void load(CompoundTag tag) {
if (readUpdateData(new FriendlyByteBuf(Unpooled.wrappedBuffer(updateData)))) {
// Triggers a chunk re-render if the level is already loaded
if (level != null) {
+ requestModelDataUpdate();
level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 0);
}
}
@@ -271,6 +267,9 @@ protected void loadVisualState(CompoundTag data) {
}
public void markForUpdate() {
+ // Clearing the cached model-data is always harmless regardless of status
+ this.requestModelDataUpdate();
+
// TODO: Optimize Network Load
if (this.level != null && !this.isRemoved() && !notLoaded()) {
@@ -421,10 +420,19 @@ public InternalInventory getSubInventory(ResourceLocation id) {
return null;
}
- @Nullable
@Override
- public Object getRenderAttachmentData() {
- return new AEModelData();
+ public ModelData getModelData() {
+ return AEModelData.create();
+ }
+
+ /**
+ * AE Block entities will generally confine themselves to rendering within the bounding block. Forge however would
+ * retrieve the collision box here, which is very expensive.
+ */
+ @OnlyIn(Dist.CLIENT)
+ @Override
+ public AABB getRenderBoundingBox() {
+ return new AABB(worldPosition, worldPosition.offset(1, 1, 1));
}
/**
diff --git a/src/main/java/appeng/blockentity/AEBaseInvBlockEntity.java b/src/main/java/appeng/blockentity/AEBaseInvBlockEntity.java
index f66e0839d68..71bb388e88f 100644
--- a/src/main/java/appeng/blockentity/AEBaseInvBlockEntity.java
+++ b/src/main/java/appeng/blockentity/AEBaseInvBlockEntity.java
@@ -27,6 +27,9 @@
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
+import net.neoforged.neoforge.common.capabilities.Capabilities;
+import net.neoforged.neoforge.common.capabilities.Capability;
+import net.neoforged.neoforge.common.util.LazyOptional;
import appeng.api.inventories.InternalInventory;
import appeng.api.stacks.GenericStack;
@@ -97,8 +100,21 @@ public void clearContent() {
@Override
public abstract void onChangeInventory(InternalInventory inv, int slot);
- public InternalInventory getExposedInventoryForSide(Direction side) {
+ protected InternalInventory getExposedInventoryForSide(Direction side) {
return this.getInternalInventory();
}
+ @SuppressWarnings("unchecked")
+ @Override
+ public LazyOptional getCapability(Capability capability, Direction facing) {
+ if (capability == Capabilities.ITEM_HANDLER) {
+ if (facing == null) {
+ return (LazyOptional) LazyOptional.of(getInternalInventory()::toItemHandler);
+ } else {
+ return (LazyOptional) LazyOptional.of(() -> getExposedInventoryForSide(facing).toItemHandler());
+ }
+ }
+ return super.getCapability(capability, facing);
+ }
+
}
diff --git a/src/main/java/appeng/blockentity/DeferredBlockEntityUnloader.java b/src/main/java/appeng/blockentity/DeferredBlockEntityUnloader.java
deleted file mode 100644
index 34b2f5df48b..00000000000
--- a/src/main/java/appeng/blockentity/DeferredBlockEntityUnloader.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * This file is part of Applied Energistics 2.
- * Copyright (c) 2013 - 2014, AlgorithmX2, All rights reserved.
- *
- * Applied Energistics 2 is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Lesser General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * Applied Energistics 2 is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with Applied Energistics 2. If not, see .
- */
-package appeng.blockentity;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents;
-import net.minecraft.world.level.Level;
-import net.minecraft.world.level.block.entity.BlockEntity;
-
-import appeng.hooks.ticking.TickHandler;
-import appeng.util.ILevelRunnable;
-
-/**
- * We need to defer actually unloading tile entities until the end of the tick, after the chunk has been saved to disk.
- * The CHUNK_UNLOAD event runs before the chunk has been saved, and if we disconnect nodes at that point, the saved data
- * will be missing information from the node (such as the player id).
- */
-class DeferredBlockEntityUnloader implements ILevelRunnable {
-
- public static void register() {
- ServerChunkEvents.CHUNK_UNLOAD.register((serverWorld, worldChunk) -> {
- List entitiesToRemove = null;
- for (BlockEntity value : worldChunk.getBlockEntities().values()) {
- if (value instanceof AEBaseBlockEntity) {
- if (entitiesToRemove == null) {
- entitiesToRemove = new ArrayList<>();
- }
- entitiesToRemove.add((AEBaseBlockEntity) value);
- }
- }
- if (entitiesToRemove != null) {
- TickHandler.instance().addCallable(serverWorld, new DeferredBlockEntityUnloader(entitiesToRemove));
- }
- });
- }
-
- private final List entitiesToRemove;
-
- public DeferredBlockEntityUnloader(List entitiesToRemove) {
- this.entitiesToRemove = entitiesToRemove;
- }
-
- @Override
- public void call(Level world) {
- for (AEBaseBlockEntity blockEntity : entitiesToRemove) {
- blockEntity.onChunkUnloaded();
- }
- }
-
-}
diff --git a/src/main/java/appeng/blockentity/crafting/CraftingBlockEntity.java b/src/main/java/appeng/blockentity/crafting/CraftingBlockEntity.java
index 489f1768f3d..2e8a881a1fd 100644
--- a/src/main/java/appeng/blockentity/crafting/CraftingBlockEntity.java
+++ b/src/main/java/appeng/blockentity/crafting/CraftingBlockEntity.java
@@ -35,6 +35,7 @@
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
+import net.neoforged.neoforge.client.model.data.ModelData;
import appeng.api.implementations.IPowerChannelState;
import appeng.api.networking.GridFlags;
@@ -289,8 +290,8 @@ public void setPreviousState(CompoundTag previousState) {
}
@Override
- public Object getRenderAttachmentData() {
- return new CraftingCubeModelData(getConnections());
+ public ModelData getModelData() {
+ return CraftingCubeModelData.create(getConnections());
}
protected EnumSet getConnections() {
@@ -314,6 +315,16 @@ private boolean isConnected(BlockGetter level, BlockPos pos, Direction side) {
return level.getBlockState(adjacentPos).getBlock() instanceof AbstractCraftingUnitBlock;
}
+ /**
+ * When the block state changes (i.e. becoming formed or unformed), we need to update the model data since it
+ * contains connections to neighboring block entities.
+ */
+ @Override
+ public void setBlockState(BlockState state) {
+ super.setBlockState(state);
+ requestModelDataUpdate();
+ }
+
private Iterator getMultiblockNodes() {
if (this.getCluster() == null) {
return new ChainedIterator<>();
diff --git a/src/main/java/appeng/blockentity/crafting/CraftingCubeModelData.java b/src/main/java/appeng/blockentity/crafting/CraftingCubeModelData.java
index bed5ae76c4b..522f1a05695 100644
--- a/src/main/java/appeng/blockentity/crafting/CraftingCubeModelData.java
+++ b/src/main/java/appeng/blockentity/crafting/CraftingCubeModelData.java
@@ -19,49 +19,29 @@
package appeng.blockentity.crafting;
import java.util.EnumSet;
-import java.util.Objects;
import net.minecraft.core.Direction;
+import net.neoforged.neoforge.client.model.data.ModelData;
+import net.neoforged.neoforge.client.model.data.ModelProperty;
import appeng.client.render.model.AEModelData;
-public class CraftingCubeModelData extends AEModelData {
+public final class CraftingCubeModelData {
// Contains information on which sides of the block are connected to other parts
// of a formed crafting cube
- private final EnumSet connections;
+ public static final ModelProperty> CONNECTIONS = new ModelProperty<>();
- public CraftingCubeModelData(EnumSet connections) {
- this.connections = Objects.requireNonNull(connections);
+ private CraftingCubeModelData() {
}
- @Override
- public boolean isCacheable() {
- return false; // Too many variants
+ public static ModelData.Builder builder(EnumSet connections) {
+ return AEModelData.builder()
+ .with(AEModelData.SKIP_CACHE, true)
+ .with(CONNECTIONS, connections);
}
- public EnumSet getConnections() {
- return connections;
+ public static ModelData create(EnumSet connections) {
+ return builder(connections).build();
}
-
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- if (!super.equals(o)) {
- return false;
- }
- CraftingCubeModelData that = (CraftingCubeModelData) o;
- return connections.equals(that.connections);
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(super.hashCode(), connections);
- }
-
}
diff --git a/src/main/java/appeng/blockentity/crafting/CraftingMonitorBlockEntity.java b/src/main/java/appeng/blockentity/crafting/CraftingMonitorBlockEntity.java
index 9fda807ef39..e169d7aa4d7 100644
--- a/src/main/java/appeng/blockentity/crafting/CraftingMonitorBlockEntity.java
+++ b/src/main/java/appeng/blockentity/crafting/CraftingMonitorBlockEntity.java
@@ -29,6 +29,7 @@
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
+import net.neoforged.neoforge.client.model.data.ModelData;
import appeng.api.implementations.blockentities.IColorableBlockEntity;
import appeng.api.stacks.GenericStack;
@@ -106,7 +107,7 @@ public boolean recolourBlock(Direction side, AEColor newPaintedColor, Player who
}
@Override
- public Object getRenderAttachmentData() {
- return new CraftingMonitorModelData(getConnections(), getColor());
+ public ModelData getModelData() {
+ return CraftingMonitorModelData.create(getConnections(), getColor());
}
}
diff --git a/src/main/java/appeng/blockentity/crafting/CraftingMonitorModelData.java b/src/main/java/appeng/blockentity/crafting/CraftingMonitorModelData.java
index c5b9e4964e4..9ee9214b58f 100644
--- a/src/main/java/appeng/blockentity/crafting/CraftingMonitorModelData.java
+++ b/src/main/java/appeng/blockentity/crafting/CraftingMonitorModelData.java
@@ -22,40 +22,21 @@
import java.util.Objects;
import net.minecraft.core.Direction;
+import net.neoforged.neoforge.client.model.data.ModelData;
+import net.neoforged.neoforge.client.model.data.ModelProperty;
import appeng.api.util.AEColor;
-public class CraftingMonitorModelData extends CraftingCubeModelData {
+public final class CraftingMonitorModelData {
+ public static final ModelProperty COLOR = new ModelProperty<>();
- private final AEColor color;
-
- public CraftingMonitorModelData(EnumSet connections, AEColor color) {
- super(connections);
- this.color = Objects.requireNonNull(color);
- }
-
- public AEColor getColor() {
- return color;
+ public static ModelData.Builder builder(EnumSet connections,
+ AEColor color) {
+ return CraftingCubeModelData.builder(connections)
+ .with(COLOR, Objects.requireNonNull(color));
}
- @Override
- public boolean equals(Object o) {
- if (this == o) {
- return true;
- }
- if (o == null || getClass() != o.getClass()) {
- return false;
- }
- if (!super.equals(o)) {
- return false;
- }
- CraftingMonitorModelData that = (CraftingMonitorModelData) o;
- return color == that.color;
+ public static ModelData create(EnumSet connections, AEColor color) {
+ return builder(connections, color).build();
}
-
- @Override
- public int hashCode() {
- return Objects.hash(super.hashCode(), color);
- }
-
}
diff --git a/src/main/java/appeng/blockentity/crafting/MolecularAssemblerBlockEntity.java b/src/main/java/appeng/blockentity/crafting/MolecularAssemblerBlockEntity.java
index 9cbd019e607..13745cb81e4 100644
--- a/src/main/java/appeng/blockentity/crafting/MolecularAssemblerBlockEntity.java
+++ b/src/main/java/appeng/blockentity/crafting/MolecularAssemblerBlockEntity.java
@@ -22,8 +22,6 @@
import org.jetbrains.annotations.Nullable;
-import net.fabricmc.api.EnvType;
-import net.fabricmc.api.Environment;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
@@ -37,6 +35,10 @@
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
+import net.neoforged.api.distmarker.Dist;
+import net.neoforged.api.distmarker.OnlyIn;
+import net.neoforged.neoforge.common.capabilities.Capability;
+import net.neoforged.neoforge.common.util.LazyOptional;
import appeng.api.config.Actionable;
import appeng.api.config.PowerMultiplier;
@@ -59,6 +61,7 @@
import appeng.api.upgrades.UpgradeInventories;
import appeng.api.util.AECableType;
import appeng.blockentity.grid.AENetworkInvBlockEntity;
+import appeng.capabilities.Capabilities;
import appeng.client.render.crafting.AssemblerAnimationStatus;
import appeng.core.AELog;
import appeng.core.AppEng;
@@ -99,7 +102,7 @@ public class MolecularAssemblerBlockEntity extends AENetworkInvBlockEntity
private boolean forcePlan = false;
private boolean reboot = true;
- @Environment(EnvType.CLIENT)
+ @OnlyIn(Dist.CLIENT)
private AssemblerAnimationStatus animationStatus;
public MolecularAssemblerBlockEntity(BlockEntityType> blockEntityType, BlockPos pos, BlockState blockState) {
@@ -344,7 +347,7 @@ public InternalInventory getInternalInventory() {
}
@Override
- public InternalInventory getExposedInventoryForSide(Direction side) {
+ protected InternalInventory getExposedInventoryForSide(Direction side) {
return this.gridInvExt;
}
@@ -564,12 +567,12 @@ public boolean isActive() {
return this.isPowered;
}
- @Environment(EnvType.CLIENT)
+ @OnlyIn(Dist.CLIENT)
public void setAnimationStatus(@Nullable AssemblerAnimationStatus status) {
this.animationStatus = status;
}
- @Environment(EnvType.CLIENT)
+ @OnlyIn(Dist.CLIENT)
@Nullable
public AssemblerAnimationStatus getAnimationStatus() {
return this.animationStatus;
@@ -580,6 +583,15 @@ public IUpgradeInventory getUpgrades() {
return upgrades;
}
+ @Override
+ public LazyOptional getCapability(Capability capability, Direction facing) {
+ if (Capabilities.CRAFTING_MACHINE == capability) {
+ return Capabilities.CRAFTING_MACHINE.orEmpty(capability, LazyOptional.of(() -> this));
+ }
+
+ return super.getCapability(capability, facing);
+ }
+
@Nullable
public IMolecularAssemblerSupportedPattern getCurrentPattern() {
if (isClientSide()) {
diff --git a/src/main/java/appeng/blockentity/crafting/PatternProviderBlockEntity.java b/src/main/java/appeng/blockentity/crafting/PatternProviderBlockEntity.java
index aa239ac4a4b..978295fbf42 100644
--- a/src/main/java/appeng/blockentity/crafting/PatternProviderBlockEntity.java
+++ b/src/main/java/appeng/blockentity/crafting/PatternProviderBlockEntity.java
@@ -34,6 +34,8 @@
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
+import net.neoforged.neoforge.common.capabilities.Capability;
+import net.neoforged.neoforge.common.util.LazyOptional;
import appeng.api.networking.IGridNodeListener;
import appeng.api.orientation.BlockOrientation;
@@ -159,7 +161,7 @@ public AEItemKey getTerminalIcon() {
@Override
public void exportSettings(SettingsFrom mode, CompoundTag output,
- @org.jetbrains.annotations.Nullable Player player) {
+ @Nullable Player player) {
super.exportSettings(mode, output, player);
if (mode == SettingsFrom.MEMORY_CARD) {
@@ -172,7 +174,7 @@ public void exportSettings(SettingsFrom mode, CompoundTag output,
@Override
public void importSettings(SettingsFrom mode, CompoundTag input,
- @org.jetbrains.annotations.Nullable Player player) {
+ @Nullable Player player) {
super.importSettings(mode, input, player);
if (mode == SettingsFrom.MEMORY_CARD) {
@@ -193,6 +195,15 @@ public void importSettings(SettingsFrom mode, CompoundTag input,
}
}
+ @Override
+ public LazyOptional getCapability(Capability cap, @javax.annotation.Nullable Direction side) {
+ var lo = logic.getCapability(cap);
+ if (lo.isPresent()) {
+ return lo;
+ }
+ return super.getCapability(cap, side);
+ }
+
@Override
public ItemStack getMainMenuIcon() {
return AEBlocks.PATTERN_PROVIDER.stack();
diff --git a/src/main/java/appeng/blockentity/misc/ChargerBlockEntity.java b/src/main/java/appeng/blockentity/misc/ChargerBlockEntity.java
index 559400f419c..8abed5a5a21 100644
--- a/src/main/java/appeng/blockentity/misc/ChargerBlockEntity.java
+++ b/src/main/java/appeng/blockentity/misc/ChargerBlockEntity.java
@@ -33,6 +33,8 @@
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
+import net.neoforged.neoforge.common.capabilities.Capability;
+import net.neoforged.neoforge.common.util.LazyOptional;
import appeng.api.config.Actionable;
import appeng.api.config.PowerMultiplier;
@@ -50,6 +52,7 @@
import appeng.api.util.AECableType;
import appeng.api.util.DimensionalBlockPos;
import appeng.blockentity.grid.AENetworkPowerBlockEntity;
+import appeng.capabilities.Capabilities;
import appeng.core.AEConfig;
import appeng.core.settings.TickRates;
import appeng.util.Platform;
@@ -253,6 +256,21 @@ public ICrankable getCrankable(Direction direction) {
return null;
}
+ @Override
+ public LazyOptional getCapability(Capability capability, Direction facing) {
+ if (Capabilities.CRANKABLE.equals(capability)) {
+ var crankable = getCrankable(facing);
+ if (crankable == null) {
+ return LazyOptional.empty();
+ }
+ return Capabilities.CRANKABLE.orEmpty(
+ capability,
+ LazyOptional.of(() -> crankable));
+ }
+
+ return super.getCapability(capability, facing);
+ }
+
private record ChargerInvFilter(ChargerBlockEntity chargerBlockEntity) implements IAEItemFilter {
@Override
diff --git a/src/main/java/appeng/blockentity/misc/ChargerRecipes.java b/src/main/java/appeng/blockentity/misc/ChargerRecipes.java
index a3018f045c9..5f30a00c8c2 100644
--- a/src/main/java/appeng/blockentity/misc/ChargerRecipes.java
+++ b/src/main/java/appeng/blockentity/misc/ChargerRecipes.java
@@ -3,21 +3,22 @@
import org.jetbrains.annotations.Nullable;
import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.item.crafting.RecipeHolder;
import net.minecraft.world.level.Level;
import appeng.recipes.handlers.ChargerRecipe;
public class ChargerRecipes {
- public static Iterable getRecipes(Level level) {
+ public static Iterable> getRecipes(Level level) {
return level.getRecipeManager().byType(ChargerRecipe.TYPE).values();
}
@Nullable
public static ChargerRecipe findRecipe(Level level, ItemStack input) {
- for (ChargerRecipe recipe : getRecipes(level)) {
- if (recipe.ingredient.test(input)) {
- return recipe;
+ for (var recipe : getRecipes(level)) {
+ if (recipe.value().ingredient.test(input)) {
+ return recipe.value();
}
}
diff --git a/src/main/java/appeng/blockentity/misc/CondenserBlockEntity.java b/src/main/java/appeng/blockentity/misc/CondenserBlockEntity.java
index b7f998ad517..f0645abea28 100644
--- a/src/main/java/appeng/blockentity/misc/CondenserBlockEntity.java
+++ b/src/main/java/appeng/blockentity/misc/CondenserBlockEntity.java
@@ -18,20 +18,19 @@
package appeng.blockentity.misc;
-import java.util.Collections;
-import java.util.Iterator;
-
-import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant;
-import net.fabricmc.fabric.api.transfer.v1.storage.Storage;
-import net.fabricmc.fabric.api.transfer.v1.storage.StorageView;
-import net.fabricmc.fabric.api.transfer.v1.storage.base.InsertionOnlyStorage;
-import net.fabricmc.fabric.api.transfer.v1.transaction.TransactionContext;
-import net.fabricmc.fabric.api.transfer.v1.transaction.base.SnapshotParticipant;
+import org.jetbrains.annotations.Nullable;
+
import net.minecraft.core.BlockPos;
+import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
+import net.neoforged.neoforge.common.capabilities.Capability;
+import net.neoforged.neoforge.common.util.LazyOptional;
+import net.neoforged.neoforge.fluids.FluidStack;
+import net.neoforged.neoforge.fluids.IFluidTank;
+import net.neoforged.neoforge.fluids.capability.IFluidHandler;
import appeng.api.config.CondenserOutput;
import appeng.api.config.Settings;
@@ -39,11 +38,11 @@
import appeng.api.inventories.BaseInternalInventory;
import appeng.api.inventories.InternalInventory;
import appeng.api.stacks.AEFluidKey;
-import appeng.api.stacks.AEKeyType;
import appeng.api.storage.MEStorage;
import appeng.api.util.IConfigManager;
import appeng.api.util.IConfigurableObject;
import appeng.blockentity.AEBaseInvBlockEntity;
+import appeng.capabilities.Capabilities;
import appeng.core.definitions.AEItems;
import appeng.util.ConfigManager;
import appeng.util.inv.AppEngInternalInventory;
@@ -63,12 +62,12 @@ public class CondenserBlockEntity extends AEBaseInvBlockEntity implements IConfi
private final AppEngInternalInventory outputSlot = new AppEngInternalInventory(this, 1);
private final AppEngInternalInventory storageSlot = new AppEngInternalInventory(this, 1);
private final InternalInventory inputSlot = new CondenseItemHandler();
- private final Storage fluidHandler = new FluidHandler();
+ private final IFluidHandler fluidHandler = new FluidHandler();
/**
* This is used to expose a fake ME subnetwork that is only composed of this condenser. The purpose of this is to
- * enable the condenser to override the {@link appeng.api.storage.MEStorage#isPreferredStorageFor} method to make
- * sure a condenser is only ever used if an item can't go anywhere else.
+ * enable the condenser to override the {@link MEStorage#isPreferredStorageFor} method to make sure a condenser is
+ * only ever used if an item can't go anywhere else.
*/
private final CondenserMEStorage meStorage = new CondenserMEStorage(this);
@@ -185,7 +184,7 @@ public InternalInventory getExternalInv() {
return externalInv;
}
- public Storage getFluidHandler() {
+ public IFluidHandler getFluidHandler() {
return fluidHandler;
}
@@ -193,6 +192,19 @@ public MEStorage getMEStorage() {
return meStorage;
}
+ @SuppressWarnings("unchecked")
+ @Override
+ public LazyOptional getCapability(Capability capability, @Nullable Direction facing) {
+ if (capability == net.neoforged.neoforge.common.capabilities.Capabilities.ITEM_HANDLER) {
+ return (LazyOptional) LazyOptional.of(this.externalInv::toItemHandler);
+ } else if (capability == net.neoforged.neoforge.common.capabilities.Capabilities.FLUID_HANDLER) {
+ return (LazyOptional) LazyOptional.of(() -> this.fluidHandler);
+ } else if (capability == Capabilities.STORAGE) {
+ return (LazyOptional) LazyOptional.of(() -> this.meStorage);
+ }
+ return super.getCapability(capability, facing);
+ }
+
private class CondenseItemHandler extends BaseInternalInventory {
@Override
public int size() {
@@ -231,37 +243,71 @@ public ItemStack extractItem(int slot, int amount, boolean simulate) {
* A fluid handler that exposes a 1 bucket tank that can only be filled, and - when filled - will add power to this
* condenser.
*/
- private class FluidHandler extends SnapshotParticipant implements InsertionOnlyStorage {
- private double pendingEnergy = 0;
+ private class FluidHandler implements IFluidTank, IFluidHandler {
+
+ @Override
+ public FluidStack getFluid() {
+ return FluidStack.EMPTY;
+ }
+
+ @Override
+ public int getFluidAmount() {
+ return 0;
+ }
+
+ @Override
+ public int getCapacity() {
+ return AEFluidKey.AMOUNT_BUCKET;
+ }
+
+ @Override
+ public boolean isFluidValid(FluidStack stack) {
+ return !stack.isEmpty();
+ }
@Override
- public long insert(FluidVariant resource, long maxAmount, TransactionContext transaction) {
- // We allow up to a bucket per insert
- var amount = Math.min(AEFluidKey.AMOUNT_BUCKET, maxAmount);
- updateSnapshots(transaction);
- pendingEnergy += amount / AEKeyType.fluids().getAmountPerOperation();
+ public int fill(FluidStack resource, FluidAction action) {
+ int amount = resource.isEmpty() ? 0 : Math.min(resource.getAmount(), AEFluidKey.AMOUNT_BUCKET);
+
+ if (action == FluidAction.EXECUTE) {
+ var what = AEFluidKey.of(resource);
+ if (what != null) {
+ var transferFactor = (double) what.getAmountPerOperation();
+ CondenserBlockEntity.this.addPower(amount / transferFactor);
+ }
+ }
+
return amount;
}
@Override
- public Iterator> iterator() {
- return Collections.emptyIterator();
+ public FluidStack drain(int maxDrain, FluidAction action) {
+ return FluidStack.EMPTY;
+ }
+
+ @Override
+ public FluidStack drain(FluidStack resource, FluidAction action) {
+ return FluidStack.EMPTY;
+ }
+
+ @Override
+ public int getTanks() {
+ return 1;
}
@Override
- protected Double createSnapshot() {
- return pendingEnergy;
+ public FluidStack getFluidInTank(int tank) {
+ return FluidStack.EMPTY;
}
@Override
- protected void readSnapshot(Double snapshot) {
- pendingEnergy = snapshot;
+ public int getTankCapacity(int tank) {
+ return tank == 0 ? getCapacity() : 0;
}
@Override
- protected void onFinalCommit() {
- CondenserBlockEntity.this.addPower(pendingEnergy);
- pendingEnergy = 0;
+ public boolean isFluidValid(int tank, FluidStack stack) {
+ return tank == 0 && isFluidValid(stack);
}
}
}
diff --git a/src/main/java/appeng/blockentity/misc/GrowthAcceleratorBlockEntity.java b/src/main/java/appeng/blockentity/misc/GrowthAcceleratorBlockEntity.java
index 2d3faafb25c..21f3c8f5034 100644
--- a/src/main/java/appeng/blockentity/misc/GrowthAcceleratorBlockEntity.java
+++ b/src/main/java/appeng/blockentity/misc/GrowthAcceleratorBlockEntity.java
@@ -27,6 +27,8 @@
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
+import net.neoforged.neoforge.common.capabilities.Capability;
+import net.neoforged.neoforge.common.util.LazyOptional;
import appeng.api.ids.AETags;
import appeng.api.implementations.IPowerChannelState;
@@ -40,6 +42,7 @@
import appeng.api.orientation.RelativeSide;
import appeng.api.util.AECableType;
import appeng.blockentity.grid.AENetworkBlockEntity;
+import appeng.capabilities.Capabilities;
import appeng.core.AEConfig;
public class GrowthAcceleratorBlockEntity extends AENetworkBlockEntity implements IPowerChannelState {
@@ -156,6 +159,21 @@ public ICrankable getCrankable(Direction direction) {
return null;
}
+ @Override
+ public LazyOptional getCapability(Capability capability, Direction facing) {
+ if (Capabilities.CRANKABLE.equals(capability)) {
+ var crankable = getCrankable(facing);
+ if (crankable == null) {
+ return LazyOptional.empty();
+ }
+ return Capabilities.CRANKABLE.orEmpty(
+ capability,
+ LazyOptional.of(() -> crankable));
+ }
+
+ return super.getCapability(capability, facing);
+ }
+
class Crankable implements ICrankable {
@Override
public boolean canTurn() {
diff --git a/src/main/java/appeng/blockentity/misc/InscriberBlockEntity.java b/src/main/java/appeng/blockentity/misc/InscriberBlockEntity.java
index 9b4e2147924..e5fc7d15815 100644
--- a/src/main/java/appeng/blockentity/misc/InscriberBlockEntity.java
+++ b/src/main/java/appeng/blockentity/misc/InscriberBlockEntity.java
@@ -35,6 +35,8 @@
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
+import net.neoforged.neoforge.common.capabilities.Capability;
+import net.neoforged.neoforge.common.util.LazyOptional;
import appeng.api.config.Actionable;
import appeng.api.config.PowerMultiplier;
@@ -60,6 +62,7 @@
import appeng.api.util.IConfigManager;
import appeng.api.util.IConfigurableObject;
import appeng.blockentity.grid.AENetworkPowerBlockEntity;
+import appeng.capabilities.Capabilities;
import appeng.core.definitions.AEBlocks;
import appeng.core.definitions.AEItems;
import appeng.core.settings.TickRates;
@@ -424,7 +427,7 @@ private boolean isSeparateSides() {
}
@Override
- public InternalInventory getExposedInventoryForSide(Direction facing) {
+ protected InternalInventory getExposedInventoryForSide(Direction facing) {
if (isSeparateSides()) {
if (facing == this.getTop()) {
return this.topItemHandlerExtern;
@@ -516,7 +519,7 @@ private void setProcessingTime(int processingTime) {
/**
* Allow cranking from any side other than the front.
*/
- @org.jetbrains.annotations.Nullable
+ @Nullable
public ICrankable getCrankable(Direction direction) {
if (direction != getFront()) {
return new Crankable();
@@ -524,6 +527,21 @@ public ICrankable getCrankable(Direction direction) {
return null;
}
+ @Override
+ public LazyOptional getCapability(Capability capability, Direction facing) {
+ if (Capabilities.CRANKABLE.equals(capability)) {
+ var crankable = getCrankable(facing);
+ if (crankable == null) {
+ return LazyOptional.empty();
+ }
+ return Capabilities.CRANKABLE.orEmpty(
+ capability,
+ LazyOptional.of(() -> crankable));
+ }
+
+ return super.getCapability(capability, facing);
+ }
+
public class BaseFilter implements IAEItemFilter {
@Override
public boolean allowInsert(InternalInventory inv, int slot, ItemStack stack) {
@@ -559,7 +577,8 @@ public boolean allowInsert(InternalInventory inv, int slot, ItemStack stack) {
if (inv == topItemHandler)
top = stack;
- for (var recipe : InscriberRecipes.getRecipes(getLevel())) {
+ for (var holder : InscriberRecipes.getRecipes(level)) {
+ var recipe = holder.value();
if (!middle.isEmpty() && !recipe.getMiddleInput().test(middle)) {
continue;
}
diff --git a/src/main/java/appeng/blockentity/misc/InscriberRecipes.java b/src/main/java/appeng/blockentity/misc/InscriberRecipes.java
index 197bc15f662..2c64deaea7b 100644
--- a/src/main/java/appeng/blockentity/misc/InscriberRecipes.java
+++ b/src/main/java/appeng/blockentity/misc/InscriberRecipes.java
@@ -22,12 +22,11 @@
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
-import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.Ingredient;
+import net.minecraft.world.item.crafting.RecipeHolder;
import net.minecraft.world.level.Level;
-import appeng.core.AppEng;
import appeng.core.definitions.AEItems;
import appeng.items.materials.NamePressItem;
import appeng.recipes.handlers.InscriberProcessType;
@@ -39,15 +38,13 @@
*/
public final class InscriberRecipes {
- public static final ResourceLocation NAMEPLATE_RECIPE_ID = new ResourceLocation(AppEng.MOD_ID, "nameplate");
-
private InscriberRecipes() {
}
/**
* Returns an unmodifiable view of all registered inscriber recipes.
*/
- public static Iterable getRecipes(Level level) {
+ public static Iterable> getRecipes(Level level) {
return level.getRecipeManager().byType(InscriberRecipe.TYPE).values();
}
@@ -65,7 +62,8 @@ public static InscriberRecipe findRecipe(Level level, ItemStack input, ItemStack
}
}
- for (InscriberRecipe recipe : getRecipes(level)) {
+ for (var holder : getRecipes(level)) {
+ var recipe = holder.value();
// The recipe can be flipped at will
final boolean matchA = recipe.getTopOptional().test(plateA) && recipe.getBottomOptional().test(plateB);
final boolean matchB = recipe.getTopOptional().test(plateB) && recipe.getBottomOptional().test(plateA);
@@ -102,7 +100,7 @@ private static InscriberRecipe makeNamePressRecipe(ItemStack input, ItemStack pl
final InscriberProcessType type = InscriberProcessType.INSCRIBE;
- return new InscriberRecipe(NAMEPLATE_RECIPE_ID, startingItem, renamedItem,
+ return new InscriberRecipe(startingItem, renamedItem,
plateA.isEmpty() ? Ingredient.EMPTY : Ingredient.of(plateA),
plateB.isEmpty() ? Ingredient.EMPTY : Ingredient.of(plateB), type);
}
@@ -112,7 +110,8 @@ private static InscriberRecipe makeNamePressRecipe(ItemStack input, ItemStack pl
* combination and the reverse will be searched.
*/
public static boolean isValidOptionalIngredientCombination(Level level, ItemStack pressA, ItemStack pressB) {
- for (InscriberRecipe recipe : getRecipes(level)) {
+ for (var holder : getRecipes(level)) {
+ var recipe = holder.value();
if (recipe.getTopOptional().test(pressA) && recipe.getBottomOptional().test(pressB)
|| recipe.getTopOptional().test(pressB) && recipe.getBottomOptional().test(pressA)) {
return true;
@@ -127,7 +126,8 @@ public static boolean isValidOptionalIngredientCombination(Level level, ItemStac
* top can be used interchangeably here, because the inscriber will flip the recipe if needed.
*/
public static boolean isValidOptionalIngredient(Level level, ItemStack is) {
- for (InscriberRecipe recipe : getRecipes(level)) {
+ for (var holder : getRecipes(level)) {
+ var recipe = holder.value();
if (recipe.getTopOptional().test(is) || recipe.getBottomOptional().test(is)) {
return true;
}
diff --git a/src/main/java/appeng/blockentity/misc/InterfaceBlockEntity.java b/src/main/java/appeng/blockentity/misc/InterfaceBlockEntity.java
index 16e0aa521a5..ee037bd4a05 100644
--- a/src/main/java/appeng/blockentity/misc/InterfaceBlockEntity.java
+++ b/src/main/java/appeng/blockentity/misc/InterfaceBlockEntity.java
@@ -30,6 +30,8 @@
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
+import net.neoforged.neoforge.common.capabilities.Capability;
+import net.neoforged.neoforge.common.util.LazyOptional;
import appeng.api.inventories.InternalInventory;
import appeng.api.networking.GridHelper;
@@ -117,6 +119,15 @@ public ItemStack getMainMenuIcon() {
return AEBlocks.INTERFACE.stack();
}
+ @Override
+ public LazyOptional getCapability(Capability capability, @Nullable Direction facing) {
+ LazyOptional result = this.logic.getCapability(capability, facing);
+ if (result.isPresent()) {
+ return result;
+ }
+ return super.getCapability(capability, facing);
+ }
+
@Nullable
@Override
public InternalInventory getSubInventory(ResourceLocation id) {
diff --git a/src/main/java/appeng/blockentity/misc/PaintSplotchesBlockEntity.java b/src/main/java/appeng/blockentity/misc/PaintSplotchesBlockEntity.java
index a8bb8eb7cc7..d4d9c715aa0 100644
--- a/src/main/java/appeng/blockentity/misc/PaintSplotchesBlockEntity.java
+++ b/src/main/java/appeng/blockentity/misc/PaintSplotchesBlockEntity.java
@@ -33,6 +33,8 @@
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
+import net.neoforged.neoforge.client.model.data.ModelData;
+import net.neoforged.neoforge.client.model.data.ModelProperty;
import appeng.api.util.AEColor;
import appeng.block.paint.PaintSplotches;
@@ -43,6 +45,8 @@
public class PaintSplotchesBlockEntity extends AEBaseBlockEntity {
+ public static final ModelProperty SPLOTCHES = new ModelProperty<>();
+
private List