Skip to content

Commit

Permalink
Remove tunnel type, allow for addon P2P tunnels. (#5622)
Browse files Browse the repository at this point in the history
By removing TunnelType and switching to a system where the tunnel type is communicated via the part item, addons should now be able to add new P2P tunnels that work like the built-in ones in terms of attunement.

Addons will still need to depend on internals for the actual tunnel item.
  • Loading branch information
shartte committed Oct 31, 2021
1 parent 62618fd commit 1908f6c
Show file tree
Hide file tree
Showing 6 changed files with 181 additions and 280 deletions.
33 changes: 0 additions & 33 deletions src/main/java/appeng/api/config/TunnelType.java

This file was deleted.

159 changes: 102 additions & 57 deletions src/main/java/appeng/api/features/P2PTunnelAttunement.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
import java.util.function.Function;

import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.ThreadSafe;

import com.google.common.base.Preconditions;
Expand All @@ -40,10 +39,14 @@
import net.fabricmc.fabric.api.transfer.v1.context.ContainerItemContext;
import net.fabricmc.fabric.api.transfer.v1.item.base.SingleStackStorage;
import net.minecraft.core.Registry;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.ItemLike;

import appeng.api.config.TunnelType;
import appeng.core.definitions.AEParts;
import appeng.items.parts.PartItem;
import appeng.parts.p2p.P2PTunnelPart;

/**
* A Registry for how p2p Tunnels are attuned
Expand All @@ -52,55 +55,86 @@
public final class P2PTunnelAttunement {
private static final int INITIAL_CAPACITY = 40;

static final Map<ItemStack, TunnelType> tunnels = new HashMap<>(INITIAL_CAPACITY);
static final Map<String, TunnelType> modIdTunnels = new HashMap<>(INITIAL_CAPACITY);
static final Map<Item, Item> tunnels = new HashMap<>(INITIAL_CAPACITY);
static final Map<String, Item> modIdTunnels = new HashMap<>(INITIAL_CAPACITY);
static final List<ApiAttunement<?>> apiAttunements = new ArrayList<>();

/**
* The default tunnel part for ME tunnels. Use this to register additional attunement options.
*/
public static final ItemLike ME_TUNNEL = AEParts.ME_P2P_TUNNEL;

/**
* The default tunnel part for energy (i.e. Forge Energy) tunnels. Use this to register additional attunement
* options.
*/
public static final ItemLike ENERGY_TUNNEL = AEParts.FE_P2P_TUNNEL;

/**
* The default tunnel part for redstone tunnels. Use this to register additional attunement options.
*/
public static final ItemLike REDSTONE_TUNNEL = AEParts.REDSTONE_P2P_TUNNEL;

/**
* The default tunnel part for fluid tunnels. Use this to register additional attunement options.
*/
public static final ItemLike FLUID_TUNNEL = AEParts.FLUID_P2P_TUNNEL;

/**
* The default tunnel part for item tunnels. Use this to register additional attunement options.
*/
public static final ItemLike ITEM_TUNNEL = AEParts.ITEM_P2P_TUNNEL;

/**
* The default tunnel part for light tunnels. Use this to register additional attunement options.
*/
public static final ItemLike LIGHT_TUNNEL = AEParts.LIGHT_P2P_TUNNEL;

private P2PTunnelAttunement() {
}

/**
* Allows third parties to register items from their mod as potential attunements for AE's P2P Tunnels
*
* @param trigger - the item which triggers attunement
* @param type - the type of tunnel
* @param trigger - the item which triggers attunement
* @param tunnelPart The P2P-tunnel part item.
*/
public synchronized static void addNewAttunement(@Nonnull ItemStack trigger, @Nonnull TunnelType type) {
public synchronized static void addItem(@Nonnull ItemLike trigger, @Nonnull ItemLike tunnelPart) {
Objects.requireNonNull(trigger, "trigger");
Objects.requireNonNull(type, "type");
Preconditions.checkArgument(!trigger.isEmpty(), "!trigger.isEmpty()");
tunnels.put(trigger, type);
Item triggerItem = trigger.asItem();
Objects.requireNonNull(triggerItem, "trigger.asItem()");
Preconditions.checkArgument(triggerItem != Items.AIR, "trigger shouldn't be air!");
tunnels.put(triggerItem, validateTunnelPartItem(tunnelPart));
}

public synchronized static void addNewAttunement(@Nonnull ItemLike trigger, @Nonnull TunnelType type) {
Objects.requireNonNull(trigger, "trigger");
addNewAttunement(new ItemStack(trigger), type);
}

public synchronized static void addNewAttunement(@Nonnull String modId, @Nonnull TunnelType type) {
/**
* Adds all items from the given mod as attunement items for the given tunnel part.
*
* @param modId The mod-id that triggers attunement into the given tunnel part.
* @param tunnelPart The P2P-tunnel part item.
*/
public synchronized static void addItemByMod(@Nonnull String modId, @Nonnull ItemLike tunnelPart) {
Objects.requireNonNull(modId, "modId");
Objects.requireNonNull(type, "type");
modIdTunnels.put(modId, type);
modIdTunnels.put(modId, validateTunnelPartItem(tunnelPart));
}

/**
* Attunement based on the ability of getting an API via Fabric API Lookup from the item.
*/
public synchronized static <T> void addNewAttunement(ItemApiLookup<?, T> api,
public synchronized static <T> void addItemByApi(ItemApiLookup<?, T> api,
Function<ItemStack, T> contextProvider,
TunnelType type) {
ItemLike tunnelPart) {
Objects.requireNonNull(api, "api");
Objects.requireNonNull(contextProvider, "contextProvider");
Objects.requireNonNull(type, "type");
apiAttunements.add(new ApiAttunement<>(api, contextProvider, type));
apiAttunements.add(new ApiAttunement<>(api, contextProvider, validateTunnelPartItem(tunnelPart)));
}

/**
* Attunement based on the ability of getting a storage container API via Fabric API Lookup from the item.
*/
public synchronized static void addNewAttunement(@Nonnull ItemApiLookup<?, ContainerItemContext> api,
@Nonnull TunnelType type) {
addNewAttunement(api, stack -> ContainerItemContext.ofSingleSlot(new SingleStackStorage() {
public synchronized static void addItemByApi(@Nonnull ItemApiLookup<?, ContainerItemContext> api,
@Nonnull ItemLike tunnelPart) {
addItemByApi(api, stack -> ContainerItemContext.ofSingleSlot(new SingleStackStorage() {
ItemStack buffer = stack;

@Override
Expand All @@ -112,57 +146,68 @@ protected ItemStack getStack() {
protected void setStack(ItemStack stack) {
buffer = stack;
}
}), type);
}), tunnelPart);
}

/**
* returns null if no attunement can be found.
*
* @param trigger attunement trigger
* @return null if no attunement can be found or attunement
* @return The part item for a P2P-Tunnel that should handle the given attunement, or an empty item stack.
*/
@Nullable
public synchronized static TunnelType getTunnelTypeByItem(ItemStack trigger) {
if (!trigger.isEmpty()) {
// First match exact items
for (final Map.Entry<ItemStack, TunnelType> entry : tunnels.entrySet()) {
final ItemStack is = entry.getKey();

if (is.getItem() == trigger.getItem()) {
return entry.getValue();
}

if (ItemStack.isSame(is, trigger)) {
return entry.getValue();
}
}
@Nonnull
public synchronized static ItemStack getTunnelPartByTriggerItem(ItemStack trigger) {
if (trigger.isEmpty()) {
return ItemStack.EMPTY;
}

// Check provided APIs
for (var apiAttunement : apiAttunements) {
if (apiAttunement.hasApi(trigger)) {
return apiAttunement.type();
}
// First match exact items
var tunnelItem = tunnels.get(trigger.getItem());
if (tunnelItem != null) {
return new ItemStack(tunnelItem);
}

// Check provided APIs
for (var apiAttunement : apiAttunements) {
if (apiAttunement.hasApi(trigger)) {
return new ItemStack(apiAttunement.tunnelType());
}
}

// Use the mod id as last option.
for (final Map.Entry<String, TunnelType> entry : modIdTunnels.entrySet()) {
var id = Registry.ITEM.getKey(trigger.getItem());
if (id.getNamespace().equals(entry.getKey())) {
return entry.getValue();
}
// Use the mod id as last option.
for (var entry : modIdTunnels.entrySet()) {
var id = Registry.ITEM.getKey(trigger.getItem());
if (id.getNamespace().equals(entry.getKey())) {
return new ItemStack(entry.getValue());
}
}

return null;
return ItemStack.EMPTY;
}

record ApiAttunement<T> (
ItemApiLookup<?, T> api,
Function<ItemStack, T> contextProvider,
TunnelType type) {
Item tunnelType) {
public boolean hasApi(ItemStack stack) {
return api.find(stack, contextProvider.apply(stack)) != null;
}
}

private static Item validateTunnelPartItem(ItemLike itemLike) {
Objects.requireNonNull(itemLike, "item");
var item = itemLike.asItem();
Objects.requireNonNull(item, "item");
if (!(item instanceof PartItem<?>partItem)) {
throw new IllegalArgumentException("Given tunnel part item is not a part");
}

var is = new ItemStack(partItem);
var part = partItem.createPart(is);
if (!(part instanceof P2PTunnelPart)) {
throw new IllegalArgumentException("Given tunnel part item results in a part that is not a P2P tunnel: "
+ part.getClass());
}

return item;
}

}
32 changes: 8 additions & 24 deletions src/main/java/appeng/api/features/P2PTunnelAttunementInternal.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,52 +23,36 @@

import net.fabricmc.fabric.api.lookup.v1.item.ItemApiLookup;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;

import appeng.api.config.TunnelType;
import appeng.core.definitions.AEParts;
import net.minecraft.world.level.ItemLike;

/**
* Internal methods that complement {@link P2PTunnelAttunement} and which are not part of the public API.
*/
public final class P2PTunnelAttunementInternal {
/**
* Gets the tunnel part to use for a given tunnel type.
*/
public static ItemStack getTunnelPart(TunnelType type) {
return switch (type) {
case LIGHT -> AEParts.LIGHT_P2P_TUNNEL.stack();
case FE_POWER -> AEParts.FE_P2P_TUNNEL.stack();
case FLUID -> AEParts.FLUID_P2P_TUNNEL.stack();
case ITEM -> AEParts.ITEM_P2P_TUNNEL.stack();
case ME -> AEParts.ME_P2P_TUNNEL.stack();
case REDSTONE -> AEParts.REDSTONE_P2P_TUNNEL.stack();
default -> throw new IllegalArgumentException("Unsupported tunnel type: " + type);
};
}

/**
* Gets a report which sources of attunement exist for a given tunnel type.
*/
public static AttunementInfo getAttunementInfo(TunnelType type) {
public static AttunementInfo getAttunementInfo(ItemLike tunnelType) {
var tunnelItem = tunnelType.asItem();

Set<Item> items = new HashSet<>();
Set<String> mods = new HashSet<>();
Set<ItemApiLookup<?, ?>> apis = new HashSet<>();

for (var entry : P2PTunnelAttunement.tunnels.entrySet()) {
if (entry.getValue() == type) {
items.add(entry.getKey().getItem());
if (entry.getValue() == tunnelItem) {
items.add(entry.getKey());
}
}

for (var entry : P2PTunnelAttunement.modIdTunnels.entrySet()) {
if (entry.getValue() == type) {
if (entry.getValue() == tunnelItem) {
mods.add(entry.getKey());
}
}

for (var apiAttunement : P2PTunnelAttunement.apiAttunements) {
if (apiAttunement.type() == type) {
if (apiAttunement.tunnelType() == tunnelItem) {
apis.add(apiAttunement.api());
}
}
Expand Down

0 comments on commit 1908f6c

Please sign in to comment.