1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-02-15 02:20:05 +00:00

Rewrite turtle upgrade registration to be more data driven (#967)

The feature nobody asked for, but we're getting anyway.

Old way to register a turtle/pocket computer upgrade:

    ComputerCraftAPI.registerTurtleUpgrade(new MyUpgrade(new ResourceLocation("my_mod", "my_upgrade")));

New way to register a turtle/pocket computer upgrade:

First, define a serialiser for your turtle upgrade type:

    static final DeferredRegister<TurtleUpgradeSerialiser<?>> SERIALISERS = DeferredRegister.create( TurtleUpgradeSerialiser.TYPE, "my_mod" );
    public static final RegistryObject<TurtleUpgradeSerialiser<MyUpgrade>> MY_UPGRADE =
        SERIALISERS.register( "my_upgrade", () -> TurtleUpgradeSerialiser.simple( MyUpgrade::new ) );
    SERIALISERS.register(bus); // Call in your mod constructor.

Now either create a JSON string or use a data generator to register your upgrades:

    class TurtleDataGenerator extends TurtleUpgradeDataProvider {
        @Override
        protected void addUpgrades( @Nonnull Consumer<Upgrade<TurtleUpgradeSerialiser<?>>> addUpgrade )
            simple(new ResourceLocation("my_mod", my_upgrade"), MY_UPGRADE.get()).add(addUpgrade);
        }
    }

See much better! In all seriousness, this does offer some benefits,
namely that it's now possible to overwrite or create upgrades via
datapacks.

Actual changes:
 - Remove ComputerCraftAPI.register{Turtle,Pocket}Upgrade functions.

 - Instead add {Turtle,Pocket}UpgradeSerialiser classes, which are used
   to load upgrades from JSON files in datapacks, and then read/write
   them to network packets (much like recipe serialisers).

 - The upgrade registries now subscribe to datapack reload events. They
   find all JSON files in the
   data/$mod_id/computercraft/{turtle,pocket}_upgrades directories,
   parse them, and then register them as upgrades.

   Once datapacks have fully reloaded, these upgrades are then sent over
   the network to the client.

 - Add data generators for turtle and pocket computer upgrades, to make
   the creation of JSON files a bit easier.

 - Port all of CC:T's upgrades over to use the new system.
This commit is contained in:
Jonathan Coates 2021-11-26 23:36:02 +00:00 committed by GitHub
parent a4c5ecf8df
commit 7b7527ec80
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
72 changed files with 1548 additions and 834 deletions

View File

@ -0,0 +1,4 @@
{
"type": "computercraft:speaker",
"item": "computercraft:speaker"
}

View File

@ -0,0 +1,4 @@
{
"type": "computercraft:wireless_modem_advanced",
"item": "computercraft:wireless_modem_advanced"
}

View File

@ -0,0 +1,4 @@
{
"type": "computercraft:wireless_modem_normal",
"item": "computercraft:wireless_modem_normal"
}

View File

@ -0,0 +1,4 @@
{
"type": "computercraft:speaker",
"item": "computercraft:speaker"
}

View File

@ -0,0 +1,4 @@
{
"type": "computercraft:wireless_modem_advanced",
"item": "computercraft:wireless_modem_advanced"
}

View File

@ -0,0 +1,4 @@
{
"type": "computercraft:wireless_modem_normal",
"item": "computercraft:wireless_modem_normal"
}

View File

@ -0,0 +1,4 @@
{
"type": "computercraft:workbench",
"item": "minecraft:crafting_table"
}

View File

@ -0,0 +1,5 @@
{
"type": "computercraft:tool",
"item": "minecraft:diamond_axe",
"damageMultiplier": 6.0
}

View File

@ -0,0 +1,5 @@
{
"type": "computercraft:tool",
"item": "minecraft:diamond_hoe",
"breakable": "computercraft:turtle_hoe_harvestable"
}

View File

@ -0,0 +1,4 @@
{
"type": "computercraft:tool",
"item": "minecraft:diamond_pickaxe"
}

View File

@ -0,0 +1,5 @@
{
"type": "computercraft:tool",
"item": "minecraft:diamond_shovel",
"breakable": "computercraft:turtle_shovel_harvestable"
}

View File

@ -0,0 +1,6 @@
{
"type": "computercraft:tool",
"item": "minecraft:diamond_sword",
"damageMultiplier": 9.0,
"breakable": "computercraft:turtle_sword_harvestable"
}

View File

@ -12,9 +12,6 @@ import dan200.computercraft.shared.Registry;
import dan200.computercraft.shared.computer.core.ClientComputerRegistry;
import dan200.computercraft.shared.computer.core.ServerComputerRegistry;
import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer;
import dan200.computercraft.shared.pocket.peripherals.PocketModem;
import dan200.computercraft.shared.pocket.peripherals.PocketSpeaker;
import dan200.computercraft.shared.turtle.upgrades.*;
import net.minecraftforge.fml.common.Mod;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
@ -79,27 +76,6 @@ public final class ComputerCraft
public static int monitorWidth = 8;
public static int monitorHeight = 6;
public static final class TurtleUpgrades
{
public static TurtleModem wirelessModemNormal;
public static TurtleModem wirelessModemAdvanced;
public static TurtleSpeaker speaker;
public static TurtleCraftingTable craftingTable;
public static TurtleSword diamondSword;
public static TurtleShovel diamondShovel;
public static TurtleTool diamondPickaxe;
public static TurtleAxe diamondAxe;
public static TurtleHoe diamondHoe;
}
public static final class PocketUpgrades
{
public static PocketModem wirelessModemNormal;
public static PocketModem wirelessModemAdvanced;
public static PocketSpeaker speaker;
}
// Registries
public static final ClientComputerRegistry clientComputerRegistry = new ClientComputerRegistry();
public static final ServerComputerRegistry serverComputerRegistry = new ServerComputerRegistry();

View File

@ -15,14 +15,14 @@ import dan200.computercraft.api.network.IPacketNetwork;
import dan200.computercraft.api.network.wired.IWiredElement;
import dan200.computercraft.api.network.wired.IWiredNode;
import dan200.computercraft.api.peripheral.IPeripheralProvider;
import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.api.redstone.IBundledRedstoneProvider;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.core.apis.ApiFactories;
import dan200.computercraft.core.asm.GenericMethod;
import dan200.computercraft.core.filesystem.FileMount;
import dan200.computercraft.core.filesystem.ResourceMount;
import dan200.computercraft.shared.*;
import dan200.computercraft.shared.BundledRedstone;
import dan200.computercraft.shared.MediaProviders;
import dan200.computercraft.shared.Peripherals;
import dan200.computercraft.shared.peripheral.generic.GenericPeripheralProvider;
import dan200.computercraft.shared.peripheral.modem.wireless.WirelessNetwork;
import dan200.computercraft.shared.util.IDAssigner;
@ -125,12 +125,6 @@ public final class ComputerCraftAPIImpl implements IComputerCraftAPI
GenericPeripheralProvider.addCapability( capability );
}
@Override
public void registerTurtleUpgrade( @Nonnull ITurtleUpgrade upgrade )
{
TurtleUpgrades.register( upgrade );
}
@Override
public void registerBundledRedstoneProvider( @Nonnull IBundledRedstoneProvider provider )
{
@ -149,12 +143,6 @@ public final class ComputerCraftAPIImpl implements IComputerCraftAPI
MediaProviders.register( provider );
}
@Override
public void registerPocketUpgrade( @Nonnull IPocketUpgrade upgrade )
{
PocketUpgrades.register( upgrade );
}
@Nonnull
@Override
public IPacketNetwork getWirelessNetwork()

View File

@ -17,9 +17,7 @@ import dan200.computercraft.api.network.wired.IWiredNode;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.peripheral.IPeripheralProvider;
import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.api.redstone.IBundledRedstoneProvider;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.BlockGetter;
@ -38,19 +36,14 @@ import javax.annotation.Nullable;
*/
public final class ComputerCraftAPI
{
public static final String MOD_ID = "computercraft";
@Nonnull
public static String getInstalledVersion()
{
return getInstance().getInstalledVersion();
}
@Nonnull
@Deprecated
public static String getAPIVersion()
{
return getInstalledVersion();
}
/**
* Creates a numbered directory in a subfolder of the save directory for a given world, and returns that number.
*
@ -151,19 +144,6 @@ public final class ComputerCraftAPI
getInstance().registerGenericCapability( capability );
}
/**
* Registers a new turtle turtle for use in ComputerCraft. After calling this,
* users should be able to craft Turtles with your new turtle. It is recommended to call
* this during the load() method of your mod.
*
* @param upgrade The turtle upgrade to register.
* @see ITurtleUpgrade
*/
public static void registerTurtleUpgrade( @Nonnull ITurtleUpgrade upgrade )
{
getInstance().registerTurtleUpgrade( upgrade );
}
/**
* Registers a bundled redstone provider to provide bundled redstone output for blocks.
*
@ -201,11 +181,6 @@ public final class ComputerCraftAPI
getInstance().registerMediaProvider( provider );
}
public static void registerPocketUpgrade( @Nonnull IPocketUpgrade upgrade )
{
getInstance().registerPocketUpgrade( upgrade );
}
/**
* Attempt to get the game-wide wireless network.
*
@ -286,16 +261,12 @@ public final class ComputerCraftAPI
void registerGenericCapability( @Nonnull Capability<?> capability );
void registerTurtleUpgrade( @Nonnull ITurtleUpgrade upgrade );
void registerBundledRedstoneProvider( @Nonnull IBundledRedstoneProvider provider );
int getBundledRedstoneOutput( @Nonnull Level world, @Nonnull BlockPos pos, @Nonnull Direction side );
void registerMediaProvider( @Nonnull IMediaProvider provider );
void registerPocketUpgrade( @Nonnull IPocketUpgrade upgrade );
@Nonnull
IPacketNetwork getWirelessNetwork();

View File

@ -1,9 +1,9 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. 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.shared;
package dan200.computercraft.api;
import dan200.computercraft.ComputerCraft;
import net.minecraft.resources.ResourceLocation;
@ -13,6 +13,9 @@ import net.minecraft.tags.Tag;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
/**
* Tags provided by ComputerCraft.
*/
public class ComputerCraftTags
{
public static class Items
@ -35,9 +38,24 @@ public class ComputerCraftTags
public static final Tag.Named<Block> WIRED_MODEM = make( "wired_modem" );
public static final Tag.Named<Block> MONITOR = make( "monitor" );
/**
* Blocks which can be broken by any turtle tool.
*/
public static final Tag.Named<Block> TURTLE_ALWAYS_BREAKABLE = make( "turtle_always_breakable" );
/**
* Blocks which can be broken by the default shovel tool.
*/
public static final Tag.Named<Block> TURTLE_SHOVEL_BREAKABLE = make( "turtle_shovel_harvestable" );
/**
* Blocks which can be broken with the default sword tool.
*/
public static final Tag.Named<Block> TURTLE_SWORD_BREAKABLE = make( "turtle_sword_harvestable" );
/**
* Blocks which can be broken with the default hoe tool.
*/
public static final Tag.Named<Block> TURTLE_HOE_BREAKABLE = make( "turtle_hoe_harvestable" );
private static Tag.Named<Block> make( String name )

View File

@ -5,15 +5,11 @@
*/
package dan200.computercraft.api.pocket;
import net.minecraft.Util;
import dan200.computercraft.api.upgrades.IUpgradeBase;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.ItemLike;
import net.minecraftforge.common.util.NonNullSupplier;
import javax.annotation.Nonnull;
import java.util.function.Supplier;
/**
* A base class for {@link IPocketUpgrade}s.
@ -24,48 +20,18 @@ public abstract class AbstractPocketUpgrade implements IPocketUpgrade
{
private final ResourceLocation id;
private final String adjective;
private final NonNullSupplier<ItemStack> stack;
private final ItemStack stack;
protected AbstractPocketUpgrade( ResourceLocation id, String adjective, NonNullSupplier<ItemStack> stack )
protected AbstractPocketUpgrade( ResourceLocation id, String adjective, ItemStack stack )
{
this.id = id;
this.adjective = adjective;
this.stack = stack;
}
protected AbstractPocketUpgrade( ResourceLocation id, NonNullSupplier<ItemStack> item )
{
this( id, Util.makeDescriptionId( "upgrade", id ) + ".adjective", item );
}
protected AbstractPocketUpgrade( ResourceLocation id, String adjective, ItemStack stack )
{
this( id, adjective, () -> stack );
}
protected AbstractPocketUpgrade( ResourceLocation id, ItemStack stack )
{
this( id, () -> stack );
}
protected AbstractPocketUpgrade( ResourceLocation id, String adjective, ItemLike item )
{
this( id, adjective, new CachedStack( () -> item ) );
}
protected AbstractPocketUpgrade( ResourceLocation id, ItemLike item )
{
this( id, new CachedStack( () -> item ) );
}
protected AbstractPocketUpgrade( ResourceLocation id, String adjective, Supplier<? extends ItemLike> item )
{
this( id, adjective, new CachedStack( item ) );
}
protected AbstractPocketUpgrade( ResourceLocation id, Supplier<? extends ItemLike> item )
{
this( id, new CachedStack( item ) );
this( id, IUpgradeBase.getDefaultAdjective( id ), stack );
}
@Nonnull
@ -86,32 +52,6 @@ public abstract class AbstractPocketUpgrade implements IPocketUpgrade
@Override
public final ItemStack getCraftingItem()
{
return stack.get();
}
/**
* Caches the construction of an item stack.
*
* @see dan200.computercraft.api.turtle.AbstractTurtleUpgrade For explanation of this class.
*/
private static final class CachedStack implements NonNullSupplier<ItemStack>
{
private final Supplier<? extends ItemLike> provider;
private Item item;
private ItemStack stack;
CachedStack( Supplier<? extends ItemLike> provider )
{
this.provider = provider;
}
@Nonnull
@Override
public ItemStack get()
{
Item item = provider.get().asItem();
if( item == this.item && stack != null ) return stack;
return stack = new ItemStack( this.item = item );
}
return stack;
}
}

View File

@ -5,18 +5,24 @@
*/
package dan200.computercraft.api.pocket;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.IUpgradeBase;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.upgrades.IUpgradeBase;
import net.minecraft.world.level.Level;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* Additional peripherals for pocket computers.
* A peripheral which can be equipped to the back side of a pocket computer.
*
* @see ComputerCraftAPI#registerPocketUpgrade(IPocketUpgrade)
* Pocket upgrades are defined in two stages. First, on creates a {@link IPocketUpgrade} subclass and corresponding
* {@link PocketUpgradeSerialiser} instance, which are then registered in a Forge registry.
*
* You then write a JSON file in your mod's {@literal data/} folder. This is then parsed when the world is loaded, and
* the upgrade registered internally. See the documentation in {@link PocketUpgradeSerialiser} for details on this process
* and where files should be located.
*
* @see PocketUpgradeSerialiser For how to register a pocket computer upgrade.
*/
public interface IPocketUpgrade extends IUpgradeBase
{

View File

@ -0,0 +1,30 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. 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.pocket;
import dan200.computercraft.api.upgrades.UpgradeDataProvider;
import net.minecraft.data.DataGenerator;
import net.minecraftforge.forge.event.lifecycle.GatherDataEvent;
import javax.annotation.Nonnull;
import java.util.function.Consumer;
/**
* A data provider to generate pocket computer upgrades.
*
* This should be subclassed and registered to a {@link DataGenerator}. Override the {@link #addUpgrades(Consumer)} function,
* construct each upgrade, and pass them off to the provided consumer to generate them.
*
* @see GatherDataEvent To register your data provider
* @see PocketUpgradeSerialiser
*/
public abstract class PocketUpgradeDataProvider extends UpgradeDataProvider<IPocketUpgrade, PocketUpgradeSerialiser<?>>
{
public PocketUpgradeDataProvider( @Nonnull DataGenerator generator )
{
super( generator, "Pocket Computer Upgrades", "computercraft/pocket_upgrades", PocketUpgradeSerialiser.TYPE );
}
}

View File

@ -0,0 +1,97 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. 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.pocket;
import dan200.computercraft.api.upgrades.IUpgradeBase;
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
import dan200.computercraft.internal.upgrades.SerialiserWithCraftingItem;
import dan200.computercraft.internal.upgrades.SimpleSerialiser;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.SimpleRecipeSerializer;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistryEntry;
import net.minecraftforge.registries.RegistryManager;
import javax.annotation.Nonnull;
import java.util.function.BiFunction;
import java.util.function.Function;
/**
* Reads a {@link IPocketUpgrade} from disk and reads/writes it to a network packet.
*
* This follows the same format as {@link dan200.computercraft.api.turtle.TurtleUpgradeSerialiser} - consult the
* documentation there for more information.
*
* @param <T> The type of pocket computer upgrade this is responsible for serialising.
* @see IPocketUpgrade
* @see PocketUpgradeDataProvider
*/
public interface PocketUpgradeSerialiser<T extends IPocketUpgrade> extends UpgradeSerialiser<T, PocketUpgradeSerialiser<?>>
{
/**
* A {@link Class} representing an abstract {@link PocketUpgradeSerialiser}. This is largely intended for use with
* Forge Registry methods/classes, such as {@link DeferredRegister} and {@link RegistryManager#getRegistry(Class)}.
*/
@SuppressWarnings( "unchecked" )
Class<PocketUpgradeSerialiser<?>> TYPE = (Class<PocketUpgradeSerialiser<?>>) (Class<?>) PocketUpgradeSerialiser.class;
/**
* A convenient base class to inherit to implement {@link PocketUpgradeSerialiser}.
*
* @param <T> The type of the upgrade created by this serialiser.
*/
abstract class Base<T extends IPocketUpgrade> extends ForgeRegistryEntry<PocketUpgradeSerialiser<?>> implements PocketUpgradeSerialiser<T>
{
}
/**
* Create an upgrade serialiser for a simple upgrade. This is similar to a {@link SimpleRecipeSerializer}, but for
* upgrades.
*
* If you might want to vary the item, it's suggested you use {@link #simpleWithCustomItem(BiFunction)} instead.
*
* @param factory Generate a new upgrade with a specific ID.
* @param <T> The type of the generated upgrade.
* @return The serialiser for this upgrade
*/
@Nonnull
static <T extends IPocketUpgrade> PocketUpgradeSerialiser<T> simple( @Nonnull Function<ResourceLocation, T> factory )
{
class Impl extends SimpleSerialiser<T, PocketUpgradeSerialiser<?>> implements PocketUpgradeSerialiser<T>
{
private Impl( Function<ResourceLocation, T> constructor )
{
super( constructor );
}
}
return new Impl( factory );
}
/**
* Create an upgrade serialiser for a simple upgrade whose crafting item can be specified.
*
* @param factory Generate a new upgrade with a specific ID and crafting item. The returned upgrade's
* {@link IUpgradeBase#getCraftingItem()} <strong>MUST</strong> equal the provided item.
* @param <T> The type of the generated upgrade.
* @return The serialiser for this upgrade.
* @see #simple(Function) For upgrades whose crafting stack should not vary.
*/
@Nonnull
static <T extends IPocketUpgrade> PocketUpgradeSerialiser<T> simpleWithCustomItem( @Nonnull BiFunction<ResourceLocation, ItemStack, T> factory )
{
class Impl extends SerialiserWithCraftingItem<T, PocketUpgradeSerialiser<?>> implements PocketUpgradeSerialiser<T>
{
private Impl( BiFunction<ResourceLocation, ItemStack, T> factory )
{
super( factory );
}
}
return new Impl( factory );
}
}

View File

@ -5,15 +5,11 @@
*/
package dan200.computercraft.api.turtle;
import net.minecraft.Util;
import dan200.computercraft.api.upgrades.IUpgradeBase;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.ItemLike;
import net.minecraftforge.common.util.NonNullSupplier;
import javax.annotation.Nonnull;
import java.util.function.Supplier;
/**
* A base class for {@link ITurtleUpgrade}s.
@ -25,9 +21,9 @@ public abstract class AbstractTurtleUpgrade implements ITurtleUpgrade
private final ResourceLocation id;
private final TurtleUpgradeType type;
private final String adjective;
private final NonNullSupplier<ItemStack> stack;
private final ItemStack stack;
protected AbstractTurtleUpgrade( ResourceLocation id, TurtleUpgradeType type, String adjective, NonNullSupplier<ItemStack> stack )
protected AbstractTurtleUpgrade( ResourceLocation id, TurtleUpgradeType type, String adjective, ItemStack stack )
{
this.id = id;
this.type = type;
@ -35,39 +31,9 @@ public abstract class AbstractTurtleUpgrade implements ITurtleUpgrade
this.stack = stack;
}
protected AbstractTurtleUpgrade( ResourceLocation id, TurtleUpgradeType type, NonNullSupplier<ItemStack> stack )
{
this( id, type, Util.makeDescriptionId( "upgrade", id ) + ".adjective", stack );
}
protected AbstractTurtleUpgrade( ResourceLocation id, TurtleUpgradeType type, String adjective, ItemStack stack )
{
this( id, type, adjective, () -> stack );
}
protected AbstractTurtleUpgrade( ResourceLocation id, TurtleUpgradeType type, ItemStack stack )
{
this( id, type, () -> stack );
}
protected AbstractTurtleUpgrade( ResourceLocation id, TurtleUpgradeType type, String adjective, ItemLike item )
{
this( id, type, adjective, new CachedStack( () -> item ) );
}
protected AbstractTurtleUpgrade( ResourceLocation id, TurtleUpgradeType type, ItemLike item )
{
this( id, type, new CachedStack( () -> item ) );
}
protected AbstractTurtleUpgrade( ResourceLocation id, TurtleUpgradeType type, String adjective, Supplier<? extends ItemLike> item )
{
this( id, type, adjective, new CachedStack( item ) );
}
protected AbstractTurtleUpgrade( ResourceLocation id, TurtleUpgradeType type, Supplier<? extends ItemLike> item )
{
this( id, type, new CachedStack( item ) );
this( id, type, IUpgradeBase.getDefaultAdjective( id ), stack );
}
@Nonnull
@ -95,32 +61,6 @@ public abstract class AbstractTurtleUpgrade implements ITurtleUpgrade
@Override
public final ItemStack getCraftingItem()
{
return stack.get();
}
/**
* A supplier which converts an item into an item stack.
*
* Constructing item stacks is somewhat expensive due to attaching capabilities. We cache it if given a consistent item.
*/
private static final class CachedStack implements NonNullSupplier<ItemStack>
{
private final Supplier<? extends ItemLike> provider;
private Item item;
private ItemStack stack;
CachedStack( Supplier<? extends ItemLike> provider )
{
this.provider = provider;
}
@Nonnull
@Override
public ItemStack get()
{
Item item = provider.get().asItem();
if( item == this.item && stack != null ) return stack;
return stack = new ItemStack( this.item = item );
}
return stack;
}
}

View File

@ -5,10 +5,9 @@
*/
package dan200.computercraft.api.turtle;
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.IUpgradeBase;
import dan200.computercraft.api.client.TransformedModel;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.upgrades.IUpgradeBase;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.core.Direction;
import net.minecraft.world.item.ItemStack;
@ -21,10 +20,17 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* The primary interface for defining an update for Turtles. A turtle update
* can either be a new tool, or a new peripheral.
* The primary interface for defining an update for Turtles. A turtle update can either be a new tool, or a new
* peripheral.
*
* @see ComputerCraftAPI#registerTurtleUpgrade(ITurtleUpgrade)
* Turtle upgrades are defined in two stages. First, one creates a {@link ITurtleUpgrade} subclass and corresponding
* {@link TurtleUpgradeSerialiser} instance, which are then registered in a Forge registry.
*
* You then write a JSON file in your mod's {@literal data/} folder. This is then parsed when the world is loaded, and
* the upgrade registered internally. See the documentation in {@link TurtleUpgradeSerialiser} for details on this process
* and where files should be located.
*
* @see TurtleUpgradeSerialiser For how to register a turtle upgrade.
*/
public interface ITurtleUpgrade extends IUpgradeBase
{

View File

@ -0,0 +1,148 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. 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 dan200.computercraft.api.ComputerCraftTags;
import dan200.computercraft.api.upgrades.UpgradeDataProvider;
import net.minecraft.data.DataGenerator;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.Tag;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import net.minecraftforge.forge.event.lifecycle.GatherDataEvent;
import javax.annotation.Nonnull;
import java.util.function.Consumer;
/**
* A data provider to generate turtle upgrades.
*
* This should be subclassed and registered to a {@link DataGenerator}. Override the {@link #addUpgrades(Consumer)} function,
* construct each upgrade, and pass them off to the provided consumer to generate them.
*
* @see GatherDataEvent To register your data provider
* @see TurtleUpgradeSerialiser
*/
public abstract class TurtleUpgradeDataProvider extends UpgradeDataProvider<ITurtleUpgrade, TurtleUpgradeSerialiser<?>>
{
private static final ResourceLocation TOOL_ID = new ResourceLocation( ComputerCraftAPI.MOD_ID, "tool" );
public TurtleUpgradeDataProvider( DataGenerator generator )
{
super( generator, "Turtle Upgrades", "computercraft/turtle_upgrades", TurtleUpgradeSerialiser.TYPE );
}
/**
* Create a new turtle tool upgrade, such as a pickaxe or shovel.
*
* @param id The ID of this tool.
* @param item The item used for tool actions. Note, this doesn't inherit all properties of the tool, you may need
* to specify {@link ToolBuilder#damageMultiplier(float)} and {@link ToolBuilder#breakable(Tag.Named)}.
* @return A tool builder,
*/
@Nonnull
public final ToolBuilder tool( @Nonnull ResourceLocation id, @Nonnull Item item )
{
return new ToolBuilder( id, existingSerialiser( TOOL_ID ), item );
}
/**
* A builder for custom turtle tool upgrades.
*
* @see #tool(ResourceLocation, Item)
*/
public static class ToolBuilder
{
private final ResourceLocation id;
private final TurtleUpgradeSerialiser<?> serialiser;
private final Item toolItem;
private String adjective;
private Item craftingItem;
private Float damageMultiplier = null;
private Tag.Named<Block> breakable;
ToolBuilder( ResourceLocation id, TurtleUpgradeSerialiser<?> serialiser, Item toolItem )
{
this.id = id;
this.serialiser = serialiser;
this.toolItem = toolItem;
craftingItem = null;
}
/**
* Specify a custom adjective for this tool. By default this takes its adjective from the tool item.
*
* @param adjective The new adjective to use.
* @return The tool builder, for further use.
*/
@Nonnull
public ToolBuilder adjective( @Nonnull String adjective )
{
this.adjective = adjective;
return this;
}
/**
* Specify a custom item which is used to craft this upgrade. By default this is the same as the provided tool
* item, but you may wish to override it.
*
* @param craftingItem The item used to craft this upgrade.
* @return The tool builder, for further use.
*/
@Nonnull
public ToolBuilder craftingItem( @Nonnull Item craftingItem )
{
this.craftingItem = craftingItem;
return this;
}
/**
* The amount of damage a swing of this tool will do. This is multiplied by {@link Attributes#ATTACK_DAMAGE} to
* get the final damage.
*
* @param damageMultiplier The damage multiplier.
* @return The tool builder, for futher use.
*/
public ToolBuilder damageMultiplier( float damageMultiplier )
{
this.damageMultiplier = damageMultiplier;
return this;
}
/**
* Provide a list of breakable blocks. If not given, the tool can break all blocks. If given, only blocks
* in this tag, those in {@link ComputerCraftTags.Blocks#TURTLE_ALWAYS_BREAKABLE} and "insta-mine" ones can
* be broken.
*
* @param breakable The tag containing all blocks breakable by this item.
* @return The tool builder, for further use.
* @see ComputerCraftTags.Blocks
*/
public ToolBuilder breakable( @Nonnull Tag.Named<Block> breakable )
{
this.breakable = breakable;
return this;
}
/**
* Register this as an upgrade.
*
* @param add The callback given to {@link #addUpgrades(Consumer)}.
*/
public void add( @Nonnull Consumer<Upgrade<TurtleUpgradeSerialiser<?>>> add )
{
add.accept( new Upgrade<>( id, serialiser, s -> {
s.addProperty( "item", toolItem.getRegistryName().toString() );
if( adjective != null ) s.addProperty( "adjective", adjective );
if( craftingItem != null ) s.addProperty( "craftItem", craftingItem.getRegistryName().toString() );
if( damageMultiplier != null ) s.addProperty( "damageMultiplier", damageMultiplier );
if( breakable != null ) s.addProperty( "breakable", breakable.getName().toString() );
} ) );
}
}
}

View File

@ -0,0 +1,125 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. 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.upgrades.IUpgradeBase;
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
import dan200.computercraft.internal.upgrades.SerialiserWithCraftingItem;
import dan200.computercraft.internal.upgrades.SimpleSerialiser;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.item.crafting.SimpleRecipeSerializer;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistryEntry;
import net.minecraftforge.registries.IForgeRegistry;
import net.minecraftforge.registries.RegistryManager;
import javax.annotation.Nonnull;
import java.util.function.BiFunction;
import java.util.function.Function;
/**
* Reads a {@link ITurtleUpgrade} from disk and reads/writes it to a network packet.
*
* These should be registered in a {@link IForgeRegistry} while the game is loading, much like {@link RecipeSerializer}s.
* It is suggested you use a {@link DeferredRegister}.
*
* If your turtle upgrade doesn't have any associated configurable parameters (like most upgrades), you can use
* {@link #simple(Function)} or {@link #simpleWithCustomItem(BiFunction)} to create a basic upgrade serialiser.
*
* <h2>Example</h2>
* <pre>{@code
* static final DeferredRegister<TurtleUpgradeSerialiser<?>> SERIALISERS = DeferredRegister.create( TurtleUpgradeSerialiser.TYPE, "my_mod" );
*
* // Register a new upgrade serialiser called "my_upgrade".
* public static final RegistryObject<TurtleUpgradeSerialiser<MyUpgrade>> MY_UPGRADE =
* SERIALISERS.register( "my_upgrade", () -> TurtleUpgradeSerialiser.simple( MyUpgrade::new ) );
*
* // Then in your constructor
* SERIALISERS.register( bus );
* }</pre>
*
* We can then define a new upgrade using JSON by placing the following in
* {@literal data/<my_mod>/computercraft/turtle_upgrades/<my_upgrade_id>.json}}.
*
* <pre>{@code
* {
* "type": my_mod:my_upgrade",
* }
* }</pre>
*
* {@link TurtleUpgradeDataProvider} provides a data provider to aid with generating these JSON files.
*
* @param <T> The type of turtle upgrade this is responsible for serialising.
* @see ITurtleUpgrade
* @see TurtleUpgradeDataProvider
*/
public interface TurtleUpgradeSerialiser<T extends ITurtleUpgrade> extends UpgradeSerialiser<T, TurtleUpgradeSerialiser<?>>
{
/**
* A {@link Class} representing an abstract {@link TurtleUpgradeSerialiser}. This is largely intended for use with
* Forge Registry methods/classes, such as {@link DeferredRegister} and {@link RegistryManager#getRegistry(Class)}.
*/
@SuppressWarnings( "unchecked" )
Class<TurtleUpgradeSerialiser<?>> TYPE = (Class<TurtleUpgradeSerialiser<?>>) (Class<?>) TurtleUpgradeSerialiser.class;
/**
* A convenient base class to inherit to implement {@link TurtleUpgradeSerialiser}.
*
* @param <T> The type of the upgrade created by this serialiser.
*/
abstract class Base<T extends ITurtleUpgrade> extends ForgeRegistryEntry<TurtleUpgradeSerialiser<?>> implements TurtleUpgradeSerialiser<T>
{
}
/**
* Create an upgrade serialiser for a simple upgrade. This is similar to a {@link SimpleRecipeSerializer}, but for
* upgrades.
*
* If you might want to vary the item, it's suggested you use {@link #simpleWithCustomItem(BiFunction)} instead.
*
* @param factory Generate a new upgrade with a specific ID.
* @param <T> The type of the generated upgrade.
* @return The serialiser for this upgrade
*/
@Nonnull
static <T extends ITurtleUpgrade> TurtleUpgradeSerialiser<T> simple( @Nonnull Function<ResourceLocation, T> factory )
{
class Impl extends SimpleSerialiser<T, TurtleUpgradeSerialiser<?>> implements TurtleUpgradeSerialiser<T>
{
private Impl( Function<ResourceLocation, T> constructor )
{
super( constructor );
}
}
return new Impl( factory );
}
/**
* Create an upgrade serialiser for a simple upgrade whose crafting item can be specified.
*
* @param factory Generate a new upgrade with a specific ID and crafting item. The returned upgrade's
* {@link IUpgradeBase#getCraftingItem()} <strong>MUST</strong> equal the provided item.
* @param <T> The type of the generated upgrade.
* @return The serialiser for this upgrade.
* @see #simple(Function) For upgrades whose crafting stack should not vary.
*/
@Nonnull
static <T extends ITurtleUpgrade> TurtleUpgradeSerialiser<T> simpleWithCustomItem( @Nonnull BiFunction<ResourceLocation, ItemStack, T> factory )
{
class Impl extends SerialiserWithCraftingItem<T, TurtleUpgradeSerialiser<?>> implements TurtleUpgradeSerialiser<T>
{
private Impl( BiFunction<ResourceLocation, ItemStack, T> factory )
{
super( factory );
}
}
return new Impl( factory );
}
}

View File

@ -3,10 +3,11 @@
* Copyright Daniel Ratcliffe, 2011-2021. 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;
package dan200.computercraft.api.upgrades;
import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import net.minecraft.Util;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
@ -83,4 +84,18 @@ public interface IUpgradeBase
if( craftingShareTag == null ) return shareTag.isEmpty();
return shareTag.equals( craftingShareTag );
}
/**
* Get a suitable default unlocalised adjective for an upgrade ID. This converts "modid:some_upgrade" to
* "upgrade.modid.some_upgrade.adjective".
*
* @param id The upgrade ID.
* @return The generated adjective.
* @see #getUnlocalisedAdjective()
*/
@Nonnull
static String getDefaultAdjective( @Nonnull ResourceLocation id )
{
return Util.makeDescriptionId( "upgrade", id ) + ".adjective";
}
}

View File

@ -0,0 +1,192 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. 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.upgrades;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
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.data.DataGenerator;
import net.minecraft.data.DataProvider;
import net.minecraft.data.HashCache;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraftforge.registries.RegistryManager;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.annotation.Nonnull;
import java.io.IOException;
import java.nio.file.Path;
import java.util.*;
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.
*
* @param <T> The base class of upgrades.
* @param <R> The upgrade serialiser to register for.
*/
public abstract class UpgradeDataProvider<T extends IUpgradeBase, R extends UpgradeSerialiser<?, R>> implements DataProvider
{
private static final Logger LOGGER = LogManager.getLogger();
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create();
private final DataGenerator generator;
private final String name;
private final String folder;
private final Class<R> klass;
private List<T> upgrades;
protected UpgradeDataProvider( @Nonnull DataGenerator generator, @Nonnull String name, @Nonnull String folder, @Nonnull Class<R> klass )
{
this.generator = generator;
this.name = name;
this.folder = folder;
this.klass = klass;
}
/**
* Register an upgrade using a "simple" serialiser (e.g. {@link TurtleUpgradeSerialiser#simple(Function)}).
*
* @param id The ID of the upgrade to create.
* @param serialiser The simple serialiser.
* @return The constructed upgrade, ready to be passed off to {@link #addUpgrades(Consumer)}'s consumer.
*/
@Nonnull
public final Upgrade<R> simple( @Nonnull ResourceLocation id, @Nonnull R serialiser )
{
if( !(serialiser instanceof SimpleSerialiser) )
{
throw new IllegalStateException( serialiser + " must be a simple() seriaiser." );
}
return new Upgrade<>( id, serialiser, s -> {} );
}
/**
* Register an upgrade using a "simple" serialiser (e.g. {@link TurtleUpgradeSerialiser#simple(Function)}).
*
* @param id The ID of the upgrade to create.
* @param serialiser The simple serialiser.
* @param item The crafting upgrade for this item.
* @return The constructed upgrade, ready to be passed off to {@link #addUpgrades(Consumer)}'s consumer.
*/
@Nonnull
public final Upgrade<R> simpleWithCustomItem( @Nonnull ResourceLocation id, @Nonnull R serialiser, @Nonnull Item item )
{
if( !(serialiser instanceof SerialiserWithCraftingItem) )
{
throw new IllegalStateException( serialiser + " must be a simpleWithCustomItem() serialiser." );
}
return new Upgrade<>( id, serialiser, s ->
s.addProperty( "item", Objects.requireNonNull( item.getRegistryName(), "Item is not registered" ).toString() )
);
}
/**
* Add all turtle or pocket computer upgrades.
*
* <strong>Example usage:</strong>
* <pre>{@code
* protected void addUpgrades(@Nonnull Consumer<Upgrade<TurtleUpgradeSerialiser<?>>> addUpgrade) {
* simple(new ResourceLocation("mymod", "speaker"), SPEAKER_SERIALISER.get()).add(addUpgrade);
* }
* }</pre>
*
* @param addUpgrade A callback used to register an upgrade.
*/
protected abstract void addUpgrades( @Nonnull Consumer<Upgrade<R>> addUpgrade );
@Override
public final void run( @Nonnull HashCache cache ) throws IOException
{
Path base = generator.getOutputFolder().resolve( "data" );
Set<ResourceLocation> seen = new HashSet<>();
List<T> upgrades = new ArrayList<>();
addUpgrades( upgrade -> {
if( !seen.add( upgrade.id() ) ) throw new IllegalStateException( "Duplicate upgrade " + upgrade.id() );
var json = new JsonObject();
json.addProperty( "type", Objects.requireNonNull( upgrade.serialiser().getRegistryName(), "Serialiser has not been registered" ).toString() );
upgrade.serialise().accept( json );
try
{
DataProvider.save( GSON, cache, json, base.resolve( upgrade.id().getNamespace() + "/" + folder + "/" + upgrade.id().getPath() + ".json" ) );
}
catch( IOException e )
{
LOGGER.error( "Failed to save {} {}", name, upgrade.id(), e );
}
try
{
@SuppressWarnings( "unchecked" ) var result = (T) upgrade.serialiser().fromJson( upgrade.id(), json );
upgrades.add( result );
}
catch( IllegalArgumentException | JsonParseException e )
{
LOGGER.error( "Failed to parse {} {}", name, upgrade.id(), e );
}
} );
this.upgrades = upgrades;
}
@Nonnull
@Override
public final String getName()
{
return name;
}
@Nonnull
public final R existingSerialiser( @Nonnull ResourceLocation id )
{
var result = RegistryManager.ACTIVE.getRegistry( klass ).getValue( id );
if( result == null ) throw new IllegalArgumentException( "No such serialiser " + klass );
return result;
}
@Nonnull
public List<T> getGeneratedUpgrades()
{
if( upgrades == null ) throw new IllegalStateException( "Upgrades have not beeen generated yet" );
return upgrades;
}
/**
* A constructed upgrade instance, produced {@link #addUpgrades(Consumer)}.
*
* @param id The ID for this upgrade.
* @param serialiser The serialiser which reads and writes this upgrade.
* @param serialise Augment the generated JSON with additional fields.
* @param <R> The type of upgrade serialiser.
*/
public static record Upgrade<R extends UpgradeSerialiser<?, R>>(
ResourceLocation id, R serialiser, Consumer<JsonObject> serialise
)
{
/**
* Convenience method for registering an upgrade.
*
* @param add The callback given to {@link #addUpgrades(Consumer)}
*/
public void add( @Nonnull Consumer<Upgrade<R>> add )
{
add.accept( this );
}
}
}

View File

@ -0,0 +1,59 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. 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.upgrades;
import com.google.gson.JsonObject;
import dan200.computercraft.api.pocket.PocketUpgradeSerialiser;
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.registries.IForgeRegistryEntry;
import javax.annotation.Nonnull;
/**
* Base interface for upgrade serialisers. This should generally not be implemented directly, instead implementing one
* of {@link TurtleUpgradeSerialiser} or {@link PocketUpgradeSerialiser}.
*
* However, it may sometimes be useful to implement this if you have some shared logic between upgrade types.
*
* @param <R> The serialiser for this upgrade category, either {@code TurtleUpgradeSerialiser<?>} or {@code PocketUpgradeSerialiser<?>}.
* @param <T> The upgrade that this class can serialise and deserialise.
* @see TurtleUpgradeSerialiser
* @see PocketUpgradeSerialiser
*/
public interface UpgradeSerialiser<T extends IUpgradeBase, R extends UpgradeSerialiser<?, R>> extends IForgeRegistryEntry<R>
{
/**
* Read this upgrade from a JSON file in a datapack.
*
* @param id The ID of this upgrade.
* @param object The JSON object to load this upgrade from.
* @return The constructed upgrade, with a {@link IUpgradeBase#getUpgradeID()} equal to {@code id}.
* @see net.minecraft.util.GsonHelper For additional JSON helper methods.
*/
@Nonnull
T fromJson( @Nonnull ResourceLocation id, @Nonnull JsonObject object );
/**
* Read this upgrade from a network packet, sent from the server.
*
* @param id The ID of this upgrade.
* @param buffer The buffer object to read this upgrade from.
* @return The constructed upgrade, with a {@link IUpgradeBase#getUpgradeID()} equal to {@code id}.
*/
@Nonnull
T fromNetwork( @Nonnull ResourceLocation id, @Nonnull FriendlyByteBuf buffer );
/**
* Write this upgrade to a network packet, to be sent to the client.
*
* @param buffer The buffer object to write this upgrade to
* @param upgrade The upgrade to write.
*/
void toNetwork( @Nonnull FriendlyByteBuf buffer, @Nonnull T upgrade );
}

View File

@ -14,7 +14,7 @@ import net.minecraftforge.common.data.ExistingFileHelper;
import java.util.function.BiFunction;
import java.util.function.Consumer;
public class BasicCustomLoader<T extends ModelBuilder<T>> extends CustomLoaderBuilder<T>
class BasicCustomLoader<T extends ModelBuilder<T>> extends CustomLoaderBuilder<T>
{
private final Consumer<JsonObject> extra;

View File

@ -25,13 +25,13 @@ import net.minecraftforge.registries.IForgeRegistryEntry;
import javax.annotation.Nonnull;
import java.util.Objects;
public class BlockModelProvider extends BlockStateProvider
class BlockModelProvider extends BlockStateProvider
{
private ModelFile monitorBase;
private ModelFile turtleBase;
private ModelFile modemBase;
public BlockModelProvider( DataGenerator generator, ExistingFileHelper existingFileHelper )
BlockModelProvider( DataGenerator generator, ExistingFileHelper existingFileHelper )
{
super( generator, ComputerCraft.MOD_ID, existingFileHelper );
}

View File

@ -13,11 +13,11 @@ import net.minecraft.tags.BlockTags;
import net.minecraft.world.level.block.Blocks;
import net.minecraftforge.common.data.ExistingFileHelper;
import static dan200.computercraft.shared.ComputerCraftTags.Blocks.*;
import static dan200.computercraft.api.ComputerCraftTags.Blocks.*;
public class BlockTagsGenerator extends BlockTagsProvider
class BlockTagsGenerator extends BlockTagsProvider
{
public BlockTagsGenerator( DataGenerator generator, ExistingFileHelper helper )
BlockTagsGenerator( DataGenerator generator, ExistingFileHelper helper )
{
super( generator, ComputerCraft.MOD_ID, helper );
}

View File

@ -23,7 +23,12 @@ public class Generators
DataGenerator generator = event.getGenerator();
ExistingFileHelper existingFiles = event.getExistingFileHelper();
generator.addProvider( new RecipeGenerator( generator ) );
var turtleUpgrades = new TurtleUpgradeGenerator( generator );
var pocketUpgrades = new PocketUpgradeGenerator( generator );
generator.addProvider( turtleUpgrades );
generator.addProvider( pocketUpgrades );
generator.addProvider( new RecipeGenerator( generator, turtleUpgrades, pocketUpgrades ) );
generator.addProvider( new LootTableGenerator( generator ) );
generator.addProvider( new BlockModelProvider( generator, existingFiles ) );

View File

@ -6,18 +6,18 @@
package dan200.computercraft.data;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.ComputerCraftTags.Blocks;
import dan200.computercraft.api.ComputerCraftTags.Blocks;
import dan200.computercraft.shared.Registry;
import net.minecraft.data.DataGenerator;
import net.minecraft.data.tags.ItemTagsProvider;
import net.minecraft.tags.ItemTags;
import net.minecraftforge.common.data.ExistingFileHelper;
import static dan200.computercraft.shared.ComputerCraftTags.Items.*;
import static dan200.computercraft.api.ComputerCraftTags.Items.*;
public class ItemTagsGenerator extends ItemTagsProvider
class ItemTagsGenerator extends ItemTagsProvider
{
public ItemTagsGenerator( DataGenerator generator, BlockTagsGenerator blockTags, ExistingFileHelper helper )
ItemTagsGenerator( DataGenerator generator, BlockTagsGenerator blockTags, ExistingFileHelper helper )
{
super( generator, blockTags, ComputerCraft.MOD_ID, helper );
}

View File

@ -30,9 +30,9 @@ import net.minecraftforge.fmllegacy.RegistryObject;
import java.util.function.BiConsumer;
public class LootTableGenerator extends LootTableProvider
class LootTableGenerator extends LootTableProvider
{
public LootTableGenerator( DataGenerator generator )
LootTableGenerator( DataGenerator generator )
{
super( generator );
}

View File

@ -28,13 +28,13 @@ import java.util.function.BiConsumer;
/**
* An alternative to {@link net.minecraft.data.loot.LootTableProvider}, with a more flexible interface.
*/
public abstract class LootTableProvider implements DataProvider
abstract class LootTableProvider implements DataProvider
{
private static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
private final DataGenerator generator;
public LootTableProvider( DataGenerator generator )
LootTableProvider( DataGenerator generator )
{
this.generator = generator;
}

View File

@ -0,0 +1,40 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.data;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.pocket.PocketUpgradeDataProvider;
import dan200.computercraft.api.pocket.PocketUpgradeSerialiser;
import net.minecraft.data.DataGenerator;
import net.minecraft.resources.ResourceLocation;
import javax.annotation.Nonnull;
import java.util.function.Consumer;
import static dan200.computercraft.shared.Registry.ModItems;
import static dan200.computercraft.shared.Registry.ModPocketUpgradeSerialisers;
class PocketUpgradeGenerator extends PocketUpgradeDataProvider
{
PocketUpgradeGenerator( DataGenerator generator )
{
super( generator );
}
@Override
protected void addUpgrades( @Nonnull Consumer<Upgrade<PocketUpgradeSerialiser<?>>> addUpgrade )
{
addUpgrade.accept( simpleWithCustomItem( id( "speaker" ), ModPocketUpgradeSerialisers.SPEAKER.get(), ModItems.SPEAKER.get() ) );
simpleWithCustomItem( id( "wireless_modem_normal" ), ModPocketUpgradeSerialisers.WIRELESS_MODEM_NORMAL.get(), ModItems.WIRELESS_MODEM_NORMAL.get() ).add( addUpgrade );
simpleWithCustomItem( id( "wireless_modem_advanced" ), ModPocketUpgradeSerialisers.WIRELESS_MODEM_ADVANCED.get(), ModItems.WIRELESS_MODEM_ADVANCED.get() ).add( addUpgrade );
}
@Nonnull
private static ResourceLocation id( @Nonnull String id )
{
return new ResourceLocation( ComputerCraft.MOD_ID, id );
}
}

View File

@ -6,9 +6,9 @@
package dan200.computercraft.data;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.PocketUpgrades;
import dan200.computercraft.api.pocket.PocketUpgradeDataProvider;
import dan200.computercraft.api.turtle.TurtleUpgradeDataProvider;
import dan200.computercraft.shared.Registry;
import dan200.computercraft.shared.TurtleUpgrades;
import dan200.computercraft.shared.common.ColourableRecipe;
import dan200.computercraft.shared.common.IColouredItem;
import dan200.computercraft.shared.computer.core.ComputerFamily;
@ -39,14 +39,20 @@ import javax.annotation.Nonnull;
import java.util.Locale;
import java.util.function.Consumer;
import static dan200.computercraft.shared.ComputerCraftTags.Items.COMPUTER;
import static dan200.computercraft.shared.ComputerCraftTags.Items.WIRED_MODEM;
import static dan200.computercraft.api.ComputerCraftTags.Items.COMPUTER;
import static dan200.computercraft.api.ComputerCraftTags.Items.WIRED_MODEM;
public class RecipeGenerator extends RecipeProvider
class RecipeGenerator extends RecipeProvider
{
public RecipeGenerator( DataGenerator generator )
private final TurtleUpgradeDataProvider turtleUpgrades;
private final PocketUpgradeDataProvider pocketUpgrades;
RecipeGenerator( DataGenerator generator, TurtleUpgradeDataProvider turtleUpgrades, PocketUpgradeDataProvider pocketUpgrades )
{
super( generator );
this.turtleUpgrades = turtleUpgrades;
this.pocketUpgrades = pocketUpgrades;
}
@Override
@ -101,7 +107,8 @@ public class RecipeGenerator extends RecipeProvider
String nameId = family.name().toLowerCase( Locale.ROOT );
TurtleUpgrades.getVanillaUpgrades().forEach( upgrade -> {
for( var upgrade : turtleUpgrades.getGeneratedUpgrades() )
{
ItemStack result = TurtleItemFactory.create( -1, null, -1, family, null, upgrade, -1, null );
ShapedRecipeBuilder
.shaped( result.getItem() )
@ -117,7 +124,7 @@ public class RecipeGenerator extends RecipeProvider
nameId, upgrade.getUpgradeID().getNamespace(), upgrade.getUpgradeID().getPath()
) )
);
} );
}
}
}
@ -135,7 +142,8 @@ public class RecipeGenerator extends RecipeProvider
String nameId = family.name().toLowerCase( Locale.ROOT );
PocketUpgrades.getVanillaUpgrades().forEach( upgrade -> {
for( var upgrade : pocketUpgrades.getGeneratedUpgrades() )
{
ItemStack result = PocketComputerItemFactory.create( -1, null, -1, family, upgrade );
ShapedRecipeBuilder
.shaped( result.getItem() )
@ -152,7 +160,7 @@ public class RecipeGenerator extends RecipeProvider
nameId, upgrade.getUpgradeID().getNamespace(), upgrade.getUpgradeID().getPath()
) )
);
} );
}
}
}

