diff --git a/projects/common/src/main/java/dan200/computercraft/shared/ModRegistry.java b/projects/common/src/main/java/dan200/computercraft/shared/ModRegistry.java index 6fb2c8b66..7f6e49d48 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/ModRegistry.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/ModRegistry.java @@ -45,11 +45,9 @@ import dan200.computercraft.shared.details.ItemDetails; import dan200.computercraft.shared.integration.PermissionRegistry; import dan200.computercraft.shared.lectern.CustomLecternBlock; import dan200.computercraft.shared.lectern.CustomLecternBlockEntity; +import dan200.computercraft.shared.media.MountMedia; import dan200.computercraft.shared.media.PrintoutMenu; -import dan200.computercraft.shared.media.items.DiskItem; -import dan200.computercraft.shared.media.items.PrintoutItem; -import dan200.computercraft.shared.media.items.RecordMedia; -import dan200.computercraft.shared.media.items.TreasureDiskItem; +import dan200.computercraft.shared.media.items.*; import dan200.computercraft.shared.media.recipes.DiskRecipe; import dan200.computercraft.shared.media.recipes.PrintoutRecipe; import dan200.computercraft.shared.network.container.ComputerContainerData; @@ -107,6 +105,7 @@ import net.minecraft.world.item.*; import net.minecraft.world.item.crafting.CustomRecipe; import net.minecraft.world.item.crafting.RecipeSerializer; import net.minecraft.world.item.crafting.SimpleCraftingRecipeSerializer; +import net.minecraft.world.level.ItemLike; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.SoundType; import net.minecraft.world.level.block.entity.BlockEntity; @@ -467,12 +466,6 @@ public final class ModRegistry { // Register bundled power providers ComputerCraftAPI.registerBundledRedstoneProvider(new DefaultBundledRedstoneProvider()); ComputerCraftAPI.registerRefuelHandler(new FurnaceRefuelHandler()); - ComputerCraftAPI.registerMediaProvider(stack -> { - var item = stack.getItem(); - if (item instanceof IMedia media) return media; - if (item instanceof RecordItem) return RecordMedia.INSTANCE; - return null; - }); ComputerCraftAPI.registerAPIFactory(computer -> { var turtle = computer.getComponent(ComputerComponents.TURTLE); @@ -531,6 +524,26 @@ public final class ModRegistry { wiredElements.registerForBlockEntity(ModRegistry.BlockEntities.CABLE.get(), CableBlockEntity::getWiredElement); } + /** + * Register our custom {@link IMedia} implementations. + * + * @param media The object to register our media capabilities/lookups with. + */ + public static void registerMedia(ItemComponent media) { + media.registerForItems((s, c) -> MountMedia.COMPUTER, + ModRegistry.Items.COMPUTER_NORMAL.get(), ModRegistry.Items.COMPUTER_ADVANCED.get(), + ModRegistry.Items.TURTLE_NORMAL.get(), ModRegistry.Items.TURTLE_ADVANCED.get(), + ModRegistry.Items.POCKET_COMPUTER_NORMAL.get(), ModRegistry.Items.POCKET_COMPUTER_ADVANCED.get() + ); + media.registerForItems((s, c) -> MountMedia.DISK, ModRegistry.Items.DISK.get()); + media.registerForItems((s, c) -> TreasureDiskMedia.INSTANCE, ModRegistry.Items.TREASURE_DISK.get()); + media.registerFallback((stack, ctx) -> { + if (stack.getItem() instanceof IMedia m) return m; + if (stack.getItem() instanceof RecordItem) return RecordMedia.INSTANCE; + return null; + }); + } + /** * An abstraction for registering capabilities/block lookups for blocks and block entities. * @@ -541,6 +554,17 @@ public final class ModRegistry { void registerForBlockEntity(BlockEntityType blockEntityType, BiFunction provider); } + /** + * An abstraction for registering capabilities/block lookups for items. + * + * @param The type of the component. + */ + public interface ItemComponent { + void registerForItems(BiFunction provider, ItemLike... items); + + void registerFallback(BiFunction provider); + } + private static void addTurtle(CreativeModeTab.Output out, TurtleItem turtle) { out.accept(turtle.create(-1, null, -1, null, null, 0, null)); TurtleUpgrades.getVanillaUpgrades() diff --git a/projects/common/src/main/java/dan200/computercraft/shared/computer/items/AbstractComputerItem.java b/projects/common/src/main/java/dan200/computercraft/shared/computer/items/AbstractComputerItem.java index cdffc644b..dc8ae8a21 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/computer/items/AbstractComputerItem.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/computer/items/AbstractComputerItem.java @@ -4,14 +4,9 @@ package dan200.computercraft.shared.computer.items; -import dan200.computercraft.api.ComputerCraftAPI; -import dan200.computercraft.api.filesystem.Mount; -import dan200.computercraft.api.media.IMedia; import dan200.computercraft.shared.computer.blocks.AbstractComputerBlock; -import dan200.computercraft.shared.config.Config; import net.minecraft.ChatFormatting; import net.minecraft.network.chat.Component; -import net.minecraft.server.level.ServerLevel; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.TooltipFlag; @@ -20,7 +15,7 @@ import org.jspecify.annotations.Nullable; import java.util.List; -public abstract class AbstractComputerItem extends BlockItem implements IComputerItem, IMedia { +public abstract class AbstractComputerItem extends BlockItem implements IComputerItem { public AbstractComputerItem(AbstractComputerBlock block, Properties settings) { super(block, settings); } @@ -40,20 +35,4 @@ public abstract class AbstractComputerItem extends BlockItem implements ICompute public @Nullable String getLabel(ItemStack stack) { return IComputerItem.super.getLabel(stack); } - - @Override - public boolean setLabel(ItemStack stack, @Nullable String label) { - if (label != null) { - stack.setHoverName(Component.literal(label)); - } else { - stack.resetHoverName(); - } - return true; - } - - @Override - public @Nullable Mount createDataMount(ItemStack stack, ServerLevel level) { - var id = getComputerID(stack); - return id >= 0 ? ComputerCraftAPI.createSaveDirMount(level.getServer(), "computer/" + id, Config.computerSpaceLimit) : null; - } } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/computer/items/CommandComputerItem.java b/projects/common/src/main/java/dan200/computercraft/shared/computer/items/CommandComputerItem.java index f8ce2ff76..c2478a0bb 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/computer/items/CommandComputerItem.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/computer/items/CommandComputerItem.java @@ -4,10 +4,7 @@ package dan200.computercraft.shared.computer.items; -import dan200.computercraft.api.filesystem.Mount; import dan200.computercraft.shared.computer.blocks.ComputerBlock; -import net.minecraft.server.level.ServerLevel; -import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.block.state.BlockState; import org.jspecify.annotations.Nullable; @@ -29,10 +26,4 @@ public class CommandComputerItem extends ComputerItem { var player = context.getPlayer(); return player != null && !player.canUseGameMasterBlocks() ? null : super.getPlacementState(context); } - - @Override - public @Nullable Mount createDataMount(ItemStack stack, ServerLevel level) { - // Don't allow command computers to be mounted in disk drives. - return null; - } } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/media/MountMedia.java b/projects/common/src/main/java/dan200/computercraft/shared/media/MountMedia.java new file mode 100644 index 000000000..e35d21d51 --- /dev/null +++ b/projects/common/src/main/java/dan200/computercraft/shared/media/MountMedia.java @@ -0,0 +1,93 @@ +// SPDX-FileCopyrightText: 2025 The CC: Tweaked Developers +// +// SPDX-License-Identifier: MPL-2.0 + +package dan200.computercraft.shared.media; + +import dan200.computercraft.api.ComputerCraftAPI; +import dan200.computercraft.api.filesystem.Mount; +import dan200.computercraft.api.media.IMedia; +import dan200.computercraft.shared.computer.items.AbstractComputerItem; +import dan200.computercraft.shared.computer.items.IComputerItem; +import dan200.computercraft.shared.config.ConfigSpec; +import dan200.computercraft.shared.media.items.DiskItem; +import net.minecraft.network.chat.Component; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.item.ItemStack; + +import javax.annotation.Nullable; +import java.util.function.Supplier; +import java.util.function.ToIntFunction; + +/** + * Media that provides a {@link Mount}. + */ +public final class MountMedia implements IMedia { + /** + * A {@link MountMedia} implementation for {@linkplain AbstractComputerItem computers}. + */ + public static final IMedia COMPUTER = new MountMedia( + "computer", s -> ((IComputerItem) s.getItem()).getComputerID(s), null, ConfigSpec.computerSpaceLimit + ); + + /** + * A {@link MountMedia} implementation for {@linkplain DiskItem disks}. + */ + public static final IMedia DISK = new MountMedia("disk", DiskItem::getDiskID, DiskItem::setDiskID, ConfigSpec.floppySpaceLimit); + + private final String subPath; + private final ToIntFunction getId; + private final @Nullable IdSetter setId; + private final Supplier defaultCapacity; + + /** + * Create a new {@link MountMedia}. + * + * @param subPath The sub-path to expose the mount under, for instance {@code "computer"}. + * @param getId A function to get the item's ID. + * @param setId A function to set the item's ID. If not present, then mounts will not be created when the + * item is placed in a drive. + * @param defaultCapacity A function to get the default capacity of the stack. + */ + public MountMedia( + String subPath, + ToIntFunction getId, + @Nullable IdSetter setId, + Supplier defaultCapacity + ) { + this.subPath = subPath; + this.getId = getId; + this.setId = setId; + this.defaultCapacity = defaultCapacity; + } + + @Override + public @Nullable String getLabel(ItemStack stack) { + return stack.hasCustomHoverName() ? stack.getHoverName().getString() : null; + } + + @Override + public boolean setLabel(ItemStack stack, @Nullable String label) { + if (label != null) { + stack.setHoverName(Component.literal(label)); + } else { + stack.resetHoverName(); + } + return true; + } + + @Override + public @Nullable Mount createDataMount(ItemStack stack, ServerLevel level) { + var id = getId.applyAsInt(stack); + if (id < 0) { + if (setId == null) return null; + id = ComputerCraftAPI.createUniqueNumberedSaveDir(level.getServer(), subPath); + setId.set(stack, id); + } + return ComputerCraftAPI.createSaveDirMount(level.getServer(), subPath + "/" + id, defaultCapacity.get()); + } + + public interface IdSetter { + void set(ItemStack stack, int id); + } +} diff --git a/projects/common/src/main/java/dan200/computercraft/shared/media/items/DiskItem.java b/projects/common/src/main/java/dan200/computercraft/shared/media/items/DiskItem.java index 01e1a4b0e..0cfaa6864 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/media/items/DiskItem.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/media/items/DiskItem.java @@ -5,17 +5,12 @@ package dan200.computercraft.shared.media.items; import dan200.computercraft.annotations.ForgeOverride; -import dan200.computercraft.api.ComputerCraftAPI; -import dan200.computercraft.api.filesystem.Mount; -import dan200.computercraft.api.media.IMedia; import dan200.computercraft.core.util.Colour; import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.common.IColouredItem; -import dan200.computercraft.shared.config.Config; import net.minecraft.ChatFormatting; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; -import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; @@ -26,7 +21,7 @@ import org.jspecify.annotations.Nullable; import java.util.List; -public class DiskItem extends Item implements IMedia, IColouredItem { +public class DiskItem extends Item implements IColouredItem { private static final String NBT_ID = "DiskId"; public DiskItem(Properties settings) { @@ -36,7 +31,7 @@ public class DiskItem extends Item implements IMedia, IColouredItem { public static ItemStack createFromIDAndColour(int id, @Nullable String label, int colour) { var stack = new ItemStack(ModRegistry.Items.DISK.get()); setDiskID(stack, id); - ModRegistry.Items.DISK.get().setLabel(stack, label); + if (label != null) stack.setHoverName(Component.literal(label)); IColouredItem.setColourBasic(stack, colour); return stack; } @@ -57,37 +52,12 @@ public class DiskItem extends Item implements IMedia, IColouredItem { return true; } - @Override - public @Nullable String getLabel(ItemStack stack) { - return stack.hasCustomHoverName() ? stack.getHoverName().getString() : null; - } - - @Override - public boolean setLabel(ItemStack stack, @Nullable String label) { - if (label != null) { - stack.setHoverName(Component.literal(label)); - } else { - stack.resetHoverName(); - } - return true; - } - - @Override - public @Nullable Mount createDataMount(ItemStack stack, ServerLevel level) { - var diskID = getDiskID(stack); - if (diskID < 0) { - diskID = ComputerCraftAPI.createUniqueNumberedSaveDir(level.getServer(), "disk"); - setDiskID(stack, diskID); - } - return ComputerCraftAPI.createSaveDirMount(level.getServer(), "disk/" + diskID, Config.floppySpaceLimit); - } - public static int getDiskID(ItemStack stack) { var nbt = stack.getTag(); return nbt != null && nbt.contains(NBT_ID) ? nbt.getInt(NBT_ID) : -1; } - private static void setDiskID(ItemStack stack, int id) { + public static void setDiskID(ItemStack stack, int id) { if (id >= 0) stack.getOrCreateTag().putInt(NBT_ID, id); } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/media/items/TreasureDiskItem.java b/projects/common/src/main/java/dan200/computercraft/shared/media/items/TreasureDiskItem.java index dc6e49c30..31ed46d66 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/media/items/TreasureDiskItem.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/media/items/TreasureDiskItem.java @@ -5,15 +5,10 @@ package dan200.computercraft.shared.media.items; import dan200.computercraft.annotations.ForgeOverride; -import dan200.computercraft.api.ComputerCraftAPI; -import dan200.computercraft.api.filesystem.Mount; -import dan200.computercraft.api.media.IMedia; -import dan200.computercraft.core.filesystem.SubMount; import dan200.computercraft.core.util.Colour; import dan200.computercraft.shared.ModRegistry; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; -import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; @@ -22,10 +17,9 @@ import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelReader; import org.jspecify.annotations.Nullable; -import java.io.IOException; import java.util.List; -public class TreasureDiskItem extends Item implements IMedia { +public class TreasureDiskItem extends Item { private static final String NBT_TITLE = "Title"; private static final String NBT_COLOUR = "Colour"; private static final String NBT_SUB_PATH = "SubPath"; @@ -45,30 +39,6 @@ public class TreasureDiskItem extends Item implements IMedia { return true; } - @Override - public String getLabel(ItemStack stack) { - return getTitle(stack); - } - - @Override - public @Nullable Mount createDataMount(ItemStack stack, ServerLevel level) { - var rootTreasure = ComputerCraftAPI.createResourceMount(level.getServer(), "computercraft", "lua/treasure"); - if (rootTreasure == null) return null; - - var subPath = getSubPath(stack); - try { - if (rootTreasure.exists(subPath)) { - return new SubMount(rootTreasure, subPath); - } else if (rootTreasure.exists("deprecated/" + subPath)) { - return new SubMount(rootTreasure, "deprecated/" + subPath); - } else { - return null; - } - } catch (IOException e) { - return null; - } - } - public static ItemStack create(String subPath, int colourIndex) { var result = new ItemStack(ModRegistry.Items.TREASURE_DISK.get()); var nbt = result.getOrCreateTag(); @@ -87,12 +57,12 @@ public class TreasureDiskItem extends Item implements IMedia { return result; } - private static String getTitle(ItemStack stack) { + static String getTitle(ItemStack stack) { var nbt = stack.getTag(); return nbt != null && nbt.contains(NBT_TITLE) ? nbt.getString(NBT_TITLE) : "'missingno' by how did you get this anyway?"; } - private static String getSubPath(ItemStack stack) { + static String getSubPath(ItemStack stack) { var nbt = stack.getTag(); return nbt != null && nbt.contains(NBT_SUB_PATH) ? nbt.getString(NBT_SUB_PATH) : "dan200/alongtimeago"; } diff --git a/projects/common/src/main/java/dan200/computercraft/shared/media/items/TreasureDiskMedia.java b/projects/common/src/main/java/dan200/computercraft/shared/media/items/TreasureDiskMedia.java new file mode 100644 index 000000000..f90f7dded --- /dev/null +++ b/projects/common/src/main/java/dan200/computercraft/shared/media/items/TreasureDiskMedia.java @@ -0,0 +1,49 @@ +// Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission. +// +// SPDX-License-Identifier: LicenseRef-CCPL + +package dan200.computercraft.shared.media.items; + +import dan200.computercraft.api.ComputerCraftAPI; +import dan200.computercraft.api.filesystem.Mount; +import dan200.computercraft.api.media.IMedia; +import dan200.computercraft.core.filesystem.SubMount; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.item.ItemStack; + +import javax.annotation.Nullable; +import java.io.IOException; + +/** + * An {@link IMedia} instance for {@linkplain TreasureDiskItem treasure disks}. + */ +public final class TreasureDiskMedia implements IMedia { + public static final IMedia INSTANCE = new TreasureDiskMedia(); + + private TreasureDiskMedia() { + } + + @Override + public String getLabel(ItemStack stack) { + return TreasureDiskItem.getTitle(stack); + } + + @Override + public @Nullable Mount createDataMount(ItemStack stack, ServerLevel level) { + var rootTreasure = ComputerCraftAPI.createResourceMount(level.getServer(), "computercraft", "lua/treasure"); + if (rootTreasure == null) return null; + + var subPath = TreasureDiskItem.getSubPath(stack); + try { + if (rootTreasure.exists(subPath)) { + return new SubMount(rootTreasure, subPath); + } else if (rootTreasure.exists("deprecated/" + subPath)) { + return new SubMount(rootTreasure, "deprecated/" + subPath); + } else { + return null; + } + } catch (IOException e) { + return null; + } + } +} diff --git a/projects/common/src/main/java/dan200/computercraft/shared/pocket/items/PocketComputerItem.java b/projects/common/src/main/java/dan200/computercraft/shared/pocket/items/PocketComputerItem.java index df885633e..d58f29c3e 100644 --- a/projects/common/src/main/java/dan200/computercraft/shared/pocket/items/PocketComputerItem.java +++ b/projects/common/src/main/java/dan200/computercraft/shared/pocket/items/PocketComputerItem.java @@ -6,8 +6,6 @@ package dan200.computercraft.shared.pocket.items; import dan200.computercraft.annotations.ForgeOverride; import dan200.computercraft.api.ComputerCraftAPI; -import dan200.computercraft.api.filesystem.Mount; -import dan200.computercraft.api.media.IMedia; import dan200.computercraft.api.pocket.IPocketUpgrade; import dan200.computercraft.api.upgrades.UpgradeData; import dan200.computercraft.core.computer.ComputerSide; @@ -20,7 +18,6 @@ import dan200.computercraft.shared.computer.core.ServerComputerRegistry; import dan200.computercraft.shared.computer.core.ServerContext; import dan200.computercraft.shared.computer.inventory.ComputerMenuWithoutInventory; import dan200.computercraft.shared.computer.items.IComputerItem; -import dan200.computercraft.shared.config.Config; import dan200.computercraft.shared.lectern.CustomLecternBlock; import dan200.computercraft.shared.network.container.ComputerContainerData; import dan200.computercraft.shared.platform.PlatformHelper; @@ -53,7 +50,7 @@ import java.util.List; import java.util.Objects; import java.util.UUID; -public class PocketComputerItem extends Item implements IComputerItem, IMedia, IColouredItem { +public class PocketComputerItem extends Item implements IComputerItem, IColouredItem { private static final String NBT_UPGRADE = "Upgrade"; private static final String NBT_UPGRADE_INFO = "UpgradeInfo"; public static final String NBT_ON = "On"; @@ -127,7 +124,11 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I var label = computer.getLabel(); if (!Objects.equals(label, getLabel(stack))) { changed = true; - setLabel(stack, label); + if (label != null) { + stack.setHoverName(Component.literal(label)); + } else { + stack.resetHoverName(); + } } var on = computer.isOn(); @@ -342,27 +343,6 @@ public class PocketComputerItem extends Item implements IComputerItem, IMedia, I ) : ItemStack.EMPTY; } - // IMedia - - @Override - public boolean setLabel(ItemStack stack, @Nullable String label) { - if (label != null) { - stack.setHoverName(Component.literal(label)); - } else { - stack.resetHoverName(); - } - return true; - } - - @Override - public @Nullable Mount createDataMount(ItemStack stack, ServerLevel level) { - var id = getComputerID(stack); - if (id >= 0) { - return ComputerCraftAPI.createSaveDirMount(level.getServer(), "computer/" + id, Config.computerSpaceLimit); - } - return null; - } - public static @Nullable UUID getInstanceID(ItemStack stack) { var nbt = stack.getTag(); return nbt != null && nbt.hasUUID(NBT_INSTANCE) ? nbt.getUUID(NBT_INSTANCE) : null; diff --git a/projects/fabric/src/main/java/dan200/computercraft/shared/ComputerCraft.java b/projects/fabric/src/main/java/dan200/computercraft/shared/ComputerCraft.java index b76b77246..4234c7ecf 100644 --- a/projects/fabric/src/main/java/dan200/computercraft/shared/ComputerCraft.java +++ b/projects/fabric/src/main/java/dan200/computercraft/shared/ComputerCraft.java @@ -6,6 +6,7 @@ package dan200.computercraft.shared; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.detail.FabricDetailRegistries; +import dan200.computercraft.api.media.MediaLookup; import dan200.computercraft.api.node.wired.WiredElementLookup; import dan200.computercraft.api.peripheral.PeripheralLookup; import dan200.computercraft.impl.Peripherals; @@ -27,6 +28,7 @@ import net.fabricmc.fabric.api.event.player.PlayerBlockBreakEvents; import net.fabricmc.fabric.api.event.player.UseBlockCallback; import net.fabricmc.fabric.api.itemgroup.v1.ItemGroupEvents; import net.fabricmc.fabric.api.lookup.v1.block.BlockApiLookup; +import net.fabricmc.fabric.api.lookup.v1.item.ItemApiLookup; import net.fabricmc.fabric.api.loot.v2.LootTableEvents; import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener; @@ -38,6 +40,8 @@ import net.minecraft.server.packs.PackType; import net.minecraft.server.packs.resources.PreparableReloadListener; import net.minecraft.server.packs.resources.ResourceManager; import net.minecraft.util.profiling.ProfilerFiller; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.ItemLike; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.storage.LevelResource; @@ -62,6 +66,7 @@ public class ComputerCraft { ModRegistry.registerPeripherals(new BlockComponentImpl<>(PeripheralLookup.get())); ModRegistry.registerWiredElements(new BlockComponentImpl<>(WiredElementLookup.get())); + ModRegistry.registerMedia(new ItemComponentImpl<>(MediaLookup.get())); // Register commands CommandRegistrationCallback.EVENT.register((dispatcher, context, environment) -> CommandComputerCraft.register(dispatcher)); @@ -127,4 +132,18 @@ public class ComputerCraft { lookup.registerForBlockEntity(provider, blockEntityType); } } + + private record ItemComponentImpl( + ItemApiLookup lookup + ) implements ModRegistry.ItemComponent { + @Override + public void registerForItems(BiFunction provider, ItemLike... items) { + lookup().registerForItems(provider::apply, items); + } + + @Override + public void registerFallback(BiFunction provider) { + lookup().registerFallback(provider::apply); + } + } } diff --git a/projects/forge/src/main/java/dan200/computercraft/ComputerCraft.java b/projects/forge/src/main/java/dan200/computercraft/ComputerCraft.java index 9b50fe335..705f1867d 100644 --- a/projects/forge/src/main/java/dan200/computercraft/ComputerCraft.java +++ b/projects/forge/src/main/java/dan200/computercraft/ComputerCraft.java @@ -12,6 +12,7 @@ import dan200.computercraft.api.network.wired.WiredElement; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.pocket.PocketUpgradeSerialiser; import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser; +import dan200.computercraft.impl.MediaProviders; import dan200.computercraft.shared.CommonHooks; import dan200.computercraft.shared.ModRegistry; import dan200.computercraft.shared.config.ConfigSpec; @@ -79,6 +80,8 @@ public final class ComputerCraft { ForgeDetailRegistries.FLUID_STACK.addProvider(FluidData::fill); + MediaProviders.registerDefault(); + if (ModList.get().isLoaded(CreateIntegration.ID)) event.enqueueWork(CreateIntegration::setup); if (ModList.get().isLoaded(MoreRedIntegration.MOD_ID)) MoreRedIntegration.setup(); } diff --git a/projects/forge/src/main/java/dan200/computercraft/impl/MediaProviders.java b/projects/forge/src/main/java/dan200/computercraft/impl/MediaProviders.java index a5960cddc..48bf830c6 100644 --- a/projects/forge/src/main/java/dan200/computercraft/impl/MediaProviders.java +++ b/projects/forge/src/main/java/dan200/computercraft/impl/MediaProviders.java @@ -6,18 +6,17 @@ package dan200.computercraft.impl; import dan200.computercraft.api.media.IMedia; import dan200.computercraft.api.media.MediaProvider; +import dan200.computercraft.shared.ModRegistry; +import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.ItemLike; import org.jspecify.annotations.Nullable; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import java.util.LinkedHashSet; -import java.util.Objects; -import java.util.Set; +import java.util.*; +import java.util.function.BiFunction; public final class MediaProviders { - private static final Logger LOG = LoggerFactory.getLogger(MediaProviders.class); - + private static final Map itemProviders = new HashMap<>(); private static final Set providers = new LinkedHashSet<>(); private MediaProviders() { @@ -31,16 +30,33 @@ public final class MediaProviders { public static @Nullable IMedia get(ItemStack stack) { if (stack.isEmpty()) return null; + // Try the per-item provider first. + var itemProvider = itemProviders.get(stack.getItem()); + if (itemProvider != null) { + var media = itemProvider.getMedia(stack); + if (media != null) return media; + } + // Try the handlers in order: for (var mediaProvider : providers) { - try { - var media = mediaProvider.getMedia(stack); - if (media != null) return media; - } catch (Exception e) { - // Mod misbehaved, ignore it - LOG.error("Media provider " + mediaProvider + " errored.", e); - } + var media = mediaProvider.getMedia(stack); + if (media != null) return media; } return null; } + + public static synchronized void registerDefault() { + ModRegistry.registerMedia(new ModRegistry.ItemComponent<>() { + @Override + public void registerForItems(BiFunction provider, ItemLike... items) { + MediaProvider wrappedProvider = s -> provider.apply(s, null); + for (var item : items) itemProviders.put(item.asItem(), wrappedProvider); + } + + @Override + public void registerFallback(BiFunction provider) { + register(s -> provider.apply(s, null)); + } + }); + } }