From c1bf9f0b248a064f27bb299820a888075694efe5 Mon Sep 17 00:00:00 2001 From: Jonathan Coates Date: Tue, 1 Nov 2022 21:05:24 +0000 Subject: [PATCH] Prepare the public API for multi-loader support Well, sort of. For now, we just mark a whole bunch of stuff as @Deprecated. --- .../computercraft/ComputerCraftAPIImpl.java | 10 ++- .../computercraft/api/ComputerCraftAPI.java | 16 +++- .../api/ForgeComputerCraftAPI.java | 73 +++++++++++++++++++ .../api/detail/DetailRegistry.java | 2 +- .../api/detail/ForgeDetailRegistries.java | 20 +++++ ...ries.java => VanillaDetailRegistries.java} | 7 +- .../computercraft/api/lua/GenericSource.java | 3 +- .../api/peripheral/IPeripheralProvider.java | 3 +- .../api/pocket/PocketUpgradeDataProvider.java | 2 +- .../api/pocket/PocketUpgradeSerialiser.java | 4 +- .../api/turtle/ITurtleAccess.java | 17 ++++- .../api/turtle/TurtleRefuelHandler.java | 35 +++++++++ .../api/turtle/TurtleUpgradeDataProvider.java | 2 +- .../api/turtle/TurtleUpgradeSerialiser.java | 4 +- .../api/turtle/event/TurtleEvent.java | 3 + .../api/turtle/event/TurtleRefuelEvent.java | 4 + .../api/upgrades/UpgradeDataProvider.java | 17 +++-- .../impl/ComputerCraftAPIService.java | 3 + .../impl/TurtleRefuelHandlers.java | 68 +++++++++++++++++ .../dan200/computercraft/shared/Registry.java | 16 ++-- .../shared/computer/apis/CommandAPI.java | 4 +- .../network/client/UpgradesLoadedMessage.java | 4 +- .../generic/methods/FluidMethods.java | 4 +- .../generic/methods/InventoryMethods.java | 6 +- .../peripheral/modem/wired/BlockCable.java | 4 +- .../peripheral/modem/wired/TileCable.java | 4 +- .../modem/wired/TileWiredModemFull.java | 4 +- .../shared/turtle/FurnaceRefuelHandler.java | 39 ++++------ .../shared/turtle/TurtleUtil.java | 50 +++++++++++++ .../shared/turtle/apis/TurtleAPI.java | 4 +- .../shared/turtle/core/TurtleBrain.java | 6 ++ .../turtle/core/TurtleCraftCommand.java | 12 +-- .../shared/turtle/core/TurtleDropCommand.java | 3 +- .../turtle/core/TurtleEquipCommand.java | 26 +------ .../turtle/core/TurtleInspectCommand.java | 4 +- .../turtle/core/TurtlePlaceCommand.java | 4 +- .../shared/turtle/core/TurtlePlayer.java | 19 ++--- .../turtle/core/TurtleRefuelCommand.java | 13 ++-- .../turtle/core/TurtleTransferToCommand.java | 2 +- .../shared/turtle/upgrades/TurtleTool.java | 30 ++------ .../shared/util/InventoryUtil.java | 6 -- .../computercraft/gametest/Turtle_Test.kt | 4 +- .../gametest/api/TestExtensions.kt | 2 +- 43 files changed, 396 insertions(+), 167 deletions(-) create mode 100644 src/main/java/dan200/computercraft/api/ForgeComputerCraftAPI.java create mode 100644 src/main/java/dan200/computercraft/api/detail/ForgeDetailRegistries.java rename src/main/java/dan200/computercraft/api/detail/{DetailRegistries.java => VanillaDetailRegistries.java} (78%) create mode 100644 src/main/java/dan200/computercraft/api/turtle/TurtleRefuelHandler.java create mode 100644 src/main/java/dan200/computercraft/impl/TurtleRefuelHandlers.java create mode 100644 src/main/java/dan200/computercraft/shared/turtle/TurtleUtil.java diff --git a/src/main/java/dan200/computercraft/ComputerCraftAPIImpl.java b/src/main/java/dan200/computercraft/ComputerCraftAPIImpl.java index 6d0be7c1f..8235dcaec 100644 --- a/src/main/java/dan200/computercraft/ComputerCraftAPIImpl.java +++ b/src/main/java/dan200/computercraft/ComputerCraftAPIImpl.java @@ -19,15 +19,17 @@ import dan200.computercraft.api.network.wired.IWiredElement; import dan200.computercraft.api.network.wired.IWiredNode; import dan200.computercraft.api.peripheral.IPeripheralProvider; import dan200.computercraft.api.redstone.IBundledRedstoneProvider; +import dan200.computercraft.api.turtle.TurtleRefuelHandler; import dan200.computercraft.core.apis.ApiFactories; import dan200.computercraft.core.asm.GenericMethod; import dan200.computercraft.core.filesystem.FileMount; -import dan200.computercraft.shared.computer.core.ResourceMount; import dan200.computercraft.impl.ComputerCraftAPIService; +import dan200.computercraft.impl.TurtleRefuelHandlers; import dan200.computercraft.impl.detail.DetailRegistryImpl; import dan200.computercraft.shared.BundledRedstone; import dan200.computercraft.shared.MediaProviders; import dan200.computercraft.shared.Peripherals; +import dan200.computercraft.shared.computer.core.ResourceMount; import dan200.computercraft.shared.computer.core.ServerContext; import dan200.computercraft.shared.peripheral.generic.GenericPeripheralProvider; import dan200.computercraft.shared.peripheral.generic.data.BlockData; @@ -205,6 +207,12 @@ public final class ComputerCraftAPIImpl implements ComputerCraftAPIService return tile == null ? LazyOptional.empty() : tile.getCapability( CAPABILITY_WIRED_ELEMENT, side ); } + @Override + public void registerRefuelHandler( @Nonnull TurtleRefuelHandler handler ) + { + TurtleRefuelHandlers.register( handler ); + } + @Override public DetailRegistry getItemStackDetailRegistry() { diff --git a/src/main/java/dan200/computercraft/api/ComputerCraftAPI.java b/src/main/java/dan200/computercraft/api/ComputerCraftAPI.java index 77fb1fb2e..bf7d5566c 100644 --- a/src/main/java/dan200/computercraft/api/ComputerCraftAPI.java +++ b/src/main/java/dan200/computercraft/api/ComputerCraftAPI.java @@ -21,6 +21,7 @@ import dan200.computercraft.api.peripheral.IComputerAccess; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheralProvider; import dan200.computercraft.api.redstone.IBundledRedstoneProvider; +import dan200.computercraft.api.turtle.TurtleRefuelHandler; import dan200.computercraft.impl.ComputerCraftAPIService; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -37,7 +38,7 @@ import javax.annotation.Nullable; /** * The static entry point to the ComputerCraft API. *

- * Members in this class must be called after mod_ComputerCraft has been initialised, but may be called before it is + * Members in this class must be called after ComputerCraft has been initialised, but may be called before it is * fully loaded. */ public final class ComputerCraftAPI @@ -122,7 +123,9 @@ public final class ComputerCraftAPI * @param provider The peripheral provider to register. * @see IPeripheral * @see IPeripheralProvider + * @deprecated Use {@link ForgeComputerCraftAPI#registerPeripheralProvider(IPeripheralProvider)} instead. */ + @Deprecated( forRemoval = true ) public static void registerPeripheralProvider( @Nonnull IPeripheralProvider provider ) { getInstance().registerPeripheralProvider( provider ); @@ -144,7 +147,9 @@ public final class ComputerCraftAPI * * @param capability The capability to register. * @see GenericSource + * @deprecated Use {@link ForgeComputerCraftAPI} instead. */ + @Deprecated( forRemoval = true ) public static void registerGenericCapability( @Nonnull Capability capability ) { getInstance().registerGenericCapability( capability ); @@ -212,7 +217,7 @@ public final class ComputerCraftAPI * @param The type of object that this provider can provide details for. * @deprecated Use {@link DetailRegistry#addProvider(IDetailProvider)} to register your provider. */ - @Deprecated + @Deprecated( forRemoval = true ) public static void registerDetailProvider( @Nonnull Class type, @Nonnull IDetailProvider provider ) { getInstance().registerDetailProvider( type, provider ); @@ -239,13 +244,20 @@ public final class ComputerCraftAPI * @param side The side to extract the network element from * @return The element's node * @see IWiredElement#getNode() + * @deprecated Use {@link ForgeComputerCraftAPI#getWiredElementAt(BlockGetter, BlockPos, Direction)} */ @Nonnull + @Deprecated( forRemoval = true ) public static LazyOptional getWiredElementAt( @Nonnull BlockGetter world, @Nonnull BlockPos pos, @Nonnull Direction side ) { return getInstance().getWiredElementAt( world, pos, side ); } + public static void registerRefuelHandler( @Nonnull TurtleRefuelHandler handler ) + { + getInstance().registerRefuelHandler( handler ); + } + @Nonnull private static ComputerCraftAPIService getInstance() { diff --git a/src/main/java/dan200/computercraft/api/ForgeComputerCraftAPI.java b/src/main/java/dan200/computercraft/api/ForgeComputerCraftAPI.java new file mode 100644 index 000000000..a303dbb02 --- /dev/null +++ b/src/main/java/dan200/computercraft/api/ForgeComputerCraftAPI.java @@ -0,0 +1,73 @@ +/* + * This file is part of the public ComputerCraft API - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. This API may be redistributed unmodified and in full only. + * For help using the API, and posting your mods, visit the forums at computercraft.info. + */ +package dan200.computercraft.api; + +import dan200.computercraft.api.lua.GenericSource; +import dan200.computercraft.api.network.wired.IWiredElement; +import dan200.computercraft.api.peripheral.IPeripheral; +import dan200.computercraft.api.peripheral.IPeripheralProvider; +import dan200.computercraft.impl.ComputerCraftAPIService; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.level.BlockGetter; +import net.minecraftforge.common.capabilities.Capability; +import net.minecraftforge.common.util.LazyOptional; + +import javax.annotation.Nonnull; + +/** + * The forge-specific entrypoint for ComputerCraft's API. + */ +public final class ForgeComputerCraftAPI +{ + private ForgeComputerCraftAPI() + { + } + + /** + * Registers a peripheral provider to convert blocks into {@link IPeripheral} implementations. + * + * @param provider The peripheral provider to register. + * @see IPeripheral + * @see IPeripheralProvider + */ + public static void registerPeripheralProvider( @Nonnull IPeripheralProvider provider ) + { + getInstance().registerPeripheralProvider( provider ); + } + + /** + * Registers a capability that can be used by generic peripherals. + * + * @param capability The capability to register. + * @see GenericSource + */ + public static void registerGenericCapability( @Nonnull Capability capability ) + { + getInstance().registerGenericCapability( capability ); + } + + /** + * Get the wired network element for a block in world. + * + * @param world The world the block exists in + * @param pos The position the block exists in + * @param side The side to extract the network element from + * @return The element's node + * @see IWiredElement#getNode() + */ + @Nonnull + public static LazyOptional getWiredElementAt( @Nonnull BlockGetter world, @Nonnull BlockPos pos, @Nonnull Direction side ) + { + return getInstance().getWiredElementAt( world, pos, side ); + } + + @Nonnull + private static ComputerCraftAPIService getInstance() + { + return ComputerCraftAPIService.get(); + } +} diff --git a/src/main/java/dan200/computercraft/api/detail/DetailRegistry.java b/src/main/java/dan200/computercraft/api/detail/DetailRegistry.java index 202785432..d835f8bc2 100644 --- a/src/main/java/dan200/computercraft/api/detail/DetailRegistry.java +++ b/src/main/java/dan200/computercraft/api/detail/DetailRegistry.java @@ -14,7 +14,7 @@ import java.util.Map; *

* These are used by computer methods such as {@code turtle.getItemDetail()} or {@code turtle.inspect()}. *

- * Specific instances of this registry are available from {@link DetailRegistries} and loader-specific versions + * Specific instances of this registry are available from {@link VanillaDetailRegistries} and loader-specific versions * also in this package. * * @param The type of object that this registry provides details for. diff --git a/src/main/java/dan200/computercraft/api/detail/ForgeDetailRegistries.java b/src/main/java/dan200/computercraft/api/detail/ForgeDetailRegistries.java new file mode 100644 index 000000000..ef59dde78 --- /dev/null +++ b/src/main/java/dan200/computercraft/api/detail/ForgeDetailRegistries.java @@ -0,0 +1,20 @@ +/* + * This file is part of the public ComputerCraft API - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. This API may be redistributed unmodified and in full only. + * For help using the API, and posting your mods, visit the forums at computercraft.info. + */ +package dan200.computercraft.api.detail; + +import dan200.computercraft.impl.ComputerCraftAPIService; +import net.minecraftforge.fluids.FluidStack; + +/** + * {@link DetailRegistry}s for Forge-specific types. + */ +public class ForgeDetailRegistries +{ + /** + * Provides details for {@link FluidStack}. + */ + public static final DetailRegistry FLUID_STACK = ComputerCraftAPIService.get().getFluidStackDetailRegistry(); +} diff --git a/src/main/java/dan200/computercraft/api/detail/DetailRegistries.java b/src/main/java/dan200/computercraft/api/detail/VanillaDetailRegistries.java similarity index 78% rename from src/main/java/dan200/computercraft/api/detail/DetailRegistries.java rename to src/main/java/dan200/computercraft/api/detail/VanillaDetailRegistries.java index 780f0e9cf..4c1cafc29 100644 --- a/src/main/java/dan200/computercraft/api/detail/DetailRegistries.java +++ b/src/main/java/dan200/computercraft/api/detail/VanillaDetailRegistries.java @@ -8,12 +8,11 @@ package dan200.computercraft.api.detail; import dan200.computercraft.impl.ComputerCraftAPIService; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.Block; -import net.minecraftforge.fluids.FluidStack; /** * {@link DetailRegistry}s for built-in Minecraft types. */ -public class DetailRegistries +public class VanillaDetailRegistries { /** * Provides details for {@link ItemStack}s. @@ -25,8 +24,4 @@ public class DetailRegistries */ public static final DetailRegistry BLOCK_IN_WORLD = ComputerCraftAPIService.get().getBlockInWorldDetailRegistry(); - /** - * Provides details for {@link FluidStack}. - */ - public static final DetailRegistry FLUID_STACK = ComputerCraftAPIService.get().getFluidStackDetailRegistry(); } diff --git a/src/main/java/dan200/computercraft/api/lua/GenericSource.java b/src/main/java/dan200/computercraft/api/lua/GenericSource.java index 973b2ddfa..7e2c53001 100644 --- a/src/main/java/dan200/computercraft/api/lua/GenericSource.java +++ b/src/main/java/dan200/computercraft/api/lua/GenericSource.java @@ -6,6 +6,7 @@ package dan200.computercraft.api.lua; import dan200.computercraft.api.ComputerCraftAPI; +import dan200.computercraft.api.ForgeComputerCraftAPI; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheralProvider; import dan200.computercraft.core.asm.LuaMethod; @@ -42,7 +43,7 @@ import javax.annotation.Nonnull; * } * * @see ComputerCraftAPI#registerGenericSource(GenericSource) - * @see ComputerCraftAPI#registerGenericCapability(Capability) New capabilities (those not built into Forge) must be + * @see ForgeComputerCraftAPI#registerGenericCapability(Capability) New capabilities (those not built into Forge) must be * explicitly given to the generic peripheral system, as there is no way to enumerate all capabilities. */ public interface GenericSource diff --git a/src/main/java/dan200/computercraft/api/peripheral/IPeripheralProvider.java b/src/main/java/dan200/computercraft/api/peripheral/IPeripheralProvider.java index 3237e87cb..a2c020c51 100644 --- a/src/main/java/dan200/computercraft/api/peripheral/IPeripheralProvider.java +++ b/src/main/java/dan200/computercraft/api/peripheral/IPeripheralProvider.java @@ -5,6 +5,7 @@ */ package dan200.computercraft.api.peripheral; +import dan200.computercraft.api.ForgeComputerCraftAPI; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.level.Level; @@ -31,7 +32,7 @@ public interface IPeripheralProvider * @param pos The position the block is at. * @param side The side to get the peripheral from. * @return A peripheral, or {@link LazyOptional#empty()} if there is not a peripheral here you'd like to handle. - * @see dan200.computercraft.api.ComputerCraftAPI#registerPeripheralProvider(IPeripheralProvider) + * @see ForgeComputerCraftAPI#registerPeripheralProvider(IPeripheralProvider) */ @Nonnull LazyOptional getPeripheral( @Nonnull Level world, @Nonnull BlockPos pos, @Nonnull Direction side ); diff --git a/src/main/java/dan200/computercraft/api/pocket/PocketUpgradeDataProvider.java b/src/main/java/dan200/computercraft/api/pocket/PocketUpgradeDataProvider.java index b29dff6d7..a4f3e90de 100644 --- a/src/main/java/dan200/computercraft/api/pocket/PocketUpgradeDataProvider.java +++ b/src/main/java/dan200/computercraft/api/pocket/PocketUpgradeDataProvider.java @@ -25,6 +25,6 @@ public abstract class PocketUpgradeDataProvider extends UpgradeDataProvider extends Upgra *

* This is largely intended for use with Forge Registry methods/classes, such as {@link DeferredRegister} and * {@link RegistryManager#getRegistry(ResourceKey)}. - * - * @see #registry() */ ResourceKey>> REGISTRY_ID = ResourceKey.createRegistryKey( new ResourceLocation( ComputerCraft.MOD_ID, "pocket_upgrade_serialiser" ) ); @@ -50,7 +48,9 @@ public interface PocketUpgradeSerialiser extends Upgra * * @return The registry for pocket upgrade serialisers. * @see #REGISTRY_ID + * @deprecated Use {@link #REGISTRY_ID} directly. */ + @Deprecated( forRemoval = true ) static IForgeRegistry> registry() { return RegistryManager.ACTIVE.getRegistry( REGISTRY_ID ); diff --git a/src/main/java/dan200/computercraft/api/turtle/ITurtleAccess.java b/src/main/java/dan200/computercraft/api/turtle/ITurtleAccess.java index 4307e137f..cb2eaa034 100644 --- a/src/main/java/dan200/computercraft/api/turtle/ITurtleAccess.java +++ b/src/main/java/dan200/computercraft/api/turtle/ITurtleAccess.java @@ -44,6 +44,20 @@ public interface ITurtleAccess @Nonnull BlockPos getPosition(); + /** + * Determine if this turtle has been removed. + *

+ * It's possible for a turtle to be removed while a {@link ITurtleCommand} is executed, for instance if interacting + * with a block causes the turtle to be blown up. It's recommended you check the turtle is still present before + * trying to interact with it again. + *

+ * If a turtle has been removed {@link #getLevel()} and {@link #getPosition()} will continue to function as before. + * All other methods will fail. + * + * @return Whether this turtle has been removed. + */ + boolean isRemoved(); + /** * Attempt to move this turtle to a new position. *

@@ -161,9 +175,10 @@ public interface ITurtleAccess * @return This turtle's inventory * @see #getInventory() * @see IItemHandlerModifiable - * @see net.minecraftforge.items.CapabilityItemHandler#ITEM_HANDLER_CAPABILITY + * @deprecated Use {@link #getInventory()} directly. */ @Nonnull + @Deprecated( forRemoval = true ) IItemHandlerModifiable getItemHandler(); /** diff --git a/src/main/java/dan200/computercraft/api/turtle/TurtleRefuelHandler.java b/src/main/java/dan200/computercraft/api/turtle/TurtleRefuelHandler.java new file mode 100644 index 000000000..80f24c21f --- /dev/null +++ b/src/main/java/dan200/computercraft/api/turtle/TurtleRefuelHandler.java @@ -0,0 +1,35 @@ +/* + * This file is part of the public ComputerCraft API - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2022. This API may be redistributed unmodified and in full only. + * For help using the API, and posting your mods, visit the forums at computercraft.info. + */ +package dan200.computercraft.api.turtle; + +import dan200.computercraft.api.ComputerCraftAPI; +import net.minecraft.world.item.ItemStack; + +import java.util.OptionalInt; + +/** + * A function called when a turtle attempts to refuel via {@code turtle.refuel()}. This may be used to provide + * alternative fuel sources, such as consuming RF batteries. + * + * @see ComputerCraftAPI#registerRefuelHandler(TurtleRefuelHandler) + */ +public interface TurtleRefuelHandler +{ + /** + * Refuel a turtle using an item. + * + * @param turtle The turtle to refuel. + * @param stack The stack to refuel with. + * @param slot The slot the stack resides within. This may be used to modify the inventory afterwards. + * @param limit The maximum number of refuel operations to perform. This will often correspond to the number of + * items to consume. + *

+ * This value may be zero. In this case, you should still detect if the item can be handled (returning + * {@code OptionalInt#of(0)} if so), but should NOT modify the stack or inventory. + * @return The amount of fuel gained, or {@link OptionalInt#empty()} if this handler does not accept the given item. + */ + OptionalInt refuel( ITurtleAccess turtle, ItemStack stack, int slot, int limit ); +} diff --git a/src/main/java/dan200/computercraft/api/turtle/TurtleUpgradeDataProvider.java b/src/main/java/dan200/computercraft/api/turtle/TurtleUpgradeDataProvider.java index 4aabe035b..f93b5d692 100644 --- a/src/main/java/dan200/computercraft/api/turtle/TurtleUpgradeDataProvider.java +++ b/src/main/java/dan200/computercraft/api/turtle/TurtleUpgradeDataProvider.java @@ -35,7 +35,7 @@ public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider extends Upgra *

* This is largely intended for use with Forge Registry methods/classes, such as {@link DeferredRegister} and * {@link RegistryManager#getRegistry(ResourceKey)}. - * - * @see #registry() */ ResourceKey>> REGISTRY_ID = ResourceKey.createRegistryKey( new ResourceLocation( ComputerCraft.MOD_ID, "turtle_upgrade_serialiser" ) ); @@ -88,7 +86,9 @@ public interface TurtleUpgradeSerialiser extends Upgra * * @return The registry for pocket upgrade serialisers. * @see #REGISTRY_ID + * @deprecated Use {@link #REGISTRY_ID} directly. */ + @Deprecated( forRemoval = true ) static IForgeRegistry> registry() { return RegistryManager.ACTIVE.getRegistry( REGISTRY_ID ); diff --git a/src/main/java/dan200/computercraft/api/turtle/event/TurtleEvent.java b/src/main/java/dan200/computercraft/api/turtle/event/TurtleEvent.java index 4d3ba3776..3ede5d34c 100644 --- a/src/main/java/dan200/computercraft/api/turtle/event/TurtleEvent.java +++ b/src/main/java/dan200/computercraft/api/turtle/event/TurtleEvent.java @@ -16,7 +16,10 @@ import java.util.Objects; * so sever specific methods on {@link ITurtleAccess} are safe to use. *

* You should generally not need to subscribe to this event, preferring one of the more specific classes. + * + * @deprecated No longer needed, see {@link TurtleRefuelEvent}. */ +@Deprecated( forRemoval = true ) public abstract class TurtleEvent extends Event { private final ITurtleAccess turtle; diff --git a/src/main/java/dan200/computercraft/api/turtle/event/TurtleRefuelEvent.java b/src/main/java/dan200/computercraft/api/turtle/event/TurtleRefuelEvent.java index da6b39fd5..692f1dacc 100644 --- a/src/main/java/dan200/computercraft/api/turtle/event/TurtleRefuelEvent.java +++ b/src/main/java/dan200/computercraft/api/turtle/event/TurtleRefuelEvent.java @@ -6,6 +6,7 @@ package dan200.computercraft.api.turtle.event; import dan200.computercraft.api.turtle.ITurtleAccess; +import dan200.computercraft.api.turtle.TurtleRefuelHandler; import net.minecraft.world.item.ItemStack; import javax.annotation.Nonnull; @@ -16,7 +17,10 @@ import java.util.Objects; * Fired when a turtle attempts to refuel from an item. *

* One may use {@link #setHandler(Handler)} to register a custom fuel provider for a given item. + * + * @deprecated Use {@link TurtleRefuelHandler} instead. */ +@Deprecated( forRemoval = true ) public class TurtleRefuelEvent extends TurtleEvent { private final ItemStack stack; diff --git a/src/main/java/dan200/computercraft/api/upgrades/UpgradeDataProvider.java b/src/main/java/dan200/computercraft/api/upgrades/UpgradeDataProvider.java index 72706254f..c41f4e9fc 100644 --- a/src/main/java/dan200/computercraft/api/upgrades/UpgradeDataProvider.java +++ b/src/main/java/dan200/computercraft/api/upgrades/UpgradeDataProvider.java @@ -10,13 +10,15 @@ import com.google.gson.JsonParseException; import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser; import dan200.computercraft.internal.upgrades.SerialiserWithCraftingItem; import dan200.computercraft.internal.upgrades.SimpleSerialiser; +import net.minecraft.core.Registry; import net.minecraft.data.CachedOutput; import net.minecraft.data.DataGenerator; import net.minecraft.data.DataProvider; +import net.minecraft.resources.ResourceKey; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; import net.minecraftforge.registries.ForgeRegistries; -import net.minecraftforge.registries.IForgeRegistry; +import net.minecraftforge.registries.RegistryManager; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -28,8 +30,8 @@ import java.util.function.Consumer; import java.util.function.Function; /** - * A data generator/provider for turtle and pocket computer upgrades. This should not be extended direclty, instead see - * the other sub-classes. + * A data generator/provider for turtle and pocket computer upgrades. This should not be extended directly, instead see + * the other subclasses. * * @param The base class of upgrades. * @param The upgrade serialiser to register for. @@ -41,11 +43,11 @@ public abstract class UpgradeDataProvider registry; + private final ResourceKey> registry; private List upgrades; - protected UpgradeDataProvider( @Nonnull DataGenerator generator, @Nonnull String name, @Nonnull String folder, @Nonnull IForgeRegistry registry ) + protected UpgradeDataProvider( @Nonnull DataGenerator generator, @Nonnull String name, @Nonnull String folder, @Nonnull ResourceKey> registry ) { this.generator = generator; this.name = name; @@ -94,7 +96,7 @@ public abstract class UpgradeDataProvider * Example usage: *

{@code
      * protected void addUpgrades(@Nonnull Consumer>> addUpgrade) {
@@ -109,6 +111,7 @@ public abstract class UpgradeDataProvider seen = new HashSet<>();
@@ -153,7 +156,7 @@ public abstract class UpgradeDataProvider getWiredElementAt( @Nonnull BlockGetter world, @Nonnull BlockPos pos, @Nonnull Direction side );
 
+    void registerRefuelHandler( @Nonnull TurtleRefuelHandler handler );
+
     DetailRegistry getItemStackDetailRegistry();
 
     DetailRegistry getBlockInWorldDetailRegistry();
diff --git a/src/main/java/dan200/computercraft/impl/TurtleRefuelHandlers.java b/src/main/java/dan200/computercraft/impl/TurtleRefuelHandlers.java
new file mode 100644
index 000000000..b1a64e3c3
--- /dev/null
+++ b/src/main/java/dan200/computercraft/impl/TurtleRefuelHandlers.java
@@ -0,0 +1,68 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.impl;
+
+import dan200.computercraft.api.turtle.ITurtleAccess;
+import dan200.computercraft.api.turtle.TurtleRefuelHandler;
+import dan200.computercraft.api.turtle.event.TurtleRefuelEvent;
+import net.minecraft.world.item.ItemStack;
+import net.minecraftforge.common.MinecraftForge;
+
+import java.util.List;
+import java.util.Objects;
+import java.util.OptionalInt;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Registry of {@link TurtleRefuelHandler}s.
+ */
+public final class TurtleRefuelHandlers
+{
+    private static final List handlers = new CopyOnWriteArrayList<>();
+
+    static
+    {
+        // Register a fallback handler for our event.
+        handlers.add( ( turtle, stack, slot, limit ) -> {
+            @SuppressWarnings( "removal" ) TurtleRefuelEvent event = new TurtleRefuelEvent( turtle, stack );
+            MinecraftForge.EVENT_BUS.post( event );
+            if( event.getHandler() == null ) return OptionalInt.empty();
+            if( limit == 0 ) return OptionalInt.of( 0 );
+            return OptionalInt.of( event.getHandler().refuel( turtle, stack, slot, limit ) );
+        } );
+    }
+
+    private TurtleRefuelHandlers()
+    {
+    }
+
+    public static void register( TurtleRefuelHandler handler )
+    {
+        Objects.requireNonNull( handler, "handler cannot be null" );
+        handlers.add( handler );
+    }
+
+    public static OptionalInt refuel( ITurtleAccess turtle, ItemStack stack, int slot, int limit )
+    {
+        for( var handler : handlers )
+        {
+            var fuel = handler.refuel( turtle, stack, slot, limit );
+            if( fuel.isPresent() )
+            {
+                var refuelled = fuel.getAsInt();
+                if( refuelled < 0 ) throw new IllegalStateException( handler + " returned a negative value" );
+                if( limit == 0 && refuelled != 0 )
+                {
+                    throw new IllegalStateException( handler + " refuelled despite given a limit of 0" );
+                }
+
+                return fuel;
+            }
+        }
+
+        return OptionalInt.empty();
+    }
+}
diff --git a/src/main/java/dan200/computercraft/shared/Registry.java b/src/main/java/dan200/computercraft/shared/Registry.java
index 1871bfa7b..dbd753e8a 100644
--- a/src/main/java/dan200/computercraft/shared/Registry.java
+++ b/src/main/java/dan200/computercraft/shared/Registry.java
@@ -8,7 +8,9 @@ package dan200.computercraft.shared;
 import com.mojang.brigadier.arguments.ArgumentType;
 import dan200.computercraft.ComputerCraft;
 import dan200.computercraft.api.ComputerCraftAPI;
-import dan200.computercraft.api.detail.DetailRegistries;
+import dan200.computercraft.api.ForgeComputerCraftAPI;
+import dan200.computercraft.api.detail.ForgeDetailRegistries;
+import dan200.computercraft.api.detail.VanillaDetailRegistries;
 import dan200.computercraft.api.media.IMedia;
 import dan200.computercraft.api.network.wired.IWiredElement;
 import dan200.computercraft.api.peripheral.IPeripheral;
@@ -409,13 +411,13 @@ public final class Registry
 
         // Register generic capabilities. This can technically be done off-thread, but we need it to happen
         // after Forge's common setup, so this is easiest.
-        ComputerCraftAPI.registerGenericCapability( ForgeCapabilities.ITEM_HANDLER );
-        ComputerCraftAPI.registerGenericCapability( ForgeCapabilities.ENERGY );
-        ComputerCraftAPI.registerGenericCapability( ForgeCapabilities.FLUID_HANDLER );
+        ForgeComputerCraftAPI.registerGenericCapability( ForgeCapabilities.ITEM_HANDLER );
+        ForgeComputerCraftAPI.registerGenericCapability( ForgeCapabilities.ENERGY );
+        ForgeComputerCraftAPI.registerGenericCapability( ForgeCapabilities.FLUID_HANDLER );
 
-        DetailRegistries.ITEM_STACK.addProvider( ItemData::fill );
-        DetailRegistries.BLOCK_IN_WORLD.addProvider( BlockData::fill );
-        DetailRegistries.FLUID_STACK.addProvider( FluidData::fill );
+        VanillaDetailRegistries.ITEM_STACK.addProvider( ItemData::fill );
+        VanillaDetailRegistries.BLOCK_IN_WORLD.addProvider( BlockData::fill );
+        ForgeDetailRegistries.FLUID_STACK.addProvider( FluidData::fill );
 
         CauldronInteraction.WATER.put( ModItems.TURTLE_NORMAL.get(), ItemTurtle.CAULDRON_INTERACTION );
         CauldronInteraction.WATER.put( ModItems.TURTLE_ADVANCED.get(), ItemTurtle.CAULDRON_INTERACTION );
diff --git a/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java b/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java
index 2dcfecfd1..471f42132 100644
--- a/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java
+++ b/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java
@@ -9,7 +9,7 @@ import com.mojang.brigadier.tree.CommandNode;
 import com.mojang.brigadier.tree.LiteralCommandNode;
 import dan200.computercraft.ComputerCraft;
 import dan200.computercraft.api.detail.BlockReference;
-import dan200.computercraft.api.detail.DetailRegistries;
+import dan200.computercraft.api.detail.VanillaDetailRegistries;
 import dan200.computercraft.api.lua.*;
 import dan200.computercraft.shared.computer.blocks.TileCommandComputer;
 import dan200.computercraft.shared.util.NBTUtil;
@@ -77,7 +77,7 @@ public class CommandAPI implements ILuaAPI
     {
         // Get the details of the block
         BlockReference block = new BlockReference( world, pos );
-        Map table = DetailRegistries.BLOCK_IN_WORLD.getDetails( block );
+        Map table = VanillaDetailRegistries.BLOCK_IN_WORLD.getDetails( block );
 
         BlockEntity tile = block.blockEntity();
         if( tile != null ) table.put( "nbt", NBTUtil.toLua( tile.saveWithFullMetadata() ) );
diff --git a/src/main/java/dan200/computercraft/shared/network/client/UpgradesLoadedMessage.java b/src/main/java/dan200/computercraft/shared/network/client/UpgradesLoadedMessage.java
index db9038aac..74365e970 100644
--- a/src/main/java/dan200/computercraft/shared/network/client/UpgradesLoadedMessage.java
+++ b/src/main/java/dan200/computercraft/shared/network/client/UpgradesLoadedMessage.java
@@ -72,8 +72,8 @@ public class UpgradesLoadedMessage implements NetworkMessage
     @Override
     public void toBytes( @Nonnull FriendlyByteBuf buf )
     {
-        toBytes( buf, TurtleUpgradeSerialiser.registry(), turtleUpgrades );
-        toBytes( buf, PocketUpgradeSerialiser.registry(), pocketUpgrades );
+        toBytes( buf, RegistryManager.ACTIVE.getRegistry( TurtleUpgradeSerialiser.REGISTRY_ID ), turtleUpgrades );
+        toBytes( buf, RegistryManager.ACTIVE.getRegistry( PocketUpgradeSerialiser.REGISTRY_ID ), pocketUpgrades );
     }
 
     private , T extends IUpgradeBase> void toBytes(
diff --git a/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/FluidMethods.java b/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/FluidMethods.java
index ba2790646..731f8b071 100644
--- a/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/FluidMethods.java
+++ b/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/FluidMethods.java
@@ -6,7 +6,7 @@
 package dan200.computercraft.shared.peripheral.generic.methods;
 
 import dan200.computercraft.ComputerCraft;
-import dan200.computercraft.api.detail.DetailRegistries;
+import dan200.computercraft.api.detail.ForgeDetailRegistries;
 import dan200.computercraft.api.lua.LuaException;
 import dan200.computercraft.api.lua.LuaFunction;
 import dan200.computercraft.api.peripheral.GenericPeripheral;
@@ -74,7 +74,7 @@ public class FluidMethods implements GenericPeripheral
         for( int i = 0; i < size; i++ )
         {
             FluidStack stack = fluids.getFluidInTank( i );
-            if( !stack.isEmpty() ) result.put( i + 1, DetailRegistries.FLUID_STACK.getBasicDetails( stack ) );
+            if( !stack.isEmpty() ) result.put( i + 1, ForgeDetailRegistries.FLUID_STACK.getBasicDetails( stack ) );
         }
 
         return result;
diff --git a/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/InventoryMethods.java b/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/InventoryMethods.java
index 7111cb44e..ffc5fc189 100644
--- a/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/InventoryMethods.java
+++ b/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/InventoryMethods.java
@@ -6,7 +6,7 @@
 package dan200.computercraft.shared.peripheral.generic.methods;
 
 import dan200.computercraft.ComputerCraft;
-import dan200.computercraft.api.detail.DetailRegistries;
+import dan200.computercraft.api.detail.VanillaDetailRegistries;
 import dan200.computercraft.api.lua.ILuaContext;
 import dan200.computercraft.api.lua.LuaException;
 import dan200.computercraft.api.lua.LuaFunction;
@@ -99,7 +99,7 @@ public class InventoryMethods implements GenericPeripheral
         for( int i = 0; i < size; i++ )
         {
             ItemStack stack = inventory.getStackInSlot( i );
-            if( !stack.isEmpty() ) result.put( i + 1, DetailRegistries.ITEM_STACK.getBasicDetails( stack ) );
+            if( !stack.isEmpty() ) result.put( i + 1, VanillaDetailRegistries.ITEM_STACK.getBasicDetails( stack ) );
         }
 
         return result;
@@ -149,7 +149,7 @@ public class InventoryMethods implements GenericPeripheral
         assertBetween( slot, 1, inventory.getSlots(), "Slot out of range (%s)" );
 
         ItemStack stack = inventory.getStackInSlot( slot - 1 );
-        return stack.isEmpty() ? null : DetailRegistries.ITEM_STACK.getDetails( stack );
+        return stack.isEmpty() ? null : VanillaDetailRegistries.ITEM_STACK.getDetails( stack );
     }
 
     /**
diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/BlockCable.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/BlockCable.java
index c836b06fa..2a2229f2d 100644
--- a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/BlockCable.java
+++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/BlockCable.java
@@ -6,7 +6,7 @@
 package dan200.computercraft.shared.peripheral.modem.wired;
 
 import com.google.common.collect.ImmutableMap;
-import dan200.computercraft.api.ComputerCraftAPI;
+import dan200.computercraft.api.ForgeComputerCraftAPI;
 import dan200.computercraft.shared.Registry;
 import dan200.computercraft.shared.common.BlockGeneric;
 import dan200.computercraft.shared.util.WaterloggableHelpers;
@@ -86,7 +86,7 @@ public class BlockCable extends BlockGeneric implements SimpleWaterloggedBlock
     {
         if( !state.getValue( CABLE ) ) return false;
         if( state.getValue( MODEM ).getFacing() == direction ) return true;
-        return ComputerCraftAPI.getWiredElementAt( world, pos.relative( direction ), direction.getOpposite() ).isPresent();
+        return ForgeComputerCraftAPI.getWiredElementAt( world, pos.relative( direction ), direction.getOpposite() ).isPresent();
     }
 
     @Nonnull
diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileCable.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileCable.java
index c74d35197..b2e552667 100644
--- a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileCable.java
+++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileCable.java
@@ -6,7 +6,7 @@
 package dan200.computercraft.shared.peripheral.modem.wired;
 
 import com.google.common.base.Objects;
-import dan200.computercraft.api.ComputerCraftAPI;
+import dan200.computercraft.api.ForgeComputerCraftAPI;
 import dan200.computercraft.api.network.wired.IWiredElement;
 import dan200.computercraft.api.network.wired.IWiredNode;
 import dan200.computercraft.api.peripheral.IPeripheral;
@@ -346,7 +346,7 @@ public class TileCable extends TileGeneric
             BlockPos offset = current.relative( facing );
             if( !world.isLoaded( offset ) ) continue;
 
-            LazyOptional element = ComputerCraftAPI.getWiredElementAt( world, offset, facing.getOpposite() );
+            LazyOptional element = ForgeComputerCraftAPI.getWiredElementAt( world, offset, facing.getOpposite() );
             if( !element.isPresent() ) continue;
 
             element.addListener( connectedNodeChanged );
diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileWiredModemFull.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileWiredModemFull.java
index 76f519378..fe573ca77 100644
--- a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileWiredModemFull.java
+++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileWiredModemFull.java
@@ -6,7 +6,7 @@
 package dan200.computercraft.shared.peripheral.modem.wired;
 
 import com.google.common.base.Objects;
-import dan200.computercraft.api.ComputerCraftAPI;
+import dan200.computercraft.api.ForgeComputerCraftAPI;
 import dan200.computercraft.api.network.wired.IWiredElement;
 import dan200.computercraft.api.network.wired.IWiredNode;
 import dan200.computercraft.api.peripheral.IPeripheral;
@@ -308,7 +308,7 @@ public class TileWiredModemFull extends TileGeneric
             BlockPos offset = current.relative( facing );
             if( !world.isLoaded( offset ) ) continue;
 
-            LazyOptional element = ComputerCraftAPI.getWiredElementAt( world, offset, facing.getOpposite() );
+            LazyOptional element = ForgeComputerCraftAPI.getWiredElementAt( world, offset, facing.getOpposite() );
             if( !element.isPresent() ) continue;
 
             element.addListener( connectedNodeChanged );
diff --git a/src/main/java/dan200/computercraft/shared/turtle/FurnaceRefuelHandler.java b/src/main/java/dan200/computercraft/shared/turtle/FurnaceRefuelHandler.java
index 864697b98..c8ec4aa83 100644
--- a/src/main/java/dan200/computercraft/shared/turtle/FurnaceRefuelHandler.java
+++ b/src/main/java/dan200/computercraft/shared/turtle/FurnaceRefuelHandler.java
@@ -5,20 +5,15 @@
  */
 package dan200.computercraft.shared.turtle;
 
-import dan200.computercraft.ComputerCraft;
 import dan200.computercraft.api.turtle.ITurtleAccess;
-import dan200.computercraft.api.turtle.event.TurtleRefuelEvent;
-import dan200.computercraft.shared.util.InventoryUtil;
-import dan200.computercraft.shared.util.WorldUtil;
+import dan200.computercraft.api.turtle.TurtleRefuelHandler;
 import net.minecraft.world.item.ItemStack;
 import net.minecraftforge.common.ForgeHooks;
-import net.minecraftforge.eventbus.api.SubscribeEvent;
-import net.minecraftforge.fml.common.Mod;
 
 import javax.annotation.Nonnull;
+import java.util.OptionalInt;
 
-@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID )
-public final class FurnaceRefuelHandler implements TurtleRefuelEvent.Handler
+public final class FurnaceRefuelHandler implements TurtleRefuelHandler
 {
     private static final FurnaceRefuelHandler INSTANCE = new FurnaceRefuelHandler();
 
@@ -27,37 +22,29 @@ public final class FurnaceRefuelHandler implements TurtleRefuelEvent.Handler
     }
 
     @Override
-    public int refuel( @Nonnull ITurtleAccess turtle, @Nonnull ItemStack currentStack, int slot, int limit )
+    public OptionalInt refuel( @Nonnull ITurtleAccess turtle, @Nonnull ItemStack currentStack, int slot, int limit )
     {
+        int fuelPerItem = getFuelPerItem( currentStack );
+        if( fuelPerItem <= 0 ) return OptionalInt.empty();
+        if( limit == 0 ) return OptionalInt.of( 0 );
+
         int fuelSpaceLeft = turtle.getFuelLimit() - turtle.getFuelLevel();
-        int fuelPerItem = getFuelPerItem( turtle.getItemHandler().getStackInSlot( slot ) );
         int fuelItemLimit = (int) Math.ceil( fuelSpaceLeft / (double) fuelPerItem );
         if( limit > fuelItemLimit ) limit = fuelItemLimit;
 
-        ItemStack stack = turtle.getItemHandler().extractItem( slot, limit, false );
+        ItemStack stack = turtle.getInventory().removeItem( slot, limit );
         int fuelToGive = fuelPerItem * stack.getCount();
         // Store the replacement item in the inventory
         ItemStack replacementStack = ForgeHooks.getCraftingRemainingItem( stack );
-        if( !replacementStack.isEmpty() )
-        {
-            ItemStack remainder = InventoryUtil.storeItems( replacementStack, turtle.getItemHandler(), turtle.getSelectedSlot() );
-            if( !remainder.isEmpty() )
-            {
-                WorldUtil.dropItemStack( remainder, turtle.getLevel(), turtle.getPosition(), turtle.getDirection().getOpposite() );
-            }
-        }
+        if( !replacementStack.isEmpty() ) TurtleUtil.storeItemOrDrop( turtle, replacementStack );
 
-        return fuelToGive;
+        turtle.getInventory().setChanged();
+
+        return OptionalInt.of( fuelToGive );
     }
 
     private static int getFuelPerItem( @Nonnull ItemStack stack )
     {
         return (ForgeHooks.getBurnTime( stack, null ) * 5) / 100;
     }
-
-    @SubscribeEvent
-    public static void onTurtleRefuel( TurtleRefuelEvent event )
-    {
-        if( event.getHandler() == null && getFuelPerItem( event.getStack() ) > 0 ) event.setHandler( INSTANCE );
-    }
 }
diff --git a/src/main/java/dan200/computercraft/shared/turtle/TurtleUtil.java b/src/main/java/dan200/computercraft/shared/turtle/TurtleUtil.java
new file mode 100644
index 000000000..08d6a1770
--- /dev/null
+++ b/src/main/java/dan200/computercraft/shared/turtle/TurtleUtil.java
@@ -0,0 +1,50 @@
+/*
+ * This file is part of ComputerCraft - http://www.computercraft.info
+ * Copyright Daniel Ratcliffe, 2011-2022. Do not distribute without permission.
+ * Send enquiries to dratcliffe@gmail.com
+ */
+package dan200.computercraft.shared.turtle;
+
+import dan200.computercraft.api.turtle.ITurtleAccess;
+import dan200.computercraft.shared.util.DropConsumer;
+import dan200.computercraft.shared.util.InventoryUtil;
+import dan200.computercraft.shared.util.WorldUtil;
+import net.minecraft.world.item.ItemStack;
+
+import java.util.function.Function;
+
+public class TurtleUtil
+{
+    /**
+     * Store an item in this turtle, or drop it if there is room remaining.
+     *
+     * @param turtle The turtle to store items into.
+     * @param stack  The stack to store.
+     */
+    public static void storeItemOrDrop( ITurtleAccess turtle, ItemStack stack )
+    {
+        if( stack.isEmpty() ) return;
+        if( turtle.isRemoved() )
+        {
+            WorldUtil.dropItemStack( stack, turtle.getLevel(), turtle.getPosition(), null );
+            return;
+        }
+
+        // Put the remainder back in the turtle
+        ItemStack remainder = InventoryUtil.storeItems( stack, turtle.getItemHandler(), turtle.getSelectedSlot() );
+        if( remainder.isEmpty() ) return;
+
+        WorldUtil.dropItemStack( remainder, turtle.getLevel(), turtle.getPosition(), turtle.getDirection().getOpposite() );
+    }
+
+    public static Function dropConsumer( ITurtleAccess turtle )
+    {
+        return stack -> turtle.isRemoved() ? stack : InventoryUtil.storeItems( stack, turtle.getItemHandler(), turtle.getSelectedSlot() );
+    }
+
+    public static void stopConsuming( ITurtleAccess turtle )
+    {
+        var direction = turtle.isRemoved() ? null : turtle.getDirection().getOpposite();
+        DropConsumer.clearAndDrop( turtle.getLevel(), turtle.getPosition(), direction );
+    }
+}
diff --git a/src/main/java/dan200/computercraft/shared/turtle/apis/TurtleAPI.java b/src/main/java/dan200/computercraft/shared/turtle/apis/TurtleAPI.java
index 1d552606a..8492f09d3 100644
--- a/src/main/java/dan200/computercraft/shared/turtle/apis/TurtleAPI.java
+++ b/src/main/java/dan200/computercraft/shared/turtle/apis/TurtleAPI.java
@@ -5,7 +5,7 @@
  */
 package dan200.computercraft.shared.turtle.apis;
 
-import dan200.computercraft.api.detail.DetailRegistries;
+import dan200.computercraft.api.detail.VanillaDetailRegistries;
 import dan200.computercraft.api.lua.*;
 import dan200.computercraft.api.turtle.ITurtleAccess;
 import dan200.computercraft.api.turtle.ITurtleCommand;
@@ -820,7 +820,7 @@ public class TurtleAPI implements ILuaAPI
         if( stack.isEmpty() ) return new Object[] { null };
 
         Map table = detailed
-            ? DetailRegistries.ITEM_STACK.getDetails( stack )
+            ? VanillaDetailRegistries.ITEM_STACK.getDetails( stack )
             : ItemData.fillBasicSafe( new HashMap<>(), stack );
 
         return new Object[] { table };
diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java
index bdef60b58..5b8b844b9 100644
--- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java
+++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java
@@ -287,6 +287,12 @@ public class TurtleBrain implements ITurtleAccess
         return owner.getBlockPos();
     }
 
+    @Override
+    public boolean isRemoved()
+    {
+        return owner.isRemoved();
+    }
+
     @Override
     public boolean teleportTo( @Nonnull Level world, @Nonnull BlockPos pos )
     {
diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleCraftCommand.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleCraftCommand.java
index 03f281a89..e3afa634f 100644
--- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleCraftCommand.java
+++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleCraftCommand.java
@@ -9,9 +9,8 @@ import dan200.computercraft.api.turtle.ITurtleAccess;
 import dan200.computercraft.api.turtle.ITurtleCommand;
 import dan200.computercraft.api.turtle.TurtleAnimation;
 import dan200.computercraft.api.turtle.TurtleCommandResult;
+import dan200.computercraft.shared.turtle.TurtleUtil;
 import dan200.computercraft.shared.turtle.upgrades.TurtleInventoryCrafting;
-import dan200.computercraft.shared.util.InventoryUtil;
-import dan200.computercraft.shared.util.WorldUtil;
 import net.minecraft.world.item.ItemStack;
 
 import javax.annotation.Nonnull;
@@ -36,14 +35,7 @@ public class TurtleCraftCommand implements ITurtleCommand
         if( results == null ) return TurtleCommandResult.failure( "No matching recipes" );
 
         // Store or drop any remainders
-        for( ItemStack stack : results )
-        {
-            ItemStack remainder = InventoryUtil.storeItems( stack, turtle.getItemHandler(), turtle.getSelectedSlot() );
-            if( !remainder.isEmpty() )
-            {
-                WorldUtil.dropItemStack( remainder, turtle.getLevel(), turtle.getPosition(), turtle.getDirection() );
-            }
-        }
+        for( ItemStack stack : results ) TurtleUtil.storeItemOrDrop( turtle, stack );
 
         if( !results.isEmpty() ) turtle.playAnimation( TurtleAnimation.WAIT );
         return TurtleCommandResult.success();
diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleDropCommand.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleDropCommand.java
index 5a2f7adc5..ce902933d 100644
--- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleDropCommand.java
+++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleDropCommand.java
@@ -45,11 +45,12 @@ public class TurtleDropCommand implements ITurtleCommand
         Direction direction = this.direction.toWorldDir( turtle );
 
         // Get things to drop
-        ItemStack stack = InventoryUtil.takeItems( quantity, turtle.getItemHandler(), turtle.getSelectedSlot(), 1, turtle.getSelectedSlot() );
+        ItemStack stack = turtle.getInventory().removeItem( turtle.getSelectedSlot(), quantity );
         if( stack.isEmpty() )
         {
             return TurtleCommandResult.failure( "No items to drop" );
         }
+        turtle.getInventory().setChanged();
 
         // Get inventory for thing in front
         Level world = turtle.getLevel();
diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleEquipCommand.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleEquipCommand.java
index 21174d0fa..014bb7912 100644
--- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleEquipCommand.java
+++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleEquipCommand.java
@@ -7,11 +7,8 @@ package dan200.computercraft.shared.turtle.core;
 
 import dan200.computercraft.api.turtle.*;
 import dan200.computercraft.shared.TurtleUpgrades;
-import dan200.computercraft.shared.util.InventoryUtil;
-import dan200.computercraft.shared.util.WorldUtil;
-import net.minecraft.core.BlockPos;
+import dan200.computercraft.shared.turtle.TurtleUtil;
 import net.minecraft.world.item.ItemStack;
-import net.minecraftforge.items.IItemHandler;
 
 import javax.annotation.Nonnull;
 
@@ -31,8 +28,7 @@ public class TurtleEquipCommand implements ITurtleCommand
         // Determine the upgrade to equipLeft
         ITurtleUpgrade newUpgrade;
         ItemStack newUpgradeStack;
-        IItemHandler inventory = turtle.getItemHandler();
-        ItemStack selectedStack = inventory.getStackInSlot( turtle.getSelectedSlot() );
+        ItemStack selectedStack = turtle.getInventory().getItem( turtle.getSelectedSlot() );
         if( !selectedStack.isEmpty() )
         {
             newUpgradeStack = selectedStack.copy();
@@ -59,22 +55,8 @@ public class TurtleEquipCommand implements ITurtleCommand
         }
 
         // Do the swapping:
-        if( newUpgradeStack != null )
-        {
-            // Consume new upgrades item
-            InventoryUtil.takeItems( 1, inventory, turtle.getSelectedSlot(), 1, turtle.getSelectedSlot() );
-        }
-        if( oldUpgradeStack != null )
-        {
-            // Store old upgrades item
-            ItemStack remainder = InventoryUtil.storeItems( oldUpgradeStack, inventory, turtle.getSelectedSlot() );
-            if( !remainder.isEmpty() )
-            {
-                // If there's no room for the items, drop them
-                BlockPos position = turtle.getPosition();
-                WorldUtil.dropItemStack( remainder, turtle.getLevel(), position, turtle.getDirection() );
-            }
-        }
+        if( newUpgradeStack != null ) turtle.getInventory().removeItem( turtle.getSelectedSlot(), 1 );
+        if( oldUpgradeStack != null ) TurtleUtil.storeItemOrDrop( turtle, oldUpgradeStack );
         turtle.setUpgrade( side, newUpgrade );
 
         // Animate
diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleInspectCommand.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleInspectCommand.java
index 3ff1a7936..c4ecc0704 100644
--- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleInspectCommand.java
+++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleInspectCommand.java
@@ -6,7 +6,7 @@
 package dan200.computercraft.shared.turtle.core;
 
 import dan200.computercraft.api.detail.BlockReference;
-import dan200.computercraft.api.detail.DetailRegistries;
+import dan200.computercraft.api.detail.VanillaDetailRegistries;
 import dan200.computercraft.api.turtle.ITurtleAccess;
 import dan200.computercraft.api.turtle.ITurtleCommand;
 import dan200.computercraft.api.turtle.TurtleCommandResult;
@@ -41,7 +41,7 @@ public class TurtleInspectCommand implements ITurtleCommand
         BlockReference block = new BlockReference( world, newPosition );
         if( block.state().isAir() ) return TurtleCommandResult.failure( "No block to inspect" );
 
-        Map table = DetailRegistries.BLOCK_IN_WORLD.getDetails( block );
+        Map table = VanillaDetailRegistries.BLOCK_IN_WORLD.getDetails( block );
 
         return TurtleCommandResult.success( new Object[] { table } );
 
diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlaceCommand.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlaceCommand.java
index 493a1ffe8..0ac262f57 100644
--- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlaceCommand.java
+++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlaceCommand.java
@@ -11,6 +11,7 @@ import dan200.computercraft.api.turtle.ITurtleCommand;
 import dan200.computercraft.api.turtle.TurtleAnimation;
 import dan200.computercraft.api.turtle.TurtleCommandResult;
 import dan200.computercraft.shared.TurtlePermissions;
+import dan200.computercraft.shared.turtle.TurtleUtil;
 import dan200.computercraft.shared.util.DropConsumer;
 import dan200.computercraft.shared.util.InventoryUtil;
 import dan200.computercraft.shared.util.WorldUtil;
@@ -124,7 +125,6 @@ public class TurtlePlaceCommand implements ITurtleCommand
     {
         // See if there is an entity present
         final Level world = turtle.getLevel();
-        final BlockPos position = turtle.getPosition();
         Vec3 turtlePos = turtlePlayer.position();
         Vec3 rayDir = turtlePlayer.getViewVector( 1.0f );
         Pair hit = WorldUtil.rayTraceEntities( world, turtlePos, rayDir, 1.5 );
@@ -139,7 +139,7 @@ public class TurtlePlaceCommand implements ITurtleCommand
 
         boolean placed = doDeployOnEntity( stack, turtlePlayer, hitEntity, hitPos );
 
-        DropConsumer.clearAndDrop( world, position, turtle.getDirection().getOpposite() );
+        TurtleUtil.stopConsuming( turtle );
         return placed;
     }
 
diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java
index 0905b696c..d0d799e27 100644
--- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java
+++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java
@@ -8,9 +8,8 @@ package dan200.computercraft.shared.turtle.core;
 import com.mojang.authlib.GameProfile;
 import dan200.computercraft.ComputerCraft;
 import dan200.computercraft.api.turtle.ITurtleAccess;
+import dan200.computercraft.shared.turtle.TurtleUtil;
 import dan200.computercraft.shared.util.DirectionUtil;
-import dan200.computercraft.shared.util.InventoryUtil;
-import dan200.computercraft.shared.util.WorldUtil;
 import net.minecraft.core.BlockPos;
 import net.minecraft.core.Direction;
 import net.minecraft.server.level.ServerLevel;
@@ -157,39 +156,33 @@ public final class TurtlePlayer extends FakePlayer
         getInventory().clearContent();
 
         int currentSlot = turtle.getSelectedSlot();
-        int slots = turtle.getItemHandler().getSlots();
+        int slots = turtle.getInventory().getContainerSize();
 
         // Load up the fake inventory
         getInventory().selected = 0;
         for( int i = 0; i < slots; i++ )
         {
-            getInventory().setItem( i, turtle.getItemHandler().getStackInSlot( (currentSlot + i) % slots ) );
+            getInventory().setItem( i, turtle.getInventory().getItem( (currentSlot + i) % slots ) );
         }
     }
 
     public void unloadInventory( ITurtleAccess turtle )
     {
         int currentSlot = turtle.getSelectedSlot();
-        int slots = turtle.getItemHandler().getSlots();
+        int slots = turtle.getInventory().getContainerSize();
 
         // Load up the fake inventory
         getInventory().selected = 0;
         for( int i = 0; i < slots; i++ )
         {
-            turtle.getItemHandler().setStackInSlot( (currentSlot + i) % slots, getInventory().getItem( i ) );
+            turtle.getInventory().setItem( (currentSlot + i) % slots, getInventory().getItem( i ) );
         }
 
         // Store (or drop) anything else we found
-        BlockPos dropPosition = turtle.getPosition();
-        Direction dropDirection = turtle.getDirection().getOpposite();
         int totalSize = getInventory().getContainerSize();
         for( int i = slots; i < totalSize; i++ )
         {
-            ItemStack remainder = InventoryUtil.storeItems( getInventory().getItem( i ), turtle.getItemHandler(), turtle.getSelectedSlot() );
-            if( !remainder.isEmpty() )
-            {
-                WorldUtil.dropItemStack( remainder, turtle.getLevel(), dropPosition, dropDirection );
-            }
+            TurtleUtil.storeItemOrDrop( turtle, getInventory().getItem( i ) );
         }
 
         getInventory().setChanged();
diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleRefuelCommand.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleRefuelCommand.java
index b3bcb89dd..0d4fdee8f 100644
--- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleRefuelCommand.java
+++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleRefuelCommand.java
@@ -9,9 +9,8 @@ import dan200.computercraft.api.turtle.ITurtleAccess;
 import dan200.computercraft.api.turtle.ITurtleCommand;
 import dan200.computercraft.api.turtle.TurtleAnimation;
 import dan200.computercraft.api.turtle.TurtleCommandResult;
-import dan200.computercraft.api.turtle.event.TurtleRefuelEvent;
+import dan200.computercraft.impl.TurtleRefuelHandlers;
 import net.minecraft.world.item.ItemStack;
-import net.minecraftforge.common.MinecraftForge;
 
 import javax.annotation.Nonnull;
 
@@ -32,13 +31,13 @@ public class TurtleRefuelCommand implements ITurtleCommand
         ItemStack stack = turtle.getInventory().getItem( slot );
         if( stack.isEmpty() ) return TurtleCommandResult.failure( "No items to combust" );
 
-        TurtleRefuelEvent event = new TurtleRefuelEvent( turtle, stack );
-        MinecraftForge.EVENT_BUS.post( event );
-        if( event.getHandler() == null ) return TurtleCommandResult.failure( "Items not combustible" );
+        var refuelled = TurtleRefuelHandlers.refuel( turtle, stack, slot, limit );
+        if( refuelled.isEmpty() ) return TurtleCommandResult.failure( "Items not combustible" );
 
-        if( limit != 0 )
+        var newFuel = refuelled.getAsInt();
+        if( newFuel != 0 )
         {
-            turtle.addFuel( event.getHandler().refuel( turtle, stack, slot, limit ) );
+            turtle.addFuel( newFuel );
             turtle.playAnimation( TurtleAnimation.WAIT );
         }
 
diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleTransferToCommand.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleTransferToCommand.java
index a2e25db10..02eef08b2 100644
--- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleTransferToCommand.java
+++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleTransferToCommand.java
@@ -30,7 +30,7 @@ public class TurtleTransferToCommand implements ITurtleCommand
     public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle )
     {
         // Take stack
-        ItemStack stack = InventoryUtil.takeItems( quantity, turtle.getItemHandler(), turtle.getSelectedSlot(), 1, turtle.getSelectedSlot() );
+        ItemStack stack = turtle.getInventory().removeItem( turtle.getSelectedSlot(), quantity );
         if( stack.isEmpty() )
         {
             turtle.playAnimation( TurtleAnimation.WAIT );
diff --git a/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleTool.java b/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleTool.java
index 685ed0026..9632f5064 100644
--- a/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleTool.java
+++ b/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleTool.java
@@ -9,11 +9,10 @@ import dan200.computercraft.ComputerCraft;
 import dan200.computercraft.api.ComputerCraftTags;
 import dan200.computercraft.api.turtle.*;
 import dan200.computercraft.shared.TurtlePermissions;
-import dan200.computercraft.shared.turtle.core.TurtleBrain;
+import dan200.computercraft.shared.turtle.TurtleUtil;
 import dan200.computercraft.shared.turtle.core.TurtlePlaceCommand;
 import dan200.computercraft.shared.turtle.core.TurtlePlayer;
 import dan200.computercraft.shared.util.DropConsumer;
-import dan200.computercraft.shared.util.InventoryUtil;
 import dan200.computercraft.shared.util.WorldUtil;
 import net.minecraft.core.BlockPos;
 import net.minecraft.core.Direction;
@@ -42,7 +41,6 @@ import org.apache.commons.lang3.tuple.Pair;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
-import java.util.function.Function;
 
 import static net.minecraft.nbt.Tag.TAG_COMPOUND;
 import static net.minecraft.nbt.Tag.TAG_LIST;
@@ -117,8 +115,6 @@ public class TurtleTool extends AbstractTurtleUpgrade
         // Create a fake player, and orient it appropriately
         Level world = turtle.getLevel();
         BlockPos position = turtle.getPosition();
-        BlockEntity turtleTile = turtle instanceof TurtleBrain ? ((TurtleBrain) turtle).getOwner() : world.getBlockEntity( position );
-        if( turtleTile == null ) return TurtleCommandResult.failure( "Turtle has vanished from existence." );
 
         final TurtlePlayer turtlePlayer = TurtlePlayer.getWithPosition( turtle, position, direction );
 
@@ -141,7 +137,7 @@ public class TurtleTool extends AbstractTurtleUpgrade
             }
 
             // Start claiming entity drops
-            DropConsumer.set( hitEntity, turtleDropConsumer( turtleTile, turtle ) );
+            DropConsumer.set( hitEntity, TurtleUtil.dropConsumer( turtle ) );
 
             // Attack the entity
             boolean attacked = false;
@@ -166,7 +162,7 @@ public class TurtleTool extends AbstractTurtleUpgrade
             }
 
             // Stop claiming drops
-            stopConsuming( turtleTile, turtle );
+            TurtleUtil.stopConsuming( turtle );
 
             // Put everything we collected into the turtles inventory, then return
             if( attacked )
@@ -181,8 +177,7 @@ public class TurtleTool extends AbstractTurtleUpgrade
 
     private TurtleCommandResult dig( ITurtleAccess turtle, Direction direction )
     {
-        // TODO: HOE_TILL really, if it's ever implemented
-        if( item.canPerformAction( ToolActions.SHOVEL_FLATTEN ) || item.canPerformAction( ToolActions.HOE_DIG ) )
+        if( item.canPerformAction( ToolActions.SHOVEL_FLATTEN ) || item.canPerformAction( ToolActions.HOE_TILL ) )
         {
             if( TurtlePlaceCommand.deployCopiedItem( item.copy(), turtle, direction, null, null ) )
             {
@@ -193,8 +188,6 @@ public class TurtleTool extends AbstractTurtleUpgrade
         // Get ready to dig
         Level world = turtle.getLevel();
         BlockPos turtlePosition = turtle.getPosition();
-        BlockEntity turtleTile = turtle instanceof TurtleBrain ? ((TurtleBrain) turtle).getOwner() : world.getBlockEntity( turtlePosition );
-        if( turtleTile == null ) return TurtleCommandResult.failure( "Turtle has vanished from existence." );
 
         BlockPos blockPosition = turtlePosition.relative( direction );
         if( world.isEmptyBlock( blockPosition ) || WorldUtil.isLiquidBlock( world, blockPosition ) )
@@ -227,7 +220,7 @@ public class TurtleTool extends AbstractTurtleUpgrade
         if( !breakable.isSuccess() ) return breakable;
 
         // Consume the items the block drops
-        DropConsumer.set( world, blockPosition, turtleDropConsumer( turtleTile, turtle ) );
+        DropConsumer.set( world, blockPosition, TurtleUtil.dropConsumer( turtle ) );
 
         BlockEntity tile = world.getBlockEntity( blockPosition );
 
@@ -246,23 +239,12 @@ public class TurtleTool extends AbstractTurtleUpgrade
             state.getBlock().playerDestroy( world, turtlePlayer, blockPosition, state, tile, turtlePlayer.getMainHandItem() );
         }
 
-        stopConsuming( turtleTile, turtle );
+        TurtleUtil.stopConsuming( turtle );
 
         return TurtleCommandResult.success();
 
     }
 
-    private static Function turtleDropConsumer( BlockEntity tile, ITurtleAccess turtle )
-    {
-        return drop -> tile.isRemoved() ? drop : InventoryUtil.storeItems( drop, turtle.getItemHandler(), turtle.getSelectedSlot() );
-    }
-
-    private static void stopConsuming( BlockEntity tile, ITurtleAccess turtle )
-    {
-        Direction direction = tile.isRemoved() ? null : turtle.getDirection().getOpposite();
-        DropConsumer.clearAndDrop( turtle.getLevel(), turtle.getPosition(), direction );
-    }
-
     protected boolean isTriviallyBreakable( BlockGetter reader, BlockPos pos, BlockState state )
     {
         return state.is( ComputerCraftTags.Blocks.TURTLE_ALWAYS_BREAKABLE )
diff --git a/src/main/java/dan200/computercraft/shared/util/InventoryUtil.java b/src/main/java/dan200/computercraft/shared/util/InventoryUtil.java
index 94b5ff864..88a624ca3 100644
--- a/src/main/java/dan200/computercraft/shared/util/InventoryUtil.java
+++ b/src/main/java/dan200/computercraft/shared/util/InventoryUtil.java
@@ -127,12 +127,6 @@ public final class InventoryUtil
 
     // Methods for taking out of inventories
 
-    @Nonnull
-    public static ItemStack takeItems( int count, IItemHandler inventory, int begin )
-    {
-        return takeItems( count, inventory, 0, inventory.getSlots(), begin );
-    }
-
     @Nonnull
     public static ItemStack takeItems( int count, IItemHandler inventory )
     {
diff --git a/src/testMod/kotlin/dan200/computercraft/gametest/Turtle_Test.kt b/src/testMod/kotlin/dan200/computercraft/gametest/Turtle_Test.kt
index 41d378c6d..c732731e6 100644
--- a/src/testMod/kotlin/dan200/computercraft/gametest/Turtle_Test.kt
+++ b/src/testMod/kotlin/dan200/computercraft/gametest/Turtle_Test.kt
@@ -6,7 +6,7 @@
 package dan200.computercraft.gametest
 
 import dan200.computercraft.api.detail.BasicItemDetailProvider
-import dan200.computercraft.api.detail.DetailRegistries
+import dan200.computercraft.api.detail.VanillaDetailRegistries
 import dan200.computercraft.api.lua.ObjectArguments
 import dan200.computercraft.core.apis.PeripheralAPI
 import dan200.computercraft.gametest.api.*
@@ -172,7 +172,7 @@ class Turtle_Test {
     fun Item_detail_provider(helper: GameTestHelper) = helper.sequence {
         // Register a dummy provider for printout items
         thenExecute {
-            DetailRegistries.ITEM_STACK.addProvider(
+            VanillaDetailRegistries.ITEM_STACK.addProvider(
                 object :
                     BasicItemDetailProvider("printout", ItemPrintout::class.java) {
                     override fun provideDetails(data: MutableMap, stack: ItemStack, item: ItemPrintout) {
diff --git a/src/testMod/kotlin/dan200/computercraft/gametest/api/TestExtensions.kt b/src/testMod/kotlin/dan200/computercraft/gametest/api/TestExtensions.kt
index e492f8fe0..8766e2ce1 100644
--- a/src/testMod/kotlin/dan200/computercraft/gametest/api/TestExtensions.kt
+++ b/src/testMod/kotlin/dan200/computercraft/gametest/api/TestExtensions.kt
@@ -174,7 +174,7 @@ fun GameTestHelper.assertContainerExactly(pos: BlockPos, items: List)
     }
 }
 
-private fun getName(type: BlockEntityType<*>): ResourceLocation = ForgeRegistries.BLOCK_ENTITIES.getKey(type)!!
+private fun getName(type: BlockEntityType<*>): ResourceLocation = ForgeRegistries.BLOCK_ENTITY_TYPES.getKey(type)!!
 
 /**
  * Get a [BlockEntity] of a specific type.