View File

@ -19,7 +19,7 @@ import java.util.function.Consumer;
/**
* Adapter for recipes which overrides the serializer and adds custom item NBT.
*/
public final class RecipeWrapper implements FinishedRecipe
final class RecipeWrapper implements FinishedRecipe
{
private final FinishedRecipe recipe;
private final CompoundTag resultData;

View File

@ -0,0 +1,56 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.data;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.ComputerCraftTags.Blocks;
import dan200.computercraft.api.turtle.TurtleUpgradeDataProvider;
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
import net.minecraft.data.DataGenerator;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Items;
import javax.annotation.Nonnull;
import java.util.function.Consumer;
import static dan200.computercraft.shared.Registry.ModItems;
import static dan200.computercraft.shared.Registry.ModTurtleSerialisers;
class TurtleUpgradeGenerator extends TurtleUpgradeDataProvider
{
TurtleUpgradeGenerator( DataGenerator generator )
{
super( generator );
}
@Override
protected void addUpgrades( @Nonnull Consumer<Upgrade<TurtleUpgradeSerialiser<?>>> addUpgrade )
{
simpleWithCustomItem( id( "speaker" ), ModTurtleSerialisers.SPEAKER.get(), ModItems.SPEAKER.get() ).add( addUpgrade );
simpleWithCustomItem( vanilla( "crafting_table" ), ModTurtleSerialisers.WORKBENCH.get(), Items.CRAFTING_TABLE ).add( addUpgrade );
simpleWithCustomItem( id( "wireless_modem_normal" ), ModTurtleSerialisers.WIRELESS_MODEM_NORMAL.get(), ModItems.WIRELESS_MODEM_NORMAL.get() ).add( addUpgrade );
simpleWithCustomItem( id( "wireless_modem_advanced" ), ModTurtleSerialisers.WIRELESS_MODEM_ADVANCED.get(), ModItems.WIRELESS_MODEM_ADVANCED.get() ).add( addUpgrade );
tool( vanilla( "diamond_axe" ), Items.DIAMOND_AXE ).damageMultiplier( 6.0f ).add( addUpgrade );
tool( vanilla( "diamond_pickaxe" ), Items.DIAMOND_PICKAXE ).add( addUpgrade );
tool( vanilla( "diamond_hoe" ), Items.DIAMOND_HOE ).breakable( Blocks.TURTLE_HOE_BREAKABLE ).add( addUpgrade );
tool( vanilla( "diamond_shovel" ), Items.DIAMOND_SHOVEL ).breakable( Blocks.TURTLE_SHOVEL_BREAKABLE ).add( addUpgrade );
tool( vanilla( "diamond_sword" ), Items.DIAMOND_SWORD ).breakable( Blocks.TURTLE_SWORD_BREAKABLE ).damageMultiplier( 9.0f ).add( addUpgrade );
}
@Nonnull
private static ResourceLocation id( @Nonnull String id )
{
return new ResourceLocation( ComputerCraft.MOD_ID, id );
}
@Nonnull
private static ResourceLocation vanilla( @Nonnull String id )
{
// Naughty, please don't do this. Mostly here for some semblance of backwards compatibility.
return new ResourceLocation( "minecraft", id );
}
}

View File

@ -0,0 +1,58 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.internal.upgrades;
import com.google.gson.JsonObject;
import dan200.computercraft.api.upgrades.IUpgradeBase;
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.GsonHelper;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.registries.ForgeRegistryEntry;
import javax.annotation.Nonnull;
import java.util.function.BiFunction;
/**
* Simple serialiser which returns a constant upgrade with a custom crafting item.
*
* Do <strong>NOT</strong> directly reference this class. It exists for internal use by the API.
*
* @param <R> The serialiser for this upgrade category, either {@code TurtleUpgradeSerialiser<?>} or {@code PocketUpgradeSerialiser<?>}.
* @param <T> The upgrade that this class can serialise and deserialise.
*/
public abstract class SerialiserWithCraftingItem<T extends IUpgradeBase, R extends UpgradeSerialiser<?, R>> extends ForgeRegistryEntry<R> implements UpgradeSerialiser<T, R>
{
private final BiFunction<ResourceLocation, ItemStack, T> factory;
protected SerialiserWithCraftingItem( BiFunction<ResourceLocation, ItemStack, T> factory )
{
this.factory = factory;
}
@Nonnull
@Override
public final T fromJson( @Nonnull ResourceLocation id, @Nonnull JsonObject object )
{
var item = GsonHelper.getAsItem( object, "item" );
return factory.apply( id, new ItemStack( item ) );
}
@Nonnull
@Override
public final T fromNetwork( @Nonnull ResourceLocation id, @Nonnull FriendlyByteBuf buffer )
{
ItemStack item = buffer.readItem();
return factory.apply( id, item );
}
@Override
public final void toNetwork( @Nonnull FriendlyByteBuf buffer, @Nonnull T upgrade )
{
buffer.writeItem( upgrade.getCraftingItem() );
}
}

View File

@ -0,0 +1,53 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.internal.upgrades;
import com.google.gson.JsonObject;
import dan200.computercraft.api.upgrades.IUpgradeBase;
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.registries.ForgeRegistryEntry;
import javax.annotation.Nonnull;
import java.util.function.Function;
/**
* Simple serialiser which returns a constant upgrade.
*
* Do <strong>NOT</strong> directly reference this class. It exists for internal use by the API.
*
* @param <R> The serialiser for this upgrade category, either {@code TurtleUpgradeSerialiser<?>} or {@code PocketUpgradeSerialiser<?>}.
* @param <T> The upgrade that this class can serialise and deserialise.
*/
public abstract class SimpleSerialiser<T extends IUpgradeBase, R extends UpgradeSerialiser<?, R>> extends ForgeRegistryEntry<R> implements UpgradeSerialiser<T, R>
{
private final Function<ResourceLocation, T> constructor;
public SimpleSerialiser( Function<ResourceLocation, T> constructor )
{
this.constructor = constructor;
}
@Nonnull
@Override
public final T fromJson( @Nonnull ResourceLocation id, @Nonnull JsonObject object )
{
return constructor.apply( id );
}
@Nonnull
@Override
public final T fromNetwork( @Nonnull ResourceLocation id, @Nonnull FriendlyByteBuf buffer )
{
return constructor.apply( id );
}
@Override
public final void toNetwork( @Nonnull FriendlyByteBuf buffer, @Nonnull T upgrade )
{
}
}

View File

@ -15,6 +15,8 @@ import dan200.computercraft.shared.command.CommandComputerCraft;
import dan200.computercraft.shared.computer.core.IComputer;
import dan200.computercraft.shared.computer.core.IContainerComputer;
import dan200.computercraft.shared.computer.core.ServerComputer;
import dan200.computercraft.shared.network.NetworkHandler;
import dan200.computercraft.shared.network.client.UpgradesLoadedMessage;
import dan200.computercraft.shared.peripheral.modem.wireless.WirelessNetwork;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
@ -24,10 +26,7 @@ import net.minecraft.world.level.storage.loot.BuiltInLootTables;
import net.minecraft.world.level.storage.loot.LootPool;
import net.minecraft.world.level.storage.loot.entries.LootTableReference;
import net.minecraft.world.level.storage.loot.providers.number.ConstantValue;
import net.minecraftforge.event.AddReloadListenerEvent;
import net.minecraftforge.event.LootTableLoadEvent;
import net.minecraftforge.event.RegisterCommandsEvent;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.*;
import net.minecraftforge.event.entity.player.PlayerContainerEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
@ -145,5 +144,21 @@ public final class CommonHooks
public static void onAddReloadListeners( AddReloadListenerEvent event )
{
event.addListener( ResourceMount.RELOAD_LISTENER );
event.addListener( TurtleUpgrades.instance() );
event.addListener( PocketUpgrades.instance() );
}
@SubscribeEvent
public static void onDatapackSync( OnDatapackSyncEvent event )
{
var packet = new UpgradesLoadedMessage();
if( event.getPlayer() == null )
{
NetworkHandler.sendToAllPlayers( packet );
}
else
{
NetworkHandler.sendToPlayer( event.getPlayer(), packet );
}
}
}

