1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-02-16 02:50:22 +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.ClientComputerRegistry;
import dan200.computercraft.shared.computer.core.ServerComputerRegistry; import dan200.computercraft.shared.computer.core.ServerComputerRegistry;
import dan200.computercraft.shared.peripheral.monitor.MonitorRenderer; 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 net.minecraftforge.fml.common.Mod;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@ -79,27 +76,6 @@ public final class ComputerCraft
public static int monitorWidth = 8; public static int monitorWidth = 8;
public static int monitorHeight = 6; 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 // Registries
public static final ClientComputerRegistry clientComputerRegistry = new ClientComputerRegistry(); public static final ClientComputerRegistry clientComputerRegistry = new ClientComputerRegistry();
public static final ServerComputerRegistry serverComputerRegistry = new ServerComputerRegistry(); 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.IWiredElement;
import dan200.computercraft.api.network.wired.IWiredNode; import dan200.computercraft.api.network.wired.IWiredNode;
import dan200.computercraft.api.peripheral.IPeripheralProvider; import dan200.computercraft.api.peripheral.IPeripheralProvider;
import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.api.redstone.IBundledRedstoneProvider; import dan200.computercraft.api.redstone.IBundledRedstoneProvider;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.core.apis.ApiFactories; import dan200.computercraft.core.apis.ApiFactories;
import dan200.computercraft.core.asm.GenericMethod; import dan200.computercraft.core.asm.GenericMethod;
import dan200.computercraft.core.filesystem.FileMount; import dan200.computercraft.core.filesystem.FileMount;
import dan200.computercraft.core.filesystem.ResourceMount; 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.generic.GenericPeripheralProvider;
import dan200.computercraft.shared.peripheral.modem.wireless.WirelessNetwork; import dan200.computercraft.shared.peripheral.modem.wireless.WirelessNetwork;
import dan200.computercraft.shared.util.IDAssigner; import dan200.computercraft.shared.util.IDAssigner;
@ -125,12 +125,6 @@ public final class ComputerCraftAPIImpl implements IComputerCraftAPI
GenericPeripheralProvider.addCapability( capability ); GenericPeripheralProvider.addCapability( capability );
} }
@Override
public void registerTurtleUpgrade( @Nonnull ITurtleUpgrade upgrade )
{
TurtleUpgrades.register( upgrade );
}
@Override @Override
public void registerBundledRedstoneProvider( @Nonnull IBundledRedstoneProvider provider ) public void registerBundledRedstoneProvider( @Nonnull IBundledRedstoneProvider provider )
{ {
@ -149,12 +143,6 @@ public final class ComputerCraftAPIImpl implements IComputerCraftAPI
MediaProviders.register( provider ); MediaProviders.register( provider );
} }
@Override
public void registerPocketUpgrade( @Nonnull IPocketUpgrade upgrade )
{
PocketUpgrades.register( upgrade );
}
@Nonnull @Nonnull
@Override @Override
public IPacketNetwork getWirelessNetwork() 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.IComputerAccess;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.peripheral.IPeripheralProvider; import dan200.computercraft.api.peripheral.IPeripheralProvider;
import dan200.computercraft.api.pocket.IPocketUpgrade;
import dan200.computercraft.api.redstone.IBundledRedstoneProvider; import dan200.computercraft.api.redstone.IBundledRedstoneProvider;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.BlockGetter;
@ -38,19 +36,14 @@ import javax.annotation.Nullable;
*/ */
public final class ComputerCraftAPI public final class ComputerCraftAPI
{ {
public static final String MOD_ID = "computercraft";
@Nonnull @Nonnull
public static String getInstalledVersion() public static String getInstalledVersion()
{ {
return getInstance().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. * 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 ); 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. * Registers a bundled redstone provider to provide bundled redstone output for blocks.
* *
@ -201,11 +181,6 @@ public final class ComputerCraftAPI
getInstance().registerMediaProvider( provider ); getInstance().registerMediaProvider( provider );
} }
public static void registerPocketUpgrade( @Nonnull IPocketUpgrade upgrade )
{
getInstance().registerPocketUpgrade( upgrade );
}
/** /**
* Attempt to get the game-wide wireless network. * Attempt to get the game-wide wireless network.
* *
@ -286,16 +261,12 @@ public final class ComputerCraftAPI
void registerGenericCapability( @Nonnull Capability<?> capability ); void registerGenericCapability( @Nonnull Capability<?> capability );
void registerTurtleUpgrade( @Nonnull ITurtleUpgrade upgrade );
void registerBundledRedstoneProvider( @Nonnull IBundledRedstoneProvider provider ); void registerBundledRedstoneProvider( @Nonnull IBundledRedstoneProvider provider );
int getBundledRedstoneOutput( @Nonnull Level world, @Nonnull BlockPos pos, @Nonnull Direction side ); int getBundledRedstoneOutput( @Nonnull Level world, @Nonnull BlockPos pos, @Nonnull Direction side );
void registerMediaProvider( @Nonnull IMediaProvider provider ); void registerMediaProvider( @Nonnull IMediaProvider provider );
void registerPocketUpgrade( @Nonnull IPocketUpgrade upgrade );
@Nonnull @Nonnull
IPacketNetwork getWirelessNetwork(); IPacketNetwork getWirelessNetwork();

View File

@ -1,9 +1,9 @@
/* /*
* This file is part of ComputerCraft - http://www.computercraft.info * This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Copyright Daniel Ratcliffe, 2011-2021. This API may be redistributed unmodified and in full only.
* Send enquiries to dratcliffe@gmail.com * 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 dan200.computercraft.ComputerCraft;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
@ -13,6 +13,9 @@ import net.minecraft.tags.Tag;
import net.minecraft.world.item.Item; import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
/**
* Tags provided by ComputerCraft.
*/
public class ComputerCraftTags public class ComputerCraftTags
{ {
public static class Items 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> WIRED_MODEM = make( "wired_modem" );
public static final Tag.Named<Block> MONITOR = make( "monitor" ); 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" ); 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" ); 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" ); 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" ); public static final Tag.Named<Block> TURTLE_HOE_BREAKABLE = make( "turtle_hoe_harvestable" );
private static Tag.Named<Block> make( String name ) private static Tag.Named<Block> make( String name )

View File

@ -5,15 +5,11 @@
*/ */
package dan200.computercraft.api.pocket; package dan200.computercraft.api.pocket;
import net.minecraft.Util; import dan200.computercraft.api.upgrades.IUpgradeBase;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.ItemLike;
import net.minecraftforge.common.util.NonNullSupplier;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.function.Supplier;
/** /**
* A base class for {@link IPocketUpgrade}s. * A base class for {@link IPocketUpgrade}s.
@ -24,48 +20,18 @@ public abstract class AbstractPocketUpgrade implements IPocketUpgrade
{ {
private final ResourceLocation id; private final ResourceLocation id;
private final String adjective; 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.id = id;
this.adjective = adjective; this.adjective = adjective;
this.stack = stack; 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 ) protected AbstractPocketUpgrade( ResourceLocation id, ItemStack stack )
{ {
this( id, () -> stack ); this( id, IUpgradeBase.getDefaultAdjective( 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 ) );
} }
@Nonnull @Nonnull
@ -86,32 +52,6 @@ public abstract class AbstractPocketUpgrade implements IPocketUpgrade
@Override @Override
public final ItemStack getCraftingItem() public final ItemStack getCraftingItem()
{ {
return stack.get(); return stack;
}
/**
* 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 );
}
} }
} }

View File

@ -5,18 +5,24 @@
*/ */
package dan200.computercraft.api.pocket; 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.peripheral.IPeripheral;
import dan200.computercraft.api.upgrades.IUpgradeBase;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; 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 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; package dan200.computercraft.api.turtle;
import net.minecraft.Util; import dan200.computercraft.api.upgrades.IUpgradeBase;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.ItemLike;
import net.minecraftforge.common.util.NonNullSupplier;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.function.Supplier;
/** /**
* A base class for {@link ITurtleUpgrade}s. * A base class for {@link ITurtleUpgrade}s.
@ -25,9 +21,9 @@ public abstract class AbstractTurtleUpgrade implements ITurtleUpgrade
private final ResourceLocation id; private final ResourceLocation id;
private final TurtleUpgradeType type; private final TurtleUpgradeType type;
private final String adjective; 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.id = id;
this.type = type; this.type = type;
@ -35,39 +31,9 @@ public abstract class AbstractTurtleUpgrade implements ITurtleUpgrade
this.stack = stack; 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 ) protected AbstractTurtleUpgrade( ResourceLocation id, TurtleUpgradeType type, ItemStack stack )
{ {
this( id, type, () -> stack ); this( id, type, IUpgradeBase.getDefaultAdjective( id ), 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 ) );
} }
@Nonnull @Nonnull
@ -95,32 +61,6 @@ public abstract class AbstractTurtleUpgrade implements ITurtleUpgrade
@Override @Override
public final ItemStack getCraftingItem() public final ItemStack getCraftingItem()
{ {
return stack.get(); return stack;
}
/**
* 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 );
}
} }
} }

View File

@ -5,10 +5,9 @@
*/ */
package dan200.computercraft.api.turtle; 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.client.TransformedModel;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.upgrades.IUpgradeBase;
import net.minecraft.client.resources.model.ModelResourceLocation; import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
@ -21,10 +20,17 @@ import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
/** /**
* The primary interface for defining an update for Turtles. A turtle update * The primary interface for defining an update for Turtles. A turtle update can either be a new tool, or a new
* can either be a new tool, or a new peripheral. * 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 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. * 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. * 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.pocket.IPocketUpgrade;
import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.ITurtleUpgrade;
import net.minecraft.Util;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
@ -83,4 +84,18 @@ public interface IUpgradeBase
if( craftingShareTag == null ) return shareTag.isEmpty(); if( craftingShareTag == null ) return shareTag.isEmpty();
return shareTag.equals( craftingShareTag ); 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.BiFunction;
import java.util.function.Consumer; 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; private final Consumer<JsonObject> extra;

View File

@ -25,13 +25,13 @@ import net.minecraftforge.registries.IForgeRegistryEntry;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import java.util.Objects; import java.util.Objects;
public class BlockModelProvider extends BlockStateProvider class BlockModelProvider extends BlockStateProvider
{ {
private ModelFile monitorBase; private ModelFile monitorBase;
private ModelFile turtleBase; private ModelFile turtleBase;
private ModelFile modemBase; private ModelFile modemBase;
public BlockModelProvider( DataGenerator generator, ExistingFileHelper existingFileHelper ) BlockModelProvider( DataGenerator generator, ExistingFileHelper existingFileHelper )
{ {
super( generator, ComputerCraft.MOD_ID, 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.minecraft.world.level.block.Blocks;
import net.minecraftforge.common.data.ExistingFileHelper; 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 ); super( generator, ComputerCraft.MOD_ID, helper );
} }

View File

@ -23,7 +23,12 @@ public class Generators
DataGenerator generator = event.getGenerator(); DataGenerator generator = event.getGenerator();
ExistingFileHelper existingFiles = event.getExistingFileHelper(); 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 LootTableGenerator( generator ) );
generator.addProvider( new BlockModelProvider( generator, existingFiles ) ); generator.addProvider( new BlockModelProvider( generator, existingFiles ) );

View File

@ -6,18 +6,18 @@
package dan200.computercraft.data; package dan200.computercraft.data;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraft;
import dan200.computercraft.shared.ComputerCraftTags.Blocks; import dan200.computercraft.api.ComputerCraftTags.Blocks;
import dan200.computercraft.shared.Registry; import dan200.computercraft.shared.Registry;
import net.minecraft.data.DataGenerator; import net.minecraft.data.DataGenerator;
import net.minecraft.data.tags.ItemTagsProvider; import net.minecraft.data.tags.ItemTagsProvider;
import net.minecraft.tags.ItemTags; import net.minecraft.tags.ItemTags;
import net.minecraftforge.common.data.ExistingFileHelper; 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 ); super( generator, blockTags, ComputerCraft.MOD_ID, helper );
} }

View File

@ -30,9 +30,9 @@ import net.minecraftforge.fmllegacy.RegistryObject;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
public class LootTableGenerator extends LootTableProvider class LootTableGenerator extends LootTableProvider
{ {
public LootTableGenerator( DataGenerator generator ) LootTableGenerator( DataGenerator generator )
{ {
super( 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. * 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 static final Gson GSON = new GsonBuilder().setPrettyPrinting().disableHtmlEscaping().create();
private final DataGenerator generator; private final DataGenerator generator;
public LootTableProvider( DataGenerator generator ) LootTableProvider( DataGenerator generator )
{ {
this.generator = 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; package dan200.computercraft.data;
import dan200.computercraft.ComputerCraft; 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.Registry;
import dan200.computercraft.shared.TurtleUpgrades;
import dan200.computercraft.shared.common.ColourableRecipe; import dan200.computercraft.shared.common.ColourableRecipe;
import dan200.computercraft.shared.common.IColouredItem; import dan200.computercraft.shared.common.IColouredItem;
import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ComputerFamily;
@ -39,14 +39,20 @@ import javax.annotation.Nonnull;
import java.util.Locale; import java.util.Locale;
import java.util.function.Consumer; import java.util.function.Consumer;
import static dan200.computercraft.shared.ComputerCraftTags.Items.COMPUTER; import static dan200.computercraft.api.ComputerCraftTags.Items.COMPUTER;
import static dan200.computercraft.shared.ComputerCraftTags.Items.WIRED_MODEM; 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 ); super( generator );
this.turtleUpgrades = turtleUpgrades;
this.pocketUpgrades = pocketUpgrades;
} }
@Override @Override
@ -101,7 +107,8 @@ public class RecipeGenerator extends RecipeProvider
String nameId = family.name().toLowerCase( Locale.ROOT ); 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 ); ItemStack result = TurtleItemFactory.create( -1, null, -1, family, null, upgrade, -1, null );
ShapedRecipeBuilder ShapedRecipeBuilder
.shaped( result.getItem() ) .shaped( result.getItem() )
@ -117,7 +124,7 @@ public class RecipeGenerator extends RecipeProvider
nameId, upgrade.getUpgradeID().getNamespace(), upgrade.getUpgradeID().getPath() nameId, upgrade.getUpgradeID().getNamespace(), upgrade.getUpgradeID().getPath()
) ) ) )
); );
} ); }
} }
} }
@ -135,7 +142,8 @@ public class RecipeGenerator extends RecipeProvider
String nameId = family.name().toLowerCase( Locale.ROOT ); 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 ); ItemStack result = PocketComputerItemFactory.create( -1, null, -1, family, upgrade );
ShapedRecipeBuilder ShapedRecipeBuilder
.shaped( result.getItem() ) .shaped( result.getItem() )
@ -152,7 +160,7 @@ public class RecipeGenerator extends RecipeProvider
nameId, upgrade.getUpgradeID().getNamespace(), upgrade.getUpgradeID().getPath() 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. * 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 FinishedRecipe recipe;
private final CompoundTag resultData; 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.IComputer;
import dan200.computercraft.shared.computer.core.IContainerComputer; import dan200.computercraft.shared.computer.core.IContainerComputer;
import dan200.computercraft.shared.computer.core.ServerComputer; 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 dan200.computercraft.shared.peripheral.modem.wireless.WirelessNetwork;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer; 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.LootPool;
import net.minecraft.world.level.storage.loot.entries.LootTableReference; import net.minecraft.world.level.storage.loot.entries.LootTableReference;
import net.minecraft.world.level.storage.loot.providers.number.ConstantValue; import net.minecraft.world.level.storage.loot.providers.number.ConstantValue;
import net.minecraftforge.event.AddReloadListenerEvent; import net.minecraftforge.event.*;
import net.minecraftforge.event.LootTableLoadEvent;
import net.minecraftforge.event.RegisterCommandsEvent;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.entity.player.PlayerContainerEvent; import net.minecraftforge.event.entity.player.PlayerContainerEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent; import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
@ -145,5 +144,21 @@ public final class CommonHooks
public static void onAddReloadListeners( AddReloadListenerEvent event ) public static void onAddReloadListeners( AddReloadListenerEvent event )
{ {
event.addListener( ResourceMount.RELOAD_LISTENER ); 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.ComputerCraft;
import dan200.computercraft.api.pocket.IPocketUpgrade; import dan200.computercraft.api.pocket.IPocketUpgrade;
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenCustomHashMap; import dan200.computercraft.api.pocket.PocketUpgradeSerialiser;
import net.minecraft.Util;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.fml.ModContainer;
import net.minecraftforge.fml.ModLoadingContext;
import javax.annotation.Nonnull; import java.util.stream.Stream;
import javax.annotation.Nullable;
import java.util.*;
public final class PocketUpgrades public final class PocketUpgrades
{ {
private static final Map<String, IPocketUpgrade> upgrades = new HashMap<>(); private static final UpgradeManager<PocketUpgradeSerialiser<?>, IPocketUpgrade> registry = new UpgradeManager<>(
private static final Map<IPocketUpgrade, String> upgradeOwners = new Object2ObjectLinkedOpenCustomHashMap<>( Util.identityStrategy() ); "pocket computer upgrade", "computercraft/pocket_upgrades", PocketUpgradeSerialiser.TYPE
);
private PocketUpgrades() {} private PocketUpgrades() {}
public static synchronized void register( @Nonnull IPocketUpgrade upgrade ) public static UpgradeManager<PocketUpgradeSerialiser<?>, IPocketUpgrade> instance()
{ {
Objects.requireNonNull( upgrade, "upgrade cannot be null" ); return registry;
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() );
} }
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. return instance().getUpgradeWrappers().values().stream()
if( id.equals( "computercraft:advanved_modem" ) ) id = "computercraft:advanced_modem"; .filter( x -> x.modId().equals( ComputerCraft.MOD_ID ) )
.map( UpgradeManager.UpgradeWrapper::upgrade );
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() );
} }
} }

View File

@ -10,6 +10,8 @@ import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.media.IMedia; import dan200.computercraft.api.media.IMedia;
import dan200.computercraft.api.network.wired.IWiredElement; import dan200.computercraft.api.network.wired.IWiredElement;
import dan200.computercraft.api.peripheral.IPeripheral; 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.command.arguments.ArgumentSerializers;
import dan200.computercraft.shared.common.ColourableRecipe; import dan200.computercraft.shared.common.ColourableRecipe;
import dan200.computercraft.shared.common.ContainerHeldItem; 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.EntityType;
import net.minecraft.world.entity.MobCategory; import net.minecraft.world.entity.MobCategory;
import net.minecraft.world.inventory.MenuType; 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.item.crafting.RecipeSerializer;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntity; 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.items.CapabilityItemHandler;
import net.minecraftforge.registries.DeferredRegister; import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries; import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.RegistryBuilder;
import java.util.function.BiFunction; import java.util.function.BiFunction;
@ -251,49 +257,32 @@ public final class Registry
() -> new ItemBlockCable.WiredModem( ModBlocks.CABLE.get(), properties() ) ); () -> new ItemBlockCable.WiredModem( ModBlocks.CABLE.get(), properties() ) );
} }
@SubscribeEvent public static class ModTurtleSerialisers
public static void registerItems( RegistryEvent.Register<Item> event )
{ {
registerTurtleUpgrades(); static final DeferredRegister<TurtleUpgradeSerialiser<?>> SERIALISERS = DeferredRegister.create( TurtleUpgradeSerialiser.TYPE, ComputerCraft.MOD_ID );
registerPocketUpgrades();
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 static final DeferredRegister<PocketUpgradeSerialiser<?>> SERIALISERS = DeferredRegister.create( PocketUpgradeSerialiser.TYPE, ComputerCraft.MOD_ID );
ComputerCraft.TurtleUpgrades.wirelessModemNormal = new TurtleModem( false, new ResourceLocation( ComputerCraft.MOD_ID, "wireless_modem_normal" ) );
ComputerCraftAPI.registerTurtleUpgrade( ComputerCraft.TurtleUpgrades.wirelessModemNormal );
ComputerCraft.TurtleUpgrades.wirelessModemAdvanced = new TurtleModem( true, new ResourceLocation( ComputerCraft.MOD_ID, "wireless_modem_advanced" ) ); public static final RegistryObject<PocketUpgradeSerialiser<PocketSpeaker>> SPEAKER =
ComputerCraftAPI.registerTurtleUpgrade( ComputerCraft.TurtleUpgrades.wirelessModemAdvanced ); SERIALISERS.register( "speaker", () -> PocketUpgradeSerialiser.simpleWithCustomItem( PocketSpeaker::new ) );
public static final RegistryObject<PocketUpgradeSerialiser<PocketModem>> WIRELESS_MODEM_NORMAL =
ComputerCraft.TurtleUpgrades.speaker = new TurtleSpeaker( new ResourceLocation( ComputerCraft.MOD_ID, "speaker" ) ); SERIALISERS.register( "wireless_modem_normal", () -> PocketUpgradeSerialiser.simpleWithCustomItem( ( id, item ) -> new PocketModem( id, item, false ) ) );
ComputerCraftAPI.registerTurtleUpgrade( ComputerCraft.TurtleUpgrades.speaker ); public static final RegistryObject<PocketUpgradeSerialiser<PocketModem>> WIRELESS_MODEM_ADVANCED =
SERIALISERS.register( "wireless_modem_advanced", () -> PocketUpgradeSerialiser.simpleWithCustomItem( ( id, item ) -> new PocketModem( id, item, true ) ) );
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 class ModEntities public static class ModEntities
@ -337,6 +326,20 @@ public final class Registry
() -> ContainerData.toType( ViewComputerContainerData::new, ContainerViewComputer::new ) ); () -> 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 @SubscribeEvent
public static void registerRecipeSerializers( RegistryEvent.Register<RecipeSerializer<?>> event ) public static void registerRecipeSerializers( RegistryEvent.Register<RecipeSerializer<?>> event )
{ {
@ -423,6 +426,8 @@ public final class Registry
ModBlocks.BLOCKS.register( bus ); ModBlocks.BLOCKS.register( bus );
ModBlockEntities.TILES.register( bus ); ModBlockEntities.TILES.register( bus );
ModItems.ITEMS.register( bus ); ModItems.ITEMS.register( bus );
ModTurtleSerialisers.SERIALISERS.register( bus );
ModPocketUpgradeSerialisers.SERIALISERS.register( bus );
ModEntities.ENTITIES.register( bus ); ModEntities.ENTITIES.register( bus );
ModContainers.CONTAINERS.register( bus ); ModContainers.CONTAINERS.register( bus );
} }

View File

@ -7,177 +7,27 @@ package dan200.computercraft.shared;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.api.turtle.TurtleUpgradeSerialiser;
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 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; import java.util.stream.Stream;
public final class TurtleUpgrades public final class TurtleUpgrades
{ {
private static class Wrapper private static final UpgradeManager<TurtleUpgradeSerialiser<?>, ITurtleUpgrade> registry = new UpgradeManager<>(
{ "turtle upgrade", "computercraft/turtle_upgrades", TurtleUpgradeSerialiser.TYPE
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 TurtleUpgrades() {} private TurtleUpgrades() {}
public static void register( @Nonnull ITurtleUpgrade upgrade ) public static UpgradeManager<TurtleUpgradeSerialiser<?>, ITurtleUpgrade> instance()
{ {
Objects.requireNonNull( upgrade, "upgrade cannot be null" ); return registry;
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;
} }
public static Stream<ITurtleUpgrade> getVanillaUpgrades() public static Stream<ITurtleUpgrade> getVanillaUpgrades()
{ {
if( vanilla == null ) return instance().getUpgradeWrappers().values().stream()
{ .filter( x -> x.modId().equals( ComputerCraft.MOD_ID ) )
vanilla = new ITurtleUpgrade[] { .map( UpgradeManager.UpgradeWrapper::upgrade );
// 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;
} }
} }

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<>(); List<ItemStack> upgradeItems = new ArrayList<>();
for( ComputerFamily family : MAIN_FAMILIES ) for( ComputerFamily family : MAIN_FAMILIES )
{ {
TurtleUpgrades.getUpgrades() for( ITurtleUpgrade upgrade : TurtleUpgrades.instance().getUpgrades() )
.filter( x -> TurtleUpgrades.suitableForFamily( family, x ) ) {
.map( x -> TurtleItemFactory.create( -1, null, -1, family, null, x, 0, null ) ) upgradeItems.add( TurtleItemFactory.create( -1, null, -1, family, null, upgrade, 0, null ) );
.forEach( upgradeItems::add ); }
for( IPocketUpgrade upgrade : PocketUpgrades.getUpgrades() ) for( IPocketUpgrade upgrade : PocketUpgrades.instance().getUpgrades() )
{ {
upgradeItems.add( PocketComputerItemFactory.create( -1, null, -1, family, upgrade ) ); 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 // Hide all upgrade recipes
IRecipeCategory<?> category = registry.getRecipeCategory( VanillaRecipeCategoryUid.CRAFTING, false ); IRecipeCategory<?> category = registry.getRecipeCategory( VanillaRecipeCategoryUid.CRAFTING, false );

View File

@ -6,7 +6,7 @@
package dan200.computercraft.shared.integration.jei; package dan200.computercraft.shared.integration.jei;
import dan200.computercraft.ComputerCraft; 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.pocket.IPocketUpgrade;
import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleSide; import dan200.computercraft.api.turtle.TurtleSide;
@ -52,16 +52,17 @@ class RecipeResolver implements IRecipeManagerPlugin
if( initialised ) return; if( initialised ) return;
initialised = true; initialised = true;
TurtleUpgrades.getUpgrades().forEach( upgrade -> { for( ITurtleUpgrade upgrade : TurtleUpgrades.instance().getUpgrades() )
{
ItemStack stack = upgrade.getCraftingItem(); ItemStack stack = upgrade.getCraftingItem();
if( stack.isEmpty() ) return; if( stack.isEmpty() ) return;
UpgradeInfo info = new UpgradeInfo( stack, upgrade ); UpgradeInfo info = new UpgradeInfo( stack, upgrade );
upgradeItemLookup.computeIfAbsent( stack.getItem(), k -> new ArrayList<>( 1 ) ).add( info ); upgradeItemLookup.computeIfAbsent( stack.getItem(), k -> new ArrayList<>( 1 ) ).add( info );
turtleUpgrades.add( info ); turtleUpgrades.add( info );
} ); };
for( IPocketUpgrade upgrade : PocketUpgrades.getUpgrades() ) for( IPocketUpgrade upgrade : PocketUpgrades.instance().getUpgrades() )
{ {
ItemStack stack = upgrade.getCraftingItem(); ItemStack stack = upgrade.getCraftingItem();
if( stack.isEmpty() ) continue; if( stack.isEmpty() ) continue;
@ -356,7 +357,7 @@ class RecipeResolver implements IRecipeManagerPlugin
recipes = this.recipes = new ArrayList<>( 4 ); recipes = this.recipes = new ArrayList<>( 4 );
for( ComputerFamily family : MAIN_FAMILIES ) for( ComputerFamily family : MAIN_FAMILIES )
{ {
if( turtle != null && TurtleUpgrades.suitableForFamily( family, turtle ) ) if( turtle != null )
{ {
recipes.add( horizontal( recipes.add( horizontal(
of( Ingredient.EMPTY, ingredient, of( TurtleItemFactory.create( -1, null, -1, family, null, null, 0, null ) ) ), 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( 17, NetworkDirection.PLAY_TO_CLIENT, SpeakerStopClientMessage.class, SpeakerStopClientMessage::new );
registerMainThread( 18, NetworkDirection.PLAY_TO_CLIENT, SpeakerMoveClientMessage.class, SpeakerMoveClientMessage::new ); registerMainThread( 18, NetworkDirection.PLAY_TO_CLIENT, SpeakerMoveClientMessage.class, SpeakerMoveClientMessage::new );
registerMainThread( 19, NetworkDirection.PLAY_TO_CLIENT, UploadResultMessage.class, UploadResultMessage::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 ) 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 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; private final boolean advanced;
public WirelessModemPeripheral( ModemState state, 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 abstract class UpgradeSpeakerPeripheral extends SpeakerPeripheral
{ {
public static final String ADJECTIVE = "upgrade.computercraft.speaker.adjective";
private final UUID source = UUID.randomUUID(); private final UUID source = UUID.randomUUID();
@Override @Override

View File

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

View File

@ -77,10 +77,7 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
{ {
if( !allowdedIn( group ) ) return; if( !allowdedIn( group ) ) return;
stacks.add( create( -1, null, -1, null ) ); stacks.add( create( -1, null, -1, null ) );
for( IPocketUpgrade upgrade : PocketUpgrades.getVanillaUpgrades() ) PocketUpgrades.getVanillaUpgrades().map( x -> create( -1, null, -1, x ) ).forEach( stacks::add );
{
stacks.add( create( -1, null, -1, upgrade ) );
}
} }
@Override @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 // If we're a non-vanilla, non-CC upgrade then return whichever mod this upgrade
// belongs to. // belongs to.
String mod = PocketUpgrades.getOwner( upgrade ); String mod = PocketUpgrades.instance().getOwner( upgrade );
if( mod != null && !mod.equals( ComputerCraft.MOD_ID ) ) return mod; 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(); CompoundTag compound = stack.getTag();
return compound != null && compound.contains( NBT_UPGRADE ) 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 ) 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.peripheral.IPeripheral;
import dan200.computercraft.api.pocket.AbstractPocketUpgrade; import dan200.computercraft.api.pocket.AbstractPocketUpgrade;
import dan200.computercraft.api.pocket.IPocketAccess; import dan200.computercraft.api.pocket.IPocketAccess;
import dan200.computercraft.shared.Registry;
import dan200.computercraft.shared.peripheral.modem.ModemState; import dan200.computercraft.shared.peripheral.modem.ModemState;
import dan200.computercraft.shared.peripheral.modem.wireless.WirelessModemPeripheral;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.ItemStack;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -20,14 +21,9 @@ public class PocketModem extends AbstractPocketUpgrade
{ {
private final boolean advanced; private final boolean advanced;
public PocketModem( boolean advanced ) public PocketModem( ResourceLocation id, ItemStack stack, boolean advanced )
{ {
super( super( id, advanced ? WirelessModemPeripheral.ADVANCED_ADJECTIVE : WirelessModemPeripheral.NORMAL_ADJECTIVE, stack );
new ResourceLocation( "computercraft", advanced ? "wireless_modem_advanced" : "wireless_modem_normal" ),
advanced
? Registry.ModBlocks.WIRELESS_MODEM_ADVANCED
: Registry.ModBlocks.WIRELESS_MODEM_NORMAL
);
this.advanced = advanced; 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.peripheral.IPeripheral;
import dan200.computercraft.api.pocket.AbstractPocketUpgrade; import dan200.computercraft.api.pocket.AbstractPocketUpgrade;
import dan200.computercraft.api.pocket.IPocketAccess; 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.resources.ResourceLocation;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.item.ItemStack;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable; import javax.annotation.Nullable;
public class PocketSpeaker extends AbstractPocketUpgrade 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 @Nullable

View File

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

View File

@ -695,7 +695,7 @@ public class TurtleAPI implements ILuaAPI
* @cc.usage <pre>{@code * @cc.usage <pre>{@code
* local has_block, data = turtle.inspect() * local has_block, data = turtle.inspect()
* if has_block then * if has_block then
* print(textutils.serialize(data)) * print(textutils.serialise(data))
* -- { * -- {
* -- name = "minecraft:oak_log", * -- name = "minecraft:oak_log",
* -- state = { axis = "x" }, * -- state = { axis = "x" },
@ -753,7 +753,7 @@ public class TurtleAPI implements ILuaAPI
* @cc.usage Print the current slot, assuming it contains 13 dirt. * @cc.usage Print the current slot, assuming it contains 13 dirt.
* *
* <pre>{@code * <pre>{@code
* print(textutils.serialize(turtle.getItemDetail())) * print(textutils.serialise(turtle.getItemDetail()))
* -- => { * -- => {
* -- name = "minecraft:dirt", * -- name = "minecraft:dirt",
* -- count = 13, * -- 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; overlay = nbt.contains( NBT_OVERLAY ) ? new ResourceLocation( nbt.getString( NBT_OVERLAY ) ) : null;
// Read upgrades // Read upgrades
setUpgradeDirect( TurtleSide.LEFT, nbt.contains( NBT_LEFT_UPGRADE ) ? TurtleUpgrades.get( nbt.getString( NBT_LEFT_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.get( nbt.getString( NBT_RIGHT_UPGRADE ) ) : null ); setUpgradeDirect( TurtleSide.RIGHT, nbt.contains( NBT_RIGHT_UPGRADE ) ? TurtleUpgrades.instance().get( nbt.getString( NBT_RIGHT_UPGRADE ) ) : null );
// NBT // NBT
upgradeNBTData.clear(); upgradeNBTData.clear();

View File

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

View File

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

View File

@ -152,19 +152,8 @@ public final class TurtleUpgradeRecipe extends CustomRecipe
{ {
if( !items[i].isEmpty() ) if( !items[i].isEmpty() )
{ {
ITurtleUpgrade itemUpgrade = TurtleUpgrades.get( items[i] ); ITurtleUpgrade itemUpgrade = TurtleUpgrades.instance().get( items[i] );
if( itemUpgrade == null ) if( itemUpgrade == null || upgrades[i] != null ) return ItemStack.EMPTY;
{
return ItemStack.EMPTY;
}
if( upgrades[i] != null )
{
return ItemStack.EMPTY;
}
if( !TurtleUpgrades.suitableForFamily( family, itemUpgrade ) )
{
return ItemStack.EMPTY;
}
upgrades[i] = itemUpgrade; 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 dan200.computercraft.api.turtle.TurtleUpgradeType;
import net.minecraft.client.resources.model.ModelResourceLocation; import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.resources.ResourceLocation; 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.Dist;
import net.minecraftforge.api.distmarker.OnlyIn; 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 leftModel = new ModelResourceLocation( "computercraft:turtle_crafting_table_left", "inventory" );
private static final ModelResourceLocation rightModel = new ModelResourceLocation( "computercraft:turtle_crafting_table_right", "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 @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.client.TransformedModel;
import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.turtle.*; import dan200.computercraft.api.turtle.*;
import dan200.computercraft.shared.Registry;
import dan200.computercraft.shared.peripheral.modem.ModemState; import dan200.computercraft.shared.peripheral.modem.ModemState;
import dan200.computercraft.shared.peripheral.modem.wireless.WirelessModemPeripheral; import dan200.computercraft.shared.peripheral.modem.wireless.WirelessModemPeripheral;
import net.minecraft.client.resources.model.ModelResourceLocation; import net.minecraft.client.resources.model.ModelResourceLocation;
@ -16,6 +15,7 @@ import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.Dist;
@ -68,14 +68,9 @@ public class TurtleModem extends AbstractTurtleUpgrade
private final ModelResourceLocation leftOnModel; private final ModelResourceLocation leftOnModel;
private final ModelResourceLocation rightOnModel; private final ModelResourceLocation rightOnModel;
public TurtleModem( boolean advanced, ResourceLocation id ) public TurtleModem( ResourceLocation id, ItemStack stack, boolean advanced )
{ {
super( super( id, TurtleUpgradeType.PERIPHERAL, advanced ? WirelessModemPeripheral.ADVANCED_ADJECTIVE : WirelessModemPeripheral.NORMAL_ADJECTIVE, stack );
id, TurtleUpgradeType.PERIPHERAL,
advanced
? Registry.ModBlocks.WIRELESS_MODEM_ADVANCED
: Registry.ModBlocks.WIRELESS_MODEM_NORMAL
);
this.advanced = advanced; this.advanced = advanced;
if( 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.ITurtleAccess;
import dan200.computercraft.api.turtle.TurtleSide; import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.api.turtle.TurtleUpgradeType; import dan200.computercraft.api.turtle.TurtleUpgradeType;
import dan200.computercraft.shared.Registry;
import dan200.computercraft.shared.peripheral.speaker.UpgradeSpeakerPeripheral; import dan200.computercraft.shared.peripheral.speaker.UpgradeSpeakerPeripheral;
import net.minecraft.client.resources.model.ModelResourceLocation; import net.minecraft.client.resources.model.ModelResourceLocation;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist; 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 @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.Matrix4f;
import com.mojang.math.Transformation; import com.mojang.math.Transformation;
import dan200.computercraft.ComputerCraft; import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.ComputerCraftTags;
import dan200.computercraft.api.client.TransformedModel; import dan200.computercraft.api.client.TransformedModel;
import dan200.computercraft.api.turtle.*; import dan200.computercraft.api.turtle.*;
import dan200.computercraft.shared.ComputerCraftTags;
import dan200.computercraft.shared.TurtlePermissions; import dan200.computercraft.shared.TurtlePermissions;
import dan200.computercraft.shared.turtle.core.TurtleBrain; 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.turtle.core.TurtlePlayer;
import dan200.computercraft.shared.util.DropConsumer; import dan200.computercraft.shared.util.DropConsumer;
import dan200.computercraft.shared.util.InventoryUtil; 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.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.Tag;
import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.ai.attributes.Attributes; 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.Dist;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.common.ToolActions;
import net.minecraftforge.event.entity.player.AttackEntityEvent; import net.minecraftforge.event.entity.player.AttackEntityEvent;
import net.minecraftforge.event.world.BlockEvent; import net.minecraftforge.event.world.BlockEvent;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.function.Function; 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 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 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 ); super( id, TurtleUpgradeType.TOOL, adjective, new ItemStack( craftItem ) );
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 );
item = toolItem; item = toolItem;
this.damageMulitiplier = damageMulitiplier;
this.breakableName = breakableName;
this.breakable = breakable;
} }
@Override @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 // 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. // 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( stack.isDamaged() || stack.isEnchanted() || stack.hasCustomHoverName() ) return false;
if( tag.contains( "AttributeModifiers", Tag.TAG_LIST ) && if( tag.contains( "AttributeModifiers", TAG_LIST ) &&
!tag.getList( "AttributeModifiers", Tag.TAG_COMPOUND ).isEmpty() ) !tag.getList( "AttributeModifiers", TAG_COMPOUND ).isEmpty() )
{ {
return false; return false;
} }
@ -104,9 +106,9 @@ public class TurtleTool extends AbstractTurtleUpgrade
switch( verb ) switch( verb )
{ {
case ATTACK: case ATTACK:
return attack( turtle, direction, side ); return attack( turtle, direction );
case DIG: case DIG:
return dig( turtle, direction, side ); return dig( turtle, direction );
default: default:
return TurtleCommandResult.failure( "Unsupported action" ); 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 ) protected TurtleCommandResult checkBlockBreakable( BlockState state, Level world, BlockPos pos, TurtlePlayer player )
{ {
Block block = state.getBlock(); Block block = state.getBlock();
return !state.isAir() if( state.isAir() || block == Blocks.BEDROCK
&& block != Blocks.BEDROCK || state.getDestroyProgress( player, world, pos ) <= 0
&& state.getDestroyProgress( player, world, pos ) > 0 || !block.canEntityDestroy( state, world, pos, player ) )
&& block.canEntityDestroy( state, world, pos, player ) {
? TurtleCommandResult.success() : UNBREAKABLE; return UNBREAKABLE;
}
return breakable == null || breakable.contains( state.getBlock() ) || isTriviallyBreakable( world, pos, state )
? TurtleCommandResult.success() : INEFFECTIVE;
} }
protected float getDamageMultiplier() private TurtleCommandResult attack( ITurtleAccess turtle, Direction direction )
{
return 3.0f;
}
private TurtleCommandResult attack( ITurtleAccess turtle, Direction direction, TurtleSide side )
{ {
// Create a fake player, and orient it appropriately // Create a fake player, and orient it appropriately
Level world = turtle.getLevel(); Level world = turtle.getLevel();
@ -162,9 +163,7 @@ public class TurtleTool extends AbstractTurtleUpgrade
boolean attacked = false; boolean attacked = false;
if( !hitEntity.skipAttackInteraction( turtlePlayer ) ) if( !hitEntity.skipAttackInteraction( turtlePlayer ) )
{ {
float damage = (float) turtlePlayer.getAttributeValue( Attributes.ATTACK_DAMAGE ); float damage = (float) turtlePlayer.getAttributeValue( Attributes.ATTACK_DAMAGE ) * damageMulitiplier;
damage *= getDamageMultiplier();
ComputerCraft.log.info( "Dealing {} damage", damage );
if( damage > 0.0f ) if( damage > 0.0f )
{ {
DamageSource source = DamageSource.playerAttack( turtlePlayer ); DamageSource source = DamageSource.playerAttack( turtlePlayer );
@ -196,8 +195,17 @@ public class TurtleTool extends AbstractTurtleUpgrade
return TurtleCommandResult.failure( "Nothing to attack here" ); 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 // Get ready to dig
Level world = turtle.getLevel(); Level world = turtle.getLevel();
BlockPos turtlePosition = turtle.getPosition(); 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": {
"pack_format": 6, "pack_format": 7,
"description": "CC: Tweaked" "description": "CC: Tweaked"
} }
} }

View File

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