1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-11-20 23:34:48 +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
72 changed files with 1548 additions and 834 deletions

View File

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

View File

@@ -1,48 +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;
import dan200.computercraft.ComputerCraft;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.BlockTags;
import net.minecraft.tags.ItemTags;
import net.minecraft.tags.Tag;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
public class ComputerCraftTags
{
public static class Items
{
public static final Tag.Named<Item> COMPUTER = make( "computer" );
public static final Tag.Named<Item> TURTLE = make( "turtle" );
public static final Tag.Named<Item> WIRED_MODEM = make( "wired_modem" );
public static final Tag.Named<Item> MONITOR = make( "monitor" );
private static Tag.Named<Item> make( String name )
{
return ItemTags.bind( new ResourceLocation( ComputerCraft.MOD_ID, name ).toString() );
}
}
public static class Blocks
{
public static final Tag.Named<Block> COMPUTER = make( "computer" );
public static final Tag.Named<Block> TURTLE = make( "turtle" );
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> TURTLE_ALWAYS_BREAKABLE = make( "turtle_always_breakable" );
public static final Tag.Named<Block> TURTLE_SHOVEL_BREAKABLE = make( "turtle_shovel_harvestable" );
public static final Tag.Named<Block> TURTLE_SWORD_BREAKABLE = make( "turtle_sword_harvestable" );
public static final Tag.Named<Block> TURTLE_HOE_BREAKABLE = make( "turtle_hoe_harvestable" );
private static Tag.Named<Block> make( String name )
{
return BlockTags.bind( new ResourceLocation( ComputerCraft.MOD_ID, name ).toString() );
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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