View File

@ -7,81 +7,27 @@ package dan200.computercraft.shared;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.pocket.IPocketUpgrade;
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenCustomHashMap;
import net.minecraft.Util;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.fml.ModContainer;
import net.minecraftforge.fml.ModLoadingContext;
import dan200.computercraft.api.pocket.PocketUpgradeSerialiser;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.*;
import java.util.stream.Stream;
public final class PocketUpgrades
{
private static final Map<String, IPocketUpgrade> upgrades = new HashMap<>();
private static final Map<IPocketUpgrade, String> upgradeOwners = new Object2ObjectLinkedOpenCustomHashMap<>( Util.identityStrategy() );
private static final UpgradeManager<PocketUpgradeSerialiser<?>, IPocketUpgrade> registry = new UpgradeManager<>(
"pocket computer upgrade", "computercraft/pocket_upgrades", PocketUpgradeSerialiser.TYPE
);
private PocketUpgrades() {}
public static synchronized void register( @Nonnull IPocketUpgrade upgrade )
public static UpgradeManager<PocketUpgradeSerialiser<?>, IPocketUpgrade> instance()
{
Objects.requireNonNull( upgrade, "upgrade cannot be null" );
String id = upgrade.getUpgradeID().toString();
IPocketUpgrade existing = upgrades.get( id );
if( existing != null )
{
throw new IllegalStateException( "Error registering '" + upgrade.getUnlocalisedAdjective() + " pocket computer'. UpgradeID '" + id + "' is already registered by '" + existing.getUnlocalisedAdjective() + " pocket computer'" );
}
upgrades.put( id, upgrade );
ModContainer mc = ModLoadingContext.get().getActiveContainer();
if( mc != null && mc.getModId() != null ) upgradeOwners.put( upgrade, mc.getModId() );
return registry;
}
public static IPocketUpgrade get( String id )
public static Stream<IPocketUpgrade> getVanillaUpgrades()
{
// Fix a typo in the advanced modem upgrade's name. I'm sorry, I realise this is horrible.
if( id.equals( "computercraft:advanved_modem" ) ) id = "computercraft:advanced_modem";
return upgrades.get( id );
}
public static IPocketUpgrade get( @Nonnull ItemStack stack )
{
if( stack.isEmpty() ) return null;
for( IPocketUpgrade upgrade : upgrades.values() )
{
ItemStack craftingStack = upgrade.getCraftingItem();
if( !craftingStack.isEmpty() && craftingStack.getItem() == stack.getItem() && upgrade.isItemSuitable( stack ) )
{
return upgrade;
}
}
return null;
}
@Nullable
public static String getOwner( IPocketUpgrade upgrade )
{
return upgradeOwners.get( upgrade );
}
public static Iterable<IPocketUpgrade> getVanillaUpgrades()
{
List<IPocketUpgrade> vanilla = new ArrayList<>();
vanilla.add( ComputerCraft.PocketUpgrades.wirelessModemNormal );
vanilla.add( ComputerCraft.PocketUpgrades.wirelessModemAdvanced );
vanilla.add( ComputerCraft.PocketUpgrades.speaker );
return vanilla;
}
public static Iterable<IPocketUpgrade> getUpgrades()
{
return Collections.unmodifiableCollection( upgrades.values() );
return instance().getUpgradeWrappers().values().stream()
.filter( x -> x.modId().equals( ComputerCraft.MOD_ID ) )
.map( UpgradeManager.UpgradeWrapper::upgrade );
}
}

View File

@ -10,6 +10,8 @@ import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.media.IMedia;
import dan200.computercraft.api.network.wired.IWiredElement;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.pocket.PocketUpgradeSerialiser;
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
import dan200.computercraft.shared.command.arguments.ArgumentSerializers;
import dan200.computercraft.shared.common.ColourableRecipe;
import dan200.computercraft.shared.common.ContainerHeldItem;
@ -74,7 +76,10 @@ import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.MobCategory;
import net.minecraft.world.inventory.MenuType;
import net.minecraft.world.item.*;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.CreativeModeTab;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.RecordItem;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity;
@ -95,6 +100,7 @@ import net.minecraftforge.fmllegacy.RegistryObject;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.RegistryBuilder;
import java.util.function.BiFunction;
@ -251,49 +257,32 @@ public final class Registry
() -> new ItemBlockCable.WiredModem( ModBlocks.CABLE.get(), properties() ) );
}
@SubscribeEvent
public static void registerItems( RegistryEvent.Register<Item> event )
public static class ModTurtleSerialisers
{
registerTurtleUpgrades();
registerPocketUpgrades();
static final DeferredRegister<TurtleUpgradeSerialiser<?>> SERIALISERS = DeferredRegister.create( TurtleUpgradeSerialiser.TYPE, ComputerCraft.MOD_ID );
public static final RegistryObject<TurtleUpgradeSerialiser<TurtleSpeaker>> SPEAKER =
SERIALISERS.register( "speaker", () -> TurtleUpgradeSerialiser.simpleWithCustomItem( TurtleSpeaker::new ) );
public static final RegistryObject<TurtleUpgradeSerialiser<TurtleCraftingTable>> WORKBENCH =
SERIALISERS.register( "workbench", () -> TurtleUpgradeSerialiser.simpleWithCustomItem( TurtleCraftingTable::new ) );
public static final RegistryObject<TurtleUpgradeSerialiser<TurtleModem>> WIRELESS_MODEM_NORMAL =
SERIALISERS.register( "wireless_modem_normal", () -> TurtleUpgradeSerialiser.simpleWithCustomItem( ( id, item ) -> new TurtleModem( id, item, false ) ) );
public static final RegistryObject<TurtleUpgradeSerialiser<TurtleModem>> WIRELESS_MODEM_ADVANCED =
SERIALISERS.register( "wireless_modem_advanced", () -> TurtleUpgradeSerialiser.simpleWithCustomItem( ( id, item ) -> new TurtleModem( id, item, true ) ) );
public static final RegistryObject<TurtleUpgradeSerialiser<TurtleTool>> TOOL = SERIALISERS.register( "tool", () -> TurtleToolSerialiser.INSTANCE );
}
private static void registerTurtleUpgrades()
public static class ModPocketUpgradeSerialisers
{
// Upgrades
ComputerCraft.TurtleUpgrades.wirelessModemNormal = new TurtleModem( false, new ResourceLocation( ComputerCraft.MOD_ID, "wireless_modem_normal" ) );
ComputerCraftAPI.registerTurtleUpgrade( ComputerCraft.TurtleUpgrades.wirelessModemNormal );
static final DeferredRegister<PocketUpgradeSerialiser<?>> SERIALISERS = DeferredRegister.create( PocketUpgradeSerialiser.TYPE, ComputerCraft.MOD_ID );
ComputerCraft.TurtleUpgrades.wirelessModemAdvanced = new TurtleModem( true, new ResourceLocation( ComputerCraft.MOD_ID, "wireless_modem_advanced" ) );
ComputerCraftAPI.registerTurtleUpgrade( ComputerCraft.TurtleUpgrades.wirelessModemAdvanced );
ComputerCraft.TurtleUpgrades.speaker = new TurtleSpeaker( new ResourceLocation( ComputerCraft.MOD_ID, "speaker" ) );
ComputerCraftAPI.registerTurtleUpgrade( ComputerCraft.TurtleUpgrades.speaker );
ComputerCraft.TurtleUpgrades.craftingTable = new TurtleCraftingTable( new ResourceLocation( "minecraft", "crafting_table" ) );
ComputerCraftAPI.registerTurtleUpgrade( ComputerCraft.TurtleUpgrades.craftingTable );
ComputerCraft.TurtleUpgrades.diamondSword = new TurtleSword( new ResourceLocation( "minecraft", "diamond_sword" ), Items.DIAMOND_SWORD );
ComputerCraftAPI.registerTurtleUpgrade( ComputerCraft.TurtleUpgrades.diamondSword );
ComputerCraft.TurtleUpgrades.diamondShovel = new TurtleShovel( new ResourceLocation( "minecraft", "diamond_shovel" ), Items.DIAMOND_SHOVEL );
ComputerCraftAPI.registerTurtleUpgrade( ComputerCraft.TurtleUpgrades.diamondShovel );
ComputerCraft.TurtleUpgrades.diamondPickaxe = new TurtleTool( new ResourceLocation( "minecraft", "diamond_pickaxe" ), Items.DIAMOND_PICKAXE );
ComputerCraftAPI.registerTurtleUpgrade( ComputerCraft.TurtleUpgrades.diamondPickaxe );
ComputerCraft.TurtleUpgrades.diamondAxe = new TurtleAxe( new ResourceLocation( "minecraft", "diamond_axe" ), Items.DIAMOND_AXE );
ComputerCraftAPI.registerTurtleUpgrade( ComputerCraft.TurtleUpgrades.diamondAxe );
ComputerCraft.TurtleUpgrades.diamondHoe = new TurtleHoe( new ResourceLocation( "minecraft", "diamond_hoe" ), Items.DIAMOND_HOE );
ComputerCraftAPI.registerTurtleUpgrade( ComputerCraft.TurtleUpgrades.diamondHoe );
}
private static void registerPocketUpgrades()
{
ComputerCraftAPI.registerPocketUpgrade( ComputerCraft.PocketUpgrades.wirelessModemNormal = new PocketModem( false ) );
ComputerCraftAPI.registerPocketUpgrade( ComputerCraft.PocketUpgrades.wirelessModemAdvanced = new PocketModem( true ) );
ComputerCraftAPI.registerPocketUpgrade( ComputerCraft.PocketUpgrades.speaker = new PocketSpeaker() );
public static final RegistryObject<PocketUpgradeSerialiser<PocketSpeaker>> SPEAKER =
SERIALISERS.register( "speaker", () -> PocketUpgradeSerialiser.simpleWithCustomItem( PocketSpeaker::new ) );
public static final RegistryObject<PocketUpgradeSerialiser<PocketModem>> WIRELESS_MODEM_NORMAL =
SERIALISERS.register( "wireless_modem_normal", () -> PocketUpgradeSerialiser.simpleWithCustomItem( ( id, item ) -> new PocketModem( id, item, false ) ) );
public static final RegistryObject<PocketUpgradeSerialiser<PocketModem>> WIRELESS_MODEM_ADVANCED =
SERIALISERS.register( "wireless_modem_advanced", () -> PocketUpgradeSerialiser.simpleWithCustomItem( ( id, item ) -> new PocketModem( id, item, true ) ) );
}
public static class ModEntities
@ -337,6 +326,20 @@ public final class Registry
() -> ContainerData.toType( ViewComputerContainerData::new, ContainerViewComputer::new ) );
}
@SubscribeEvent
public static void registerRegistries( RegistryEvent.NewRegistry event )
{
new RegistryBuilder<TurtleUpgradeSerialiser<?>>()
.setName( new ResourceLocation( ComputerCraft.MOD_ID, "turtle_upgrade_serialiser" ) )
.setType( TurtleUpgradeSerialiser.TYPE )
.disableSaving().disableSync().create();
new RegistryBuilder<PocketUpgradeSerialiser<?>>()
.setName( new ResourceLocation( ComputerCraft.MOD_ID, "pocket_upgrade_serialiser" ) )
.setType( PocketUpgradeSerialiser.TYPE )
.disableSaving().disableSync().create();
}
@SubscribeEvent
public static void registerRecipeSerializers( RegistryEvent.Register<RecipeSerializer<?>> event )
{
@ -423,6 +426,8 @@ public final class Registry
ModBlocks.BLOCKS.register( bus );
ModBlockEntities.TILES.register( bus );
ModItems.ITEMS.register( bus );
ModTurtleSerialisers.SERIALISERS.register( bus );
ModPocketUpgradeSerialisers.SERIALISERS.register( bus );
ModEntities.ENTITIES.register( bus );
ModContainers.CONTAINERS.register( bus );
}

View File

@ -7,177 +7,27 @@ package dan200.computercraft.shared;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.shared.computer.core.ComputerFamily;
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenCustomHashMap;
import net.minecraft.Util;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.fml.ModContainer;
import net.minecraftforge.fml.ModLoadingContext;
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Stream;
public final class TurtleUpgrades
{
private static class Wrapper
{
final ITurtleUpgrade upgrade;
final String id;
final String modId;
boolean enabled;
Wrapper( ITurtleUpgrade upgrade )
{
this.upgrade = upgrade;
id = upgrade.getUpgradeID().toString();
ModContainer mc = ModLoadingContext.get().getActiveContainer();
modId = mc != null && mc.getModId() != null ? mc.getModId() : ComputerCraft.MOD_ID;
enabled = true;
}
}
private static ITurtleUpgrade[] vanilla;
private static final Map<String, ITurtleUpgrade> upgrades = new HashMap<>();
private static final Map<ITurtleUpgrade, Wrapper> wrappers = new Object2ObjectLinkedOpenCustomHashMap<>( Util.identityStrategy() );
private static boolean needsRebuild;
private static final UpgradeManager<TurtleUpgradeSerialiser<?>, ITurtleUpgrade> registry = new UpgradeManager<>(
"turtle upgrade", "computercraft/turtle_upgrades", TurtleUpgradeSerialiser.TYPE
);
private TurtleUpgrades() {}
public static void register( @Nonnull ITurtleUpgrade upgrade )
public static UpgradeManager<TurtleUpgradeSerialiser<?>, ITurtleUpgrade> instance()
{
Objects.requireNonNull( upgrade, "upgrade cannot be null" );
rebuild();
Wrapper wrapper = new Wrapper( upgrade );
String id = wrapper.id;
ITurtleUpgrade existing = upgrades.get( id );
if( existing != null )
{
throw new IllegalStateException( "Error registering '" + upgrade.getUnlocalisedAdjective() + " Turtle'. Upgrade ID '" + id + "' is already registered by '" + existing.getUnlocalisedAdjective() + " Turtle'" );
}
upgrades.put( id, upgrade );
wrappers.put( upgrade, wrapper );
}
@Nullable
public static ITurtleUpgrade get( String id )
{
rebuild();
return upgrades.get( id );
}
@Nullable
public static String getOwner( @Nonnull ITurtleUpgrade upgrade )
{
Wrapper wrapper = wrappers.get( upgrade );
return wrapper != null ? wrapper.modId : null;
}
public static ITurtleUpgrade get( @Nonnull ItemStack stack )
{
if( stack.isEmpty() ) return null;
for( Wrapper wrapper : wrappers.values() )
{
if( !wrapper.enabled ) continue;
ItemStack craftingStack = wrapper.upgrade.getCraftingItem();
if( !craftingStack.isEmpty() && craftingStack.getItem() == stack.getItem() && wrapper.upgrade.isItemSuitable( stack ) )
{
return wrapper.upgrade;
}
}
return null;
return registry;
}
public static Stream<ITurtleUpgrade> getVanillaUpgrades()
{
if( vanilla == null )
{
vanilla = new ITurtleUpgrade[] {
// ComputerCraft upgrades
ComputerCraft.TurtleUpgrades.wirelessModemNormal,
ComputerCraft.TurtleUpgrades.wirelessModemAdvanced,
ComputerCraft.TurtleUpgrades.speaker,
// Vanilla Minecraft upgrades
ComputerCraft.TurtleUpgrades.diamondPickaxe,
ComputerCraft.TurtleUpgrades.diamondAxe,
ComputerCraft.TurtleUpgrades.diamondSword,
ComputerCraft.TurtleUpgrades.diamondShovel,
ComputerCraft.TurtleUpgrades.diamondHoe,
ComputerCraft.TurtleUpgrades.craftingTable,
};
}
return Arrays.stream( vanilla ).filter( x -> x != null && wrappers.get( x ).enabled );
}
public static Stream<ITurtleUpgrade> getUpgrades()
{
return wrappers.values().stream().filter( x -> x.enabled ).map( x -> x.upgrade );
}
public static boolean suitableForFamily( ComputerFamily family, ITurtleUpgrade upgrade )
{
return true;
}
/**
* Rebuild the cache of turtle upgrades. This is done before querying the cache or registering new upgrades.
*/
private static void rebuild()
{
if( !needsRebuild ) return;
upgrades.clear();
for( Wrapper wrapper : wrappers.values() )
{
if( !wrapper.enabled ) continue;
ITurtleUpgrade existing = upgrades.get( wrapper.id );
if( existing != null )
{
ComputerCraft.log.error( "Error registering '" + wrapper.upgrade.getUnlocalisedAdjective() + " Turtle'." +
" Upgrade ID '" + wrapper.id + "' is already registered by '" + existing.getUnlocalisedAdjective() + " Turtle'" );
continue;
}
upgrades.put( wrapper.id, wrapper.upgrade );
}
needsRebuild = false;
}
public static void enable( ITurtleUpgrade upgrade )
{
Wrapper wrapper = wrappers.get( upgrade );
if( wrapper.enabled ) return;
wrapper.enabled = true;
needsRebuild = true;
}
public static void disable( ITurtleUpgrade upgrade )
{
Wrapper wrapper = wrappers.get( upgrade );
if( !wrapper.enabled ) return;
wrapper.enabled = false;
upgrades.remove( wrapper.id );
}
public static void remove( ITurtleUpgrade upgrade )
{
wrappers.remove( upgrade );
needsRebuild = true;
return instance().getUpgradeWrappers().values().stream()
.filter( x -> x.modId().equals( ComputerCraft.MOD_ID ) )
.map( UpgradeManager.UpgradeWrapper::upgrade );
}
}

View File

@ -0,0 +1,152 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared;
import com.google.gson.*;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.upgrades.IUpgradeBase;
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.resources.ResourceManager;
import net.minecraft.server.packs.resources.SimpleJsonResourceReloadListener;
import net.minecraft.util.GsonHelper;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.registries.RegistryManager;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;
/**
* Manages turtle and pocket computer upgrades.
*
* @param <R> The type of upgrade serialisers.
* @param <T> The type of upgrade.
* @see TurtleUpgrades
* @see PocketUpgrades
*/
public class UpgradeManager<R extends UpgradeSerialiser<? extends T, R>, T extends IUpgradeBase> extends SimpleJsonResourceReloadListener
{
private static final Logger LOGGER = LogManager.getLogger();
private static final Gson GSON = (new GsonBuilder()).setPrettyPrinting().disableHtmlEscaping().create();
public static record UpgradeWrapper<R extends UpgradeSerialiser<? extends T, R>, T extends IUpgradeBase>(
@Nonnull String id, @Nonnull T upgrade, @Nonnull R serialiser, @Nonnull String modId
) {}
private final String kind;
private final Class<R> klass;
private Map<String, UpgradeWrapper<R, T>> current = Collections.emptyMap();
private Map<T, UpgradeWrapper<R, T>> currentWrappers = Collections.emptyMap();
public UpgradeManager( @Nonnull String kind, @Nonnull String path, @Nonnull Class<R> klass )
{
super( GSON, path );
this.kind = kind;
this.klass = klass;
}
@Nullable
public T get( String id )
{
var wrapper = current.get( id );
return wrapper == null ? null : wrapper.upgrade();
}
@Nullable
public String getOwner( @Nonnull T upgrade )
{
var wrapper = currentWrappers.get( upgrade );
return wrapper != null ? wrapper.modId() : null;
}
@Nullable
public T get( @Nonnull ItemStack stack )
{
if( stack.isEmpty() ) return null;
for( var wrapper : current.values() )
{
var craftingStack = wrapper.upgrade().getCraftingItem();
if( !craftingStack.isEmpty() && craftingStack.getItem() == stack.getItem() && wrapper.upgrade().isItemSuitable( stack ) )
{
return wrapper.upgrade();
}
}
return null;
}
@Nonnull
public Collection<T> getUpgrades()
{
return currentWrappers.keySet();
}
@Nonnull
public Map<String, UpgradeWrapper<R, T>> getUpgradeWrappers()
{
return current;
}
@Override
protected void apply( @Nonnull Map<ResourceLocation, JsonElement> upgrades, @Nonnull ResourceManager manager, @Nonnull ProfilerFiller profiler )
{
Map<String, UpgradeWrapper<R, T>> newUpgrades = new HashMap<>();
for( var element : upgrades.entrySet() )
{
try
{
loadUpgrade( newUpgrades, element.getKey(), element.getValue() );
}
catch( IllegalArgumentException | JsonParseException e )
{
LOGGER.error( "Error loading {} {} from JSON file", kind, element.getKey(), e );
}
}
current = Collections.unmodifiableMap( newUpgrades );
currentWrappers = newUpgrades.values().stream().collect( Collectors.toUnmodifiableMap( UpgradeWrapper::upgrade, x -> x ) );
LOGGER.info( "Loaded {} {}s", current.size(), kind );
}
private void loadUpgrade( Map<String, UpgradeWrapper<R, T>> current, ResourceLocation id, JsonElement json )
{
var root = GsonHelper.convertToJsonObject( json, "top element" );
var serialiserId = new ResourceLocation( GsonHelper.getAsString( root, "type" ) );
var serialiser = RegistryManager.ACTIVE.getRegistry( klass ).getValue( serialiserId );
if( serialiser == null ) throw new JsonSyntaxException( "Unknown upgrade type '" + serialiserId + "'" );
// TODO: Can we track which mod this resource came from and use that instead? It's theoretically possible,
// but maybe not ideal for datapacks.
var modId = id.getNamespace();
if( modId.equals( "minecraft" ) || modId.equals( "" ) ) modId = ComputerCraft.MOD_ID;
var upgrade = serialiser.fromJson( id, root );
if( !upgrade.getUpgradeID().equals( id ) )
{
throw new IllegalArgumentException( "Upgrade " + id + " from " + serialiser + " was incorrectly given id " + upgrade.getUpgradeID() );
}
UpgradeWrapper<R, T> result = new UpgradeWrapper<>( id.toString(), upgrade, serialiser, modId );
current.put( result.id(), result );
}
public void loadFromNetwork( @Nonnull Map<String, UpgradeWrapper<R, T>> newUpgrades )
{
current = Collections.unmodifiableMap( newUpgrades );
currentWrappers = newUpgrades.values().stream().collect( Collectors.toUnmodifiableMap( UpgradeWrapper::upgrade, x -> x ) );
}
}

View File

@ -76,18 +76,21 @@ public class JEIComputerCraft implements IModPlugin
List<ItemStack> upgradeItems = new ArrayList<>();
for( ComputerFamily family : MAIN_FAMILIES )
{
TurtleUpgrades.getUpgrades()
.filter( x -> TurtleUpgrades.suitableForFamily( family, x ) )
.map( x -> TurtleItemFactory.create( -1, null, -1, family, null, x, 0, null ) )
.forEach( upgradeItems::add );
for( ITurtleUpgrade upgrade : TurtleUpgrades.instance().getUpgrades() )
{
upgradeItems.add( TurtleItemFactory.create( -1, null, -1, family, null, upgrade, 0, null ) );
}
for( IPocketUpgrade upgrade : PocketUpgrades.getUpgrades() )
for( IPocketUpgrade upgrade : PocketUpgrades.instance().getUpgrades() )
{
upgradeItems.add( PocketComputerItemFactory.create( -1, null, -1, family, upgrade ) );
}
}
runtime.getIngredientManager().addIngredientsAtRuntime( VanillaTypes.ITEM, upgradeItems );
if( !upgradeItems.isEmpty() )
{
runtime.getIngredientManager().addIngredientsAtRuntime( VanillaTypes.ITEM, upgradeItems );
}
// Hide all upgrade recipes
IRecipeCategory<?> category = registry.getRecipeCategory( VanillaRecipeCategoryUid.CRAFTING, false );

View File

@ -6,7 +6,7 @@
package dan200.computercraft.shared.integration.jei;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.IUpgradeBase;
import dan200.computercraft.api.upgrades.IUpgradeBase;
import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleSide;
@ -52,16 +52,17 @@ class RecipeResolver implements IRecipeManagerPlugin
if( initialised ) return;
initialised = true;
TurtleUpgrades.getUpgrades().forEach( upgrade -> {
for( ITurtleUpgrade upgrade : TurtleUpgrades.instance().getUpgrades() )
{
ItemStack stack = upgrade.getCraftingItem();
if( stack.isEmpty() ) return;
UpgradeInfo info = new UpgradeInfo( stack, upgrade );
upgradeItemLookup.computeIfAbsent( stack.getItem(), k -> new ArrayList<>( 1 ) ).add( info );
turtleUpgrades.add( info );
} );
};
for( IPocketUpgrade upgrade : PocketUpgrades.getUpgrades() )
for( IPocketUpgrade upgrade : PocketUpgrades.instance().getUpgrades() )
{
ItemStack stack = upgrade.getCraftingItem();
if( stack.isEmpty() ) continue;
@ -356,7 +357,7 @@ class RecipeResolver implements IRecipeManagerPlugin
recipes = this.recipes = new ArrayList<>( 4 );
for( ComputerFamily family : MAIN_FAMILIES )
{
if( turtle != null && TurtleUpgrades.suitableForFamily( family, turtle ) )
if( turtle != null )
{
recipes.add( horizontal(
of( Ingredient.EMPTY, ingredient, of( TurtleItemFactory.create( -1, null, -1, family, null, null, 0, null ) ) ),

View File

@ -61,6 +61,7 @@ public final class NetworkHandler
registerMainThread( 17, NetworkDirection.PLAY_TO_CLIENT, SpeakerStopClientMessage.class, SpeakerStopClientMessage::new );
registerMainThread( 18, NetworkDirection.PLAY_TO_CLIENT, SpeakerMoveClientMessage.class, SpeakerMoveClientMessage::new );
registerMainThread( 19, NetworkDirection.PLAY_TO_CLIENT, UploadResultMessage.class, UploadResultMessage::new );
registerMainThread( 20, NetworkDirection.PLAY_TO_CLIENT, UpgradesLoadedMessage.class, UpgradesLoadedMessage::new );
}
public static void sendToPlayer( Player player, NetworkMessage packet )

View File

@ -0,0 +1,104 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.network.client;
import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.api.pocket.PocketUpgradeSerialiser;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
import dan200.computercraft.api.upgrades.IUpgradeBase;
import dan200.computercraft.api.upgrades.UpgradeSerialiser;
import dan200.computercraft.shared.PocketUpgrades;
import dan200.computercraft.shared.TurtleUpgrades;
import dan200.computercraft.shared.UpgradeManager;
import dan200.computercraft.shared.network.NetworkMessage;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.fmllegacy.network.NetworkEvent;
import net.minecraftforge.registries.IForgeRegistry;
import net.minecraftforge.registries.RegistryManager;
import javax.annotation.Nonnull;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
/**
* Syncs turtle and pocket upgrades to the client.
*/
public class UpgradesLoadedMessage implements NetworkMessage
{
private final Map<String, UpgradeManager.UpgradeWrapper<TurtleUpgradeSerialiser<?>, ITurtleUpgrade>> turtleUpgrades;
private final Map<String, UpgradeManager.UpgradeWrapper<PocketUpgradeSerialiser<?>, IPocketUpgrade>> pocketUpgrades;
public UpgradesLoadedMessage()
{
turtleUpgrades = TurtleUpgrades.instance().getUpgradeWrappers();
pocketUpgrades = PocketUpgrades.instance().getUpgradeWrappers();
}
public UpgradesLoadedMessage( @Nonnull FriendlyByteBuf buf )
{
turtleUpgrades = fromBytes( buf, RegistryManager.ACTIVE.getRegistry( TurtleUpgradeSerialiser.TYPE ) );
pocketUpgrades = fromBytes( buf, RegistryManager.ACTIVE.getRegistry( PocketUpgradeSerialiser.TYPE ) );
}
private <R extends UpgradeSerialiser<? extends T, R>, T extends IUpgradeBase> Map<String, UpgradeManager.UpgradeWrapper<R, T>> fromBytes(
@Nonnull FriendlyByteBuf buf, @Nonnull IForgeRegistry<R> registry
)
{
int size = buf.readVarInt();
Map<String, UpgradeManager.UpgradeWrapper<R, T>> upgrades = new HashMap<>( size );
for( int i = 0; i < size; i++ )
{
String id = buf.readUtf();
ResourceLocation serialiserId = buf.readResourceLocation();
R serialiser = registry.getValue( serialiserId );
if( serialiser == null ) throw new IllegalStateException( "Unknown serialiser " + serialiserId );
T upgrade = serialiser.fromNetwork( new ResourceLocation( id ), buf );
String modId = buf.readUtf();
upgrades.put( id, new UpgradeManager.UpgradeWrapper<R, T>( id, upgrade, serialiser, modId ) );
}
return upgrades;
}
@Override
public void toBytes( @Nonnull FriendlyByteBuf buf )
{
toBytes( buf, turtleUpgrades );
toBytes( buf, pocketUpgrades );
}
private <R extends UpgradeSerialiser<? extends T, R>, T extends IUpgradeBase> void toBytes(
@Nonnull FriendlyByteBuf buf, Map<String, UpgradeManager.UpgradeWrapper<R, T>> upgrades
)
{
buf.writeVarInt( upgrades.size() );
for( var entry : upgrades.entrySet() )
{
buf.writeUtf( entry.getKey() );
@SuppressWarnings( "unchecked" )
var serialiser = (UpgradeSerialiser<T, R>) entry.getValue().serialiser();
buf.writeResourceLocation( Objects.requireNonNull( serialiser.getRegistryName(), "Serialiser is not registered!" ) );
serialiser.toNetwork( buf, entry.getValue().upgrade() );
buf.writeUtf( entry.getValue().modId() );
}
}
@Override
public void handle( NetworkEvent.Context context )
{
TurtleUpgrades.instance().loadFromNetwork( turtleUpgrades );
PocketUpgrades.instance().loadFromNetwork( pocketUpgrades );
}
}

View File

@ -14,6 +14,9 @@ import net.minecraft.world.phys.Vec3;
public abstract class WirelessModemPeripheral extends ModemPeripheral
{
public static final String NORMAL_ADJECTIVE = "upgrade.computercraft.wireless_modem_normal.adjective";
public static final String ADVANCED_ADJECTIVE = "upgrade.computercraft.wireless_modem_advanced.adjective";
private final boolean advanced;
public WirelessModemPeripheral( ModemState state, boolean advanced )

View File

@ -20,6 +20,8 @@ import java.util.UUID;
*/
public abstract class UpgradeSpeakerPeripheral extends SpeakerPeripheral
{
public static final String ADJECTIVE = "upgrade.computercraft.speaker.adjective";
private final UUID source = UUID.randomUUID();
@Override

View File

@ -135,7 +135,7 @@ public class PocketAPI implements ILuaAPI
ItemStack invStack = inv.get( (i + start) % inv.size() );
if( !invStack.isEmpty() )
{
IPocketUpgrade newUpgrade = PocketUpgrades.get( invStack );
IPocketUpgrade newUpgrade = PocketUpgrades.instance().get( invStack );
if( newUpgrade != null && newUpgrade != previous )
{

View File

@ -77,10 +77,7 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
{
if( !allowdedIn( group ) ) return;
stacks.add( create( -1, null, -1, null ) );
for( IPocketUpgrade upgrade : PocketUpgrades.getVanillaUpgrades() )
{
stacks.add( create( -1, null, -1, upgrade ) );
}
PocketUpgrades.getVanillaUpgrades().map( x -> create( -1, null, -1, x ) ).forEach( stacks::add );
}
@Override
@ -203,7 +200,7 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
{
// If we're a non-vanilla, non-CC upgrade then return whichever mod this upgrade
// belongs to.
String mod = PocketUpgrades.getOwner( upgrade );
String mod = PocketUpgrades.instance().getOwner( upgrade );
if( mod != null && !mod.equals( ComputerCraft.MOD_ID ) ) return mod;
}
@ -384,8 +381,7 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
{
CompoundTag compound = stack.getTag();
return compound != null && compound.contains( NBT_UPGRADE )
? PocketUpgrades.get( compound.getString( NBT_UPGRADE ) ) : null;
? PocketUpgrades.instance().get( compound.getString( NBT_UPGRADE ) ) : null;
}
public static void setUpgrade( @Nonnull ItemStack stack, IPocketUpgrade upgrade )

View File

@ -8,10 +8,11 @@ package dan200.computercraft.shared.pocket.peripherals;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.pocket.AbstractPocketUpgrade;
import dan200.computercraft.api.pocket.IPocketAccess;
import dan200.computercraft.shared.Registry;
import dan200.computercraft.shared.peripheral.modem.ModemState;
import dan200.computercraft.shared.peripheral.modem.wireless.WirelessModemPeripheral;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.ItemStack;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@ -20,14 +21,9 @@ public class PocketModem extends AbstractPocketUpgrade
{
private final boolean advanced;
public PocketModem( boolean advanced )
public PocketModem( ResourceLocation id, ItemStack stack, boolean advanced )
{
super(
new ResourceLocation( "computercraft", advanced ? "wireless_modem_advanced" : "wireless_modem_normal" ),
advanced
? Registry.ModBlocks.WIRELESS_MODEM_ADVANCED
: Registry.ModBlocks.WIRELESS_MODEM_NORMAL
);
super( id, advanced ? WirelessModemPeripheral.ADVANCED_ADJECTIVE : WirelessModemPeripheral.NORMAL_ADJECTIVE, stack );
this.advanced = advanced;
}

View File

@ -8,18 +8,19 @@ package dan200.computercraft.shared.pocket.peripherals;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.pocket.AbstractPocketUpgrade;
import dan200.computercraft.api.pocket.IPocketAccess;
import dan200.computercraft.shared.Registry;
import dan200.computercraft.shared.peripheral.speaker.UpgradeSpeakerPeripheral;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.ItemStack;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
public class PocketSpeaker extends AbstractPocketUpgrade
{
public PocketSpeaker()
public PocketSpeaker( ResourceLocation id, ItemStack item )
{
super( new ResourceLocation( "computercraft", "speaker" ), Registry.ModBlocks.SPEAKER );
super( id, UpgradeSpeakerPeripheral.ADJECTIVE, item );
}
@Nullable

View File

@ -86,7 +86,7 @@ public final class PocketComputerUpgradeRecipe extends CustomRecipe
if( x == computerX && y == computerY - 1 )
{
upgrade = PocketUpgrades.get( item );
upgrade = PocketUpgrades.instance().get( item );
if( upgrade == null ) return ItemStack.EMPTY;
}
else if( !item.isEmpty() )

View File

@ -695,7 +695,7 @@ public class TurtleAPI implements ILuaAPI
* @cc.usage <pre>{@code
* local has_block, data = turtle.inspect()
* if has_block then
* print(textutils.serialize(data))
* print(textutils.serialise(data))
* -- {
* -- name = "minecraft:oak_log",
* -- state = { axis = "x" },
@ -753,7 +753,7 @@ public class TurtleAPI implements ILuaAPI
* @cc.usage Print the current slot, assuming it contains 13 dirt.
*
* <pre>{@code
* print(textutils.serialize(turtle.getItemDetail()))
* print(textutils.serialise(turtle.getItemDetail()))
* -- => {
* -- name = "minecraft:dirt",
* -- count = 13,

View File

@ -160,8 +160,8 @@ public class TurtleBrain implements ITurtleAccess
overlay = nbt.contains( NBT_OVERLAY ) ? new ResourceLocation( nbt.getString( NBT_OVERLAY ) ) : null;
// Read upgrades
setUpgradeDirect( TurtleSide.LEFT, nbt.contains( NBT_LEFT_UPGRADE ) ? TurtleUpgrades.get( nbt.getString( NBT_LEFT_UPGRADE ) ) : null );
setUpgradeDirect( TurtleSide.RIGHT, nbt.contains( NBT_RIGHT_UPGRADE ) ? TurtleUpgrades.get( nbt.getString( NBT_RIGHT_UPGRADE ) ) : null );
setUpgradeDirect( TurtleSide.LEFT, nbt.contains( NBT_LEFT_UPGRADE ) ? TurtleUpgrades.instance().get( nbt.getString( NBT_LEFT_UPGRADE ) ) : null );
setUpgradeDirect( TurtleSide.RIGHT, nbt.contains( NBT_RIGHT_UPGRADE ) ? TurtleUpgrades.instance().get( nbt.getString( NBT_RIGHT_UPGRADE ) ) : null );
// NBT
upgradeNBTData.clear();

View File

@ -36,11 +36,8 @@ public class TurtleEquipCommand implements ITurtleCommand
if( !selectedStack.isEmpty() )
{
newUpgradeStack = selectedStack.copy();
newUpgrade = TurtleUpgrades.get( newUpgradeStack );
if( newUpgrade == null || !TurtleUpgrades.suitableForFamily( ((TurtleBrain) turtle).getFamily(), newUpgrade ) )
{
return TurtleCommandResult.failure( "Not a valid upgrade" );
}
newUpgrade = TurtleUpgrades.instance().get( newUpgradeStack );
if( newUpgrade == null ) return TurtleCommandResult.failure( "Not a valid upgrade" );
}
else
{

View File

@ -69,7 +69,6 @@ public class ItemTurtle extends ItemComputerBase implements ITurtleItem
list.add( create( -1, null, -1, null, null, 0, null ) );
TurtleUpgrades.getVanillaUpgrades()
.filter( x -> TurtleUpgrades.suitableForFamily( family, x ) )
.map( x -> create( -1, null, -1, null, x, 0, null ) )
.forEach( list::add );
}
@ -116,14 +115,14 @@ public class ItemTurtle extends ItemComputerBase implements ITurtleItem
ITurtleUpgrade left = getUpgrade( stack, TurtleSide.LEFT );
if( left != null )
{
String mod = TurtleUpgrades.getOwner( left );
String mod = TurtleUpgrades.instance().getOwner( left );
if( mod != null && !mod.equals( ComputerCraft.MOD_ID ) ) return mod;
}
ITurtleUpgrade right = getUpgrade( stack, TurtleSide.RIGHT );
if( right != null )
{
String mod = TurtleUpgrades.getOwner( right );
String mod = TurtleUpgrades.instance().getOwner( right );
if( mod != null && !mod.equals( ComputerCraft.MOD_ID ) ) return mod;
}
@ -148,7 +147,7 @@ public class ItemTurtle extends ItemComputerBase implements ITurtleItem
if( tag == null ) return null;
String key = side == TurtleSide.LEFT ? NBT_LEFT_UPGRADE : NBT_RIGHT_UPGRADE;
return tag.contains( key ) ? TurtleUpgrades.get( tag.getString( key ) ) : null;
return tag.contains( key ) ? TurtleUpgrades.instance().get( tag.getString( key ) ) : null;
}
@Override

View File

@ -152,19 +152,8 @@ public final class TurtleUpgradeRecipe extends CustomRecipe
{
if( !items[i].isEmpty() )
{
ITurtleUpgrade itemUpgrade = TurtleUpgrades.get( items[i] );
if( itemUpgrade == null )
{
return ItemStack.EMPTY;
}
if( upgrades[i] != null )
{
return ItemStack.EMPTY;
}
if( !TurtleUpgrades.suitableForFamily( family, itemUpgrade ) )
{
return ItemStack.EMPTY;
}
ITurtleUpgrade itemUpgrade = TurtleUpgrades.instance().get( items[i] );
if( itemUpgrade == null || upgrades[i] != null ) return ItemStack.EMPTY;
upgrades[i] = itemUpgrade;
}
}

View File

@ -1,34 +0,0 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.turtle.upgrades;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
public class TurtleAxe extends TurtleTool
{
public TurtleAxe( ResourceLocation id, String adjective, Item item )
{
super( id, adjective, item );
}
public TurtleAxe( ResourceLocation id, Item item )
{
super( id, item );
}
public TurtleAxe( ResourceLocation id, ItemStack craftItem, ItemStack toolItem )
{
super( id, craftItem, toolItem );
}
@Override
protected float getDamageMultiplier()
{
return 6.0f;
}
}

View File

@ -13,7 +13,7 @@ import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.api.turtle.TurtleUpgradeType;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
@ -24,9 +24,9 @@ public class TurtleCraftingTable extends AbstractTurtleUpgrade
private static final ModelResourceLocation leftModel = new ModelResourceLocation( "computercraft:turtle_crafting_table_left", "inventory" );
private static final ModelResourceLocation rightModel = new ModelResourceLocation( "computercraft:turtle_crafting_table_right", "inventory" );
public TurtleCraftingTable( ResourceLocation id )
public TurtleCraftingTable( ResourceLocation id, ItemStack stack )
{
super( id, TurtleUpgradeType.PERIPHERAL, Blocks.CRAFTING_TABLE );
super( id, TurtleUpgradeType.PERIPHERAL, "upgrade.minecraft.crafting_table.adjective", stack );
}
@Override

View File

@ -1,66 +0,0 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.turtle.upgrades;
import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.TurtleCommandResult;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.api.turtle.TurtleVerb;
import dan200.computercraft.shared.ComputerCraftTags;
import dan200.computercraft.shared.turtle.core.TurtlePlaceCommand;
import dan200.computercraft.shared.turtle.core.TurtlePlayer;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import javax.annotation.Nonnull;
public class TurtleHoe extends TurtleTool
{
public TurtleHoe( ResourceLocation id, String adjective, Item item )
{
super( id, adjective, item );
}
public TurtleHoe( ResourceLocation id, Item item )
{
super( id, item );
}
public TurtleHoe( ResourceLocation id, ItemStack craftItem, ItemStack toolItem )
{
super( id, craftItem, toolItem );
}
@Override
protected TurtleCommandResult checkBlockBreakable( BlockState state, Level world, BlockPos pos, TurtlePlayer player )
{
TurtleCommandResult result = super.checkBlockBreakable( state, world, pos, player );
if( !result.isSuccess() ) return result;
return state.is( ComputerCraftTags.Blocks.TURTLE_HOE_BREAKABLE )
|| isTriviallyBreakable( world, pos, state )
? result : INEFFECTIVE;
}
@Nonnull
@Override
public TurtleCommandResult useTool( @Nonnull ITurtleAccess turtle, @Nonnull TurtleSide side, @Nonnull TurtleVerb verb, @Nonnull Direction direction )
{
if( verb == TurtleVerb.DIG )
{
if( TurtlePlaceCommand.deployCopiedItem( item.copy(), turtle, direction, null, null ) )
{
return TurtleCommandResult.success();
}
}
return super.useTool( turtle, side, verb, direction );
}
}

View File

@ -8,7 +8,6 @@ package dan200.computercraft.shared.turtle.upgrades;
import dan200.computercraft.api.client.TransformedModel;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.turtle.*;
import dan200.computercraft.shared.Registry;
import dan200.computercraft.shared.peripheral.modem.ModemState;
import dan200.computercraft.shared.peripheral.modem.wireless.WirelessModemPeripheral;
import net.minecraft.client.resources.model.ModelResourceLocation;
@ -16,6 +15,7 @@ import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
@ -68,14 +68,9 @@ public class TurtleModem extends AbstractTurtleUpgrade
private final ModelResourceLocation leftOnModel;
private final ModelResourceLocation rightOnModel;
public TurtleModem( boolean advanced, ResourceLocation id )
public TurtleModem( ResourceLocation id, ItemStack stack, boolean advanced )
{
super(
id, TurtleUpgradeType.PERIPHERAL,
advanced
? Registry.ModBlocks.WIRELESS_MODEM_ADVANCED
: Registry.ModBlocks.WIRELESS_MODEM_NORMAL
);
super( id, TurtleUpgradeType.PERIPHERAL, advanced ? WirelessModemPeripheral.ADVANCED_ADJECTIVE : WirelessModemPeripheral.NORMAL_ADJECTIVE, stack );
this.advanced = advanced;
if( advanced )

View File

@ -1,66 +0,0 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.turtle.upgrades;
import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.TurtleCommandResult;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.api.turtle.TurtleVerb;
import dan200.computercraft.shared.ComputerCraftTags;
import dan200.computercraft.shared.turtle.core.TurtlePlaceCommand;
import dan200.computercraft.shared.turtle.core.TurtlePlayer;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import javax.annotation.Nonnull;
public class TurtleShovel extends TurtleTool
{
public TurtleShovel( ResourceLocation id, String adjective, Item item )
{
super( id, adjective, item );
}
public TurtleShovel( ResourceLocation id, Item item )
{
super( id, item );
}
public TurtleShovel( ResourceLocation id, ItemStack craftItem, ItemStack toolItem )
{
super( id, craftItem, toolItem );
}
@Override
protected TurtleCommandResult checkBlockBreakable( BlockState state, Level world, BlockPos pos, TurtlePlayer player )
{
TurtleCommandResult result = super.checkBlockBreakable( state, world, pos, player );
if( !result.isSuccess() ) return result;
return state.is( ComputerCraftTags.Blocks.TURTLE_SHOVEL_BREAKABLE )
|| isTriviallyBreakable( world, pos, state )
? result : INEFFECTIVE;
}
@Nonnull
@Override
public TurtleCommandResult useTool( @Nonnull ITurtleAccess turtle, @Nonnull TurtleSide side, @Nonnull TurtleVerb verb, @Nonnull Direction direction )
{
if( verb == TurtleVerb.DIG )
{
if( TurtlePlaceCommand.deployCopiedItem( item.copy(), turtle, direction, null, null ) )
{
return TurtleCommandResult.success();
}
}
return super.useTool( turtle, side, verb, direction );
}
}

View File

@ -11,11 +11,11 @@ import dan200.computercraft.api.turtle.AbstractTurtleUpgrade;
import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.api.turtle.TurtleUpgradeType;
import dan200.computercraft.shared.Registry;
import dan200.computercraft.shared.peripheral.speaker.UpgradeSpeakerPeripheral;
import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.core.BlockPos;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
@ -57,9 +57,9 @@ public class TurtleSpeaker extends AbstractTurtleUpgrade
}
}
public TurtleSpeaker( ResourceLocation id )
public TurtleSpeaker( ResourceLocation id, ItemStack item )
{
super( id, TurtleUpgradeType.PERIPHERAL, Registry.ModBlocks.SPEAKER );
super( id, TurtleUpgradeType.PERIPHERAL, UpgradeSpeakerPeripheral.ADJECTIVE, item );
}
@Override

View File

@ -1,51 +0,0 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.turtle.upgrades;
import dan200.computercraft.api.turtle.TurtleCommandResult;
import dan200.computercraft.shared.ComputerCraftTags;
import dan200.computercraft.shared.turtle.core.TurtlePlayer;
import net.minecraft.core.BlockPos;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
public class TurtleSword extends TurtleTool
{
public TurtleSword( ResourceLocation id, String adjective, Item item )
{
super( id, adjective, item );
}
public TurtleSword( ResourceLocation id, Item item )
{
super( id, item );
}
public TurtleSword( ResourceLocation id, ItemStack craftItem, ItemStack toolItem )
{
super( id, craftItem, toolItem );
}
@Override
protected TurtleCommandResult checkBlockBreakable( BlockState state, Level world, BlockPos pos, TurtlePlayer player )
{
TurtleCommandResult result = super.checkBlockBreakable( state, world, pos, player );
if( !result.isSuccess() ) return result;
return state.is( ComputerCraftTags.Blocks.TURTLE_SWORD_BREAKABLE )
|| isTriviallyBreakable( world, pos, state )
? result : INEFFECTIVE;
}
@Override
protected float getDamageMultiplier()
{
return 9.0f;
}
}

View File

@ -8,11 +8,12 @@ package dan200.computercraft.shared.turtle.upgrades;
import com.mojang.math.Matrix4f;
import com.mojang.math.Transformation;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.ComputerCraftTags;
import dan200.computercraft.api.client.TransformedModel;
import dan200.computercraft.api.turtle.*;
import dan200.computercraft.shared.ComputerCraftTags;
import dan200.computercraft.shared.TurtlePermissions;
import dan200.computercraft.shared.turtle.core.TurtleBrain;
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;
@ -20,8 +21,8 @@ import dan200.computercraft.shared.util.WorldUtil;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.Tag;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.ai.attributes.Attributes;
@ -39,36 +40,37 @@ import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.ToolActions;
import net.minecraftforge.event.entity.player.AttackEntityEvent;
import net.minecraftforge.event.world.BlockEvent;
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;
public class TurtleTool extends AbstractTurtleUpgrade
{
protected static final TurtleCommandResult UNBREAKABLE = TurtleCommandResult.failure( "Unbreakable block detected" );
protected static final TurtleCommandResult UNBREAKABLE = TurtleCommandResult.failure( "Cannot break unbreakable block" );
protected static final TurtleCommandResult INEFFECTIVE = TurtleCommandResult.failure( "Cannot break block with this tool" );
protected final ItemStack item;
final ItemStack item;
final float damageMulitiplier;
@Nullable
final ResourceLocation breakableName;
@Nullable
final Tag<Block> breakable;
public TurtleTool( ResourceLocation id, String adjective, Item item )
public TurtleTool( ResourceLocation id, String adjective, Item craftItem, ItemStack toolItem, float damageMulitiplier, @Nullable ResourceLocation breakableName, @Nullable Tag<Block> breakable )
{
super( id, TurtleUpgradeType.TOOL, adjective, item );
this.item = new ItemStack( item );
}
public TurtleTool( ResourceLocation id, Item item )
{
super( id, TurtleUpgradeType.TOOL, item );
this.item = new ItemStack( item );
}
public TurtleTool( ResourceLocation id, ItemStack craftItem, ItemStack toolItem )
{
super( id, TurtleUpgradeType.TOOL, craftItem );
super( id, TurtleUpgradeType.TOOL, adjective, new ItemStack( craftItem ) );
item = toolItem;
this.damageMulitiplier = damageMulitiplier;
this.breakableName = breakableName;
this.breakable = breakable;
}
@Override
@ -80,8 +82,8 @@ public class TurtleTool extends AbstractTurtleUpgrade
// Check we've not got anything vaguely interesting on the item. We allow other mods to add their
// own NBT, with the understanding such details will be lost to the mist of time.
if( stack.isDamaged() || stack.isEnchanted() || stack.hasCustomHoverName() ) return false;
if( tag.contains( "AttributeModifiers", Tag.TAG_LIST ) &&
!tag.getList( "AttributeModifiers", Tag.TAG_COMPOUND ).isEmpty() )
if( tag.contains( "AttributeModifiers", TAG_LIST ) &&
!tag.getList( "AttributeModifiers", TAG_COMPOUND ).isEmpty() )
{
return false;
}
@ -104,9 +106,9 @@ public class TurtleTool extends AbstractTurtleUpgrade
switch( verb )
{
case ATTACK:
return attack( turtle, direction, side );
return attack( turtle, direction );
case DIG:
return dig( turtle, direction, side );
return dig( turtle, direction );
default:
return TurtleCommandResult.failure( "Unsupported action" );
}
@ -115,19 +117,18 @@ public class TurtleTool extends AbstractTurtleUpgrade
protected TurtleCommandResult checkBlockBreakable( BlockState state, Level world, BlockPos pos, TurtlePlayer player )
{
Block block = state.getBlock();
return !state.isAir()
&& block != Blocks.BEDROCK
&& state.getDestroyProgress( player, world, pos ) > 0
&& block.canEntityDestroy( state, world, pos, player )
? TurtleCommandResult.success() : UNBREAKABLE;
if( state.isAir() || block == Blocks.BEDROCK
|| state.getDestroyProgress( player, world, pos ) <= 0
|| !block.canEntityDestroy( state, world, pos, player ) )
{
return UNBREAKABLE;
}
return breakable == null || breakable.contains( state.getBlock() ) || isTriviallyBreakable( world, pos, state )
? TurtleCommandResult.success() : INEFFECTIVE;
}
protected float getDamageMultiplier()
{
return 3.0f;
}
private TurtleCommandResult attack( ITurtleAccess turtle, Direction direction, TurtleSide side )
private TurtleCommandResult attack( ITurtleAccess turtle, Direction direction )
{
// Create a fake player, and orient it appropriately
Level world = turtle.getLevel();
@ -162,9 +163,7 @@ public class TurtleTool extends AbstractTurtleUpgrade
boolean attacked = false;
if( !hitEntity.skipAttackInteraction( turtlePlayer ) )
{
float damage = (float) turtlePlayer.getAttributeValue( Attributes.ATTACK_DAMAGE );
damage *= getDamageMultiplier();
ComputerCraft.log.info( "Dealing {} damage", damage );
float damage = (float) turtlePlayer.getAttributeValue( Attributes.ATTACK_DAMAGE ) * damageMulitiplier;
if( damage > 0.0f )
{
DamageSource source = DamageSource.playerAttack( turtlePlayer );
@ -196,8 +195,17 @@ public class TurtleTool extends AbstractTurtleUpgrade
return TurtleCommandResult.failure( "Nothing to attack here" );
}
private TurtleCommandResult dig( ITurtleAccess turtle, Direction direction, TurtleSide side )
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( TurtlePlaceCommand.deployCopiedItem( item.copy(), turtle, direction, null, null ) )
{
return TurtleCommandResult.success();
}
}
// Get ready to dig
Level world = turtle.getLevel();
BlockPos turtlePosition = turtle.getPosition();

View File

@ -0,0 +1,84 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.shared.turtle.upgrades;
import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException;
import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
import dan200.computercraft.api.upgrades.IUpgradeBase;
import net.minecraft.core.Registry;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.SerializationTags;
import net.minecraft.tags.Tag;
import net.minecraft.util.GsonHelper;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.Block;
import net.minecraftforge.registries.ForgeRegistries;
import javax.annotation.Nonnull;
public final class TurtleToolSerialiser extends TurtleUpgradeSerialiser.Base<TurtleTool>
{
public static final TurtleToolSerialiser INSTANCE = new TurtleToolSerialiser();
private TurtleToolSerialiser() {}
@Nonnull
@Override
public TurtleTool fromJson( @Nonnull ResourceLocation id, @Nonnull JsonObject object )
{
var adjective = GsonHelper.getAsString( object, "adjective", IUpgradeBase.getDefaultAdjective( id ) );
var toolItem = GsonHelper.getAsItem( object, "item" );
var craftingItem = GsonHelper.getAsItem( object, "craftingItem", toolItem );
var damageMultiplier = GsonHelper.getAsFloat( object, "damageMultiplier", 3.0f );
ResourceLocation breakableName = null;
Tag<Block> breakable = null;
if( object.has( "breakable" ) )
{
breakableName = new ResourceLocation( GsonHelper.getAsString( object, "breakable" ) );
breakable = SerializationTags.getInstance().getTagOrThrow(
Registry.BLOCK_REGISTRY, breakableName,
tagId -> new JsonSyntaxException( "Unknown item tag '" + tagId + "'" )
);
}
return new TurtleTool( id, adjective, craftingItem, new ItemStack( toolItem ), damageMultiplier, breakableName, breakable );
}
@Nonnull
@Override
public TurtleTool fromNetwork( @Nonnull ResourceLocation id, @Nonnull FriendlyByteBuf buffer )
{
var adjective = buffer.readUtf();
var craftingItem = buffer.readRegistryIdUnsafe( ForgeRegistries.ITEMS );
var toolItem = buffer.readItem();
// damageMultiplier and breakable aren't used by the client, but we need to construct the upgrade exactly
// as otherwise syncing on an SP world will overwrite the (shared) upgrade registry with an invalid upgrade!
var damageMultiplier = buffer.readFloat();
ResourceLocation breakableName = null;
Tag<Block> breakable = null;
if( buffer.readBoolean() )
{
breakableName = buffer.readResourceLocation();
breakable = SerializationTags.getInstance().getOrEmpty( Registry.BLOCK_REGISTRY ).getTagOrEmpty( breakableName );
}
return new TurtleTool( id, adjective, craftingItem, toolItem, damageMultiplier, breakableName, breakable );
}
@Override
public void toNetwork( @Nonnull FriendlyByteBuf buffer, @Nonnull TurtleTool upgrade )
{
buffer.writeUtf( upgrade.getUnlocalisedAdjective() );
buffer.writeRegistryIdUnsafe( ForgeRegistries.ITEMS, upgrade.getCraftingItem().getItem() );
buffer.writeItem( upgrade.item );
buffer.writeFloat( upgrade.damageMulitiplier );
buffer.writeBoolean( upgrade.breakableName != null );
if( upgrade.breakableName != null ) buffer.writeResourceLocation( upgrade.breakableName );
}
}

View File

@ -1,6 +1,6 @@
{
"pack": {
"pack_format": 6,
"pack_format": 7,
"description": "CC: Tweaked"
}
}

View File

@ -1,6 +1,6 @@
{
"pack": {
"pack_format": 4,
"pack_format": 7,
"description": "CC: Test"
}
}