mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-08-28 00:12:16 +00:00
Merge pull request #29 from davidqueneau/fabric
Porting GenericPeripherals from upstream CC: Tweaked
This commit is contained in:
commit
88722d484f
@ -38,6 +38,9 @@ dependencies {
|
||||
implementation "blue.endless:jankson:${jankson_version}"
|
||||
implementation 'com.google.code.findbugs:jsr305:3.0.2'
|
||||
|
||||
compileOnly 'com.google.auto.service:auto-service:1.0-rc7'
|
||||
annotationProcessor 'com.google.auto.service:auto-service:1.0-rc7'
|
||||
|
||||
include "me.shedaniel.cloth:config-2:${cloth_config_version}"
|
||||
include "blue.endless:jankson:${jankson_version}"
|
||||
include 'javax.vecmath:vecmath:1.5.2'
|
||||
|
@ -21,6 +21,7 @@ import dan200.computercraft.api.turtle.event.TurtleAction;
|
||||
import dan200.computercraft.core.apis.http.options.Action;
|
||||
import dan200.computercraft.core.apis.http.options.AddressRule;
|
||||
import dan200.computercraft.core.apis.http.websocket.Websocket;
|
||||
import dan200.computercraft.core.asm.GenericSource;
|
||||
import dan200.computercraft.shared.common.ColourableRecipe;
|
||||
import dan200.computercraft.shared.computer.core.ClientComputerRegistry;
|
||||
import dan200.computercraft.shared.computer.core.ServerComputerRegistry;
|
||||
@ -48,6 +49,7 @@ import dan200.computercraft.shared.turtle.upgrades.TurtleTool;
|
||||
import dan200.computercraft.shared.util.Config;
|
||||
import dan200.computercraft.shared.util.ImpostorRecipe;
|
||||
import dan200.computercraft.shared.util.ImpostorShapelessRecipe;
|
||||
import dan200.computercraft.shared.util.ServiceUtil;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
@ -154,6 +156,7 @@ public final class ComputerCraft implements ModInitializer {
|
||||
Registry.register(Registry.LOOT_CONDITION_TYPE, new Identifier(ComputerCraft.MOD_ID, "player_creative"), PlayerCreativeLootCondition.TYPE);
|
||||
Registry.register(Registry.LOOT_CONDITION_TYPE, new Identifier(ComputerCraft.MOD_ID, "has_id"), HasComputerIdLootCondition.TYPE);
|
||||
init();
|
||||
GenericSource.setup( () -> ServiceUtil.loadServices( GenericSource.class ));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.api.peripheral.IPeripheralProvider;
|
||||
|
||||
import dan200.computercraft.shared.peripheral.generic.GenericPeripheralProvider;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
@ -53,7 +54,7 @@ public final class Peripherals {
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
return GenericPeripheralProvider.getPeripheral(world, pos, side);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* 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.peripheral.generic;
|
||||
|
||||
import dan200.computercraft.api.lua.*;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.api.peripheral.IDynamicPeripheral;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.block.entity.BlockEntityType;
|
||||
import net.minecraft.block.entity.LockableContainerBlockEntity;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.Nameable;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
|
||||
class GenericPeripheral implements IDynamicPeripheral
|
||||
{
|
||||
private final String type;
|
||||
private final BlockEntity tile;
|
||||
private final List<SaturatedMethod> methods;
|
||||
|
||||
GenericPeripheral( BlockEntity tile, List<SaturatedMethod> methods )
|
||||
{
|
||||
Identifier type = BlockEntityType.getId(tile.getType());
|
||||
this.tile = tile;
|
||||
this.type = type == null ? "unknown" : type.toString();
|
||||
this.methods = methods;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String[] getMethodNames()
|
||||
{
|
||||
String[] names = new String[methods.size()];
|
||||
for( int i = 0; i < methods.size(); i++ ) names[i] = methods.get( i ).getName();
|
||||
return names;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public MethodResult callMethod( @Nonnull IComputerAccess computer, @Nonnull ILuaContext context, int method, @Nonnull IArguments arguments ) throws LuaException
|
||||
{
|
||||
return methods.get( method ).apply( context, computer, arguments );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getType()
|
||||
{
|
||||
return type;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object getTarget()
|
||||
{
|
||||
return tile;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals( @Nullable IPeripheral other )
|
||||
{
|
||||
if( other == this ) return true;
|
||||
if( !(other instanceof GenericPeripheral) ) return false;
|
||||
|
||||
GenericPeripheral generic = (GenericPeripheral) other;
|
||||
return tile == generic.tile && methods.equals( generic.methods );
|
||||
}
|
||||
}
|
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.peripheral.generic;
|
||||
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.core.asm.NamedMethod;
|
||||
import dan200.computercraft.core.asm.PeripheralMethod;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class GenericPeripheralProvider
|
||||
{
|
||||
@Nullable
|
||||
public static IPeripheral getPeripheral( @Nonnull World world, @Nonnull BlockPos pos, @Nonnull Direction side )
|
||||
{
|
||||
BlockEntity tile = world.getBlockEntity( pos );
|
||||
if( tile == null ) return null;
|
||||
|
||||
ArrayList<SaturatedMethod> saturated = new ArrayList<>( 0 );
|
||||
|
||||
// This seems to add inventory methods, how???
|
||||
List<NamedMethod<PeripheralMethod>> tileMethods = PeripheralMethod.GENERATOR.getMethods( tile.getClass() );
|
||||
if( !tileMethods.isEmpty() ) addSaturated( saturated, tile, tileMethods );
|
||||
|
||||
return saturated.isEmpty() ? null : new GenericPeripheral( tile, saturated );
|
||||
}
|
||||
|
||||
private static void addSaturated( ArrayList<SaturatedMethod> saturated, Object target, List<NamedMethod<PeripheralMethod>> methods )
|
||||
{
|
||||
saturated.ensureCapacity( saturated.size() + methods.size() );
|
||||
for( NamedMethod<PeripheralMethod> method : methods )
|
||||
{
|
||||
saturated.add( new SaturatedMethod( target, method ) );
|
||||
}
|
||||
}
|
||||
}
|
@ -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.shared.peripheral.generic;
|
||||
|
||||
import dan200.computercraft.api.lua.IArguments;
|
||||
import dan200.computercraft.api.lua.ILuaContext;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.MethodResult;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.core.asm.NamedMethod;
|
||||
import dan200.computercraft.core.asm.PeripheralMethod;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
final class SaturatedMethod
|
||||
{
|
||||
private final Object target;
|
||||
private final String name;
|
||||
private final PeripheralMethod method;
|
||||
|
||||
SaturatedMethod( Object target, NamedMethod<PeripheralMethod> method )
|
||||
{
|
||||
this.target = target;
|
||||
this.name = method.getName();
|
||||
this.method = method.getMethod();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
MethodResult apply( @Nonnull ILuaContext context, @Nonnull IComputerAccess computer, @Nonnull IArguments args ) throws LuaException
|
||||
{
|
||||
return method.apply( target, context, computer, args );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
String getName()
|
||||
{
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals( Object obj )
|
||||
{
|
||||
if( obj == this ) return true;
|
||||
if( !(obj instanceof SaturatedMethod) ) return false;
|
||||
|
||||
SaturatedMethod other = (SaturatedMethod) obj;
|
||||
return method == other.method && target.equals( other.target );
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return 31 * target.hashCode() + method.hashCode();
|
||||
}
|
||||
}
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* 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.peripheral.generic.data;
|
||||
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.state.property.Property;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class BlockData
|
||||
{
|
||||
@Nonnull
|
||||
public static <T extends Map<? super String, Object>> T fill( @Nonnull T data, @Nonnull BlockState state )
|
||||
{
|
||||
data.put("name", DataHelpers.getId( state.getBlock() ) );
|
||||
|
||||
Map<Object, Object> stateTable = new HashMap<>();
|
||||
for (ImmutableMap.Entry<Property<?>, ? extends Comparable<?>> entry : state.getEntries().entrySet()) {
|
||||
Property<?> property = entry.getKey();
|
||||
stateTable.put(property.getName(), getPropertyValue(property, entry.getValue()));
|
||||
}
|
||||
data.put("state", stateTable);
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@SuppressWarnings( { "unchecked", "rawtypes" } )
|
||||
private static Object getPropertyValue( Property property, Comparable value )
|
||||
{
|
||||
if( value instanceof String || value instanceof Number || value instanceof Boolean ) return value;
|
||||
return property.name( value );
|
||||
}
|
||||
}
|
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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.peripheral.generic.data;
|
||||
|
||||
import net.minecraft.block.AbstractBlock;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.enchantment.Enchantment;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public final class DataHelpers
|
||||
{
|
||||
private DataHelpers()
|
||||
{ }
|
||||
|
||||
@Nonnull
|
||||
public static Map<String, Boolean> getTags( @Nonnull Collection<Identifier> tags )
|
||||
{
|
||||
Map<String, Boolean> result = new HashMap<>( tags.size() );
|
||||
for( Identifier location : tags ) result.put( location.toString(), true );
|
||||
return result;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getId( @Nonnull Block block )
|
||||
{
|
||||
Identifier id = Registry.BLOCK.getId(block);
|
||||
return id == null ? null : id.toString();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getId( @Nonnull Item item )
|
||||
{
|
||||
Identifier id = Registry.ITEM.getId(item);
|
||||
return id == null ? null : id.toString();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String getId( @Nonnull Enchantment enchantment)
|
||||
{
|
||||
Identifier id = Registry.ENCHANTMENT.getId(enchantment);
|
||||
return id == null ? null : id.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,155 @@
|
||||
/*
|
||||
* 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.peripheral.generic.data;
|
||||
|
||||
import com.google.gson.JsonParseException;
|
||||
import dan200.computercraft.shared.util.NBTUtil;
|
||||
import net.minecraft.enchantment.Enchantment;
|
||||
import net.minecraft.enchantment.EnchantmentHelper;
|
||||
import net.minecraft.item.EnchantedBookItem;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.text.Text;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Data providers for items.
|
||||
*/
|
||||
public class ItemData
|
||||
{
|
||||
@Nonnull
|
||||
public static <T extends Map<? super String, Object>> T fillBasicSafe( @Nonnull T data, @Nonnull ItemStack stack )
|
||||
{
|
||||
data.put( "name", DataHelpers.getId( stack.getItem() ) );
|
||||
data.put( "count", stack.getCount() );
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static <T extends Map<? super String, Object>> T fillBasic( @Nonnull T data, @Nonnull ItemStack stack )
|
||||
{
|
||||
fillBasicSafe( data, stack );
|
||||
String hash = NBTUtil.getNBTHash( stack.getTag() );
|
||||
if( hash != null ) data.put( "nbt", hash );
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static <T extends Map<? super String, Object>> T fill( @Nonnull T data, @Nonnull ItemStack stack )
|
||||
{
|
||||
if( stack.isEmpty() ) return data;
|
||||
|
||||
fillBasic( data, stack );
|
||||
|
||||
data.put( "displayName", stack.toHoverableText().getString() );
|
||||
data.put( "maxCount", stack.getMaxCount() );
|
||||
|
||||
if( stack.isDamageable() )
|
||||
{
|
||||
data.put( "damage", stack.getDamage() );
|
||||
data.put( "maxDamage", stack.getMaxDamage() );
|
||||
}
|
||||
|
||||
if( stack.isDamaged() )
|
||||
{
|
||||
data.put( "durability", 1.0 - ( stack.getDamage() / stack.getMaxDamage() ) );
|
||||
}
|
||||
|
||||
/*
|
||||
* Used to hide some data from ItemStack tooltip.
|
||||
* @see https://minecraft.gamepedia.com/Tutorials/Command_NBT_tags
|
||||
* @see ItemStack#getTooltip
|
||||
*/
|
||||
CompoundTag tag = stack.getTag();
|
||||
int hideFlags = tag != null ? tag.getInt( "HideFlags" ) : 0;
|
||||
|
||||
List<Map<String, Object>> enchants = getAllEnchants( stack, hideFlags );
|
||||
if( !enchants.isEmpty() ) data.put( "enchantments", enchants );
|
||||
|
||||
if( tag != null && tag.getBoolean( "Unbreakable" ) && (hideFlags & 4) == 0 )
|
||||
{
|
||||
data.put( "unbreakable", true );
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Text parseTextComponent( @Nonnull Tag x )
|
||||
{
|
||||
try
|
||||
{
|
||||
return Text.Serializer.fromJson( x.toString() );
|
||||
}
|
||||
catch( JsonParseException e )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve all visible enchantments from given stack. Try to follow all tooltip rules : order and visibility.
|
||||
*
|
||||
* @param stack Stack to analyse
|
||||
* @param hideFlags An int used as bit field to provide visibility rules.
|
||||
* @return A filled list that contain all visible enchantments.
|
||||
*/
|
||||
@Nonnull
|
||||
private static List<Map<String, Object>> getAllEnchants( @Nonnull ItemStack stack, int hideFlags )
|
||||
{
|
||||
ArrayList<Map<String, Object>> enchants = new ArrayList<>( 0 );
|
||||
|
||||
if( stack.getItem() instanceof EnchantedBookItem && (hideFlags & 32) == 0 )
|
||||
{
|
||||
addEnchantments( EnchantedBookItem.getEnchantmentTag( stack ), enchants );
|
||||
}
|
||||
|
||||
if( stack.hasEnchantments() && (hideFlags & 1) == 0 )
|
||||
{
|
||||
/*
|
||||
* Mimic the EnchantmentHelper.getEnchantments(ItemStack stack) behavior without special case for Enchanted book.
|
||||
* I'll do that to have the same data than ones displayed in tooltip.
|
||||
* @see EnchantmentHelper.getEnchantments(ItemStack stack)
|
||||
*/
|
||||
addEnchantments( stack.getEnchantments(), enchants );
|
||||
}
|
||||
|
||||
return enchants;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a Mojang enchant map to a Lua list.
|
||||
*
|
||||
* @param rawEnchants The raw NBT list of enchantments
|
||||
* @param enchants The enchantment map to add it to.
|
||||
* @see EnchantmentHelper
|
||||
*/
|
||||
private static void addEnchantments( @Nonnull ListTag rawEnchants, @Nonnull ArrayList<Map<String, Object>> enchants )
|
||||
{
|
||||
if( rawEnchants.isEmpty() ) return;
|
||||
|
||||
enchants.ensureCapacity( enchants.size() + rawEnchants.size() );
|
||||
|
||||
|
||||
for( Map.Entry<Enchantment, Integer> entry : EnchantmentHelper.fromTag( rawEnchants ).entrySet() )
|
||||
{
|
||||
Enchantment enchantment = entry.getKey();
|
||||
Integer level = entry.getValue();
|
||||
HashMap<String, Object> enchant = new HashMap<>( 3 );
|
||||
enchant.put( "name", DataHelpers.getId( enchantment ) );
|
||||
enchant.put( "level", level );
|
||||
enchant.put( "displayName", enchantment.getName( level ).getString() );
|
||||
enchants.add( enchant );
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,37 @@
|
||||
/*
|
||||
* 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.peripheral.generic.methods;
|
||||
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
|
||||
/**
|
||||
* A few helpers for working with arguments.
|
||||
*
|
||||
* This should really be moved into the public API. However, until I have settled on a suitable format, we'll keep it
|
||||
* where it is used.
|
||||
*/
|
||||
final class ArgumentHelpers
|
||||
{
|
||||
private ArgumentHelpers()
|
||||
{
|
||||
}
|
||||
|
||||
public static void assertBetween( double value, double min, double max, String message ) throws LuaException
|
||||
{
|
||||
if( value < min || value > max || Double.isNaN( value ) )
|
||||
{
|
||||
throw new LuaException( String.format( message, "between " + min + " and " + max ) );
|
||||
}
|
||||
}
|
||||
|
||||
public static void assertBetween( int value, int min, int max, String message ) throws LuaException
|
||||
{
|
||||
if( value < min || value > max )
|
||||
{
|
||||
throw new LuaException( String.format( message, "between " + min + " and " + max ) );
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,384 @@
|
||||
/*
|
||||
* 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.peripheral.generic.methods;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
import dan200.computercraft.api.peripheral.IComputerAccess;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.core.asm.GenericSource;
|
||||
import dan200.computercraft.shared.peripheral.generic.data.ItemData;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.ChestBlock;
|
||||
import net.minecraft.block.InventoryProvider;
|
||||
import net.minecraft.block.entity.BlockEntity;
|
||||
import net.minecraft.block.entity.ChestBlockEntity;
|
||||
import net.minecraft.inventory.Inventory;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.text.Text;
|
||||
import net.minecraft.text.TranslatableText;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.Nameable;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import static dan200.computercraft.shared.peripheral.generic.methods.ArgumentHelpers.assertBetween;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Methods for interacting with inventories.
|
||||
*
|
||||
* @cc.module inventory
|
||||
*/
|
||||
@AutoService( GenericSource.class )
|
||||
public class InventoryMethods implements GenericSource
|
||||
{
|
||||
@Nonnull
|
||||
@Override
|
||||
public Identifier id()
|
||||
{
|
||||
return new Identifier(ComputerCraft.MOD_ID, "inventory" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the size of this inventory.
|
||||
*
|
||||
* @param inventory The current inventory.
|
||||
* @return The number of slots in this inventory.
|
||||
*/
|
||||
@LuaFunction( mainThread = true )
|
||||
public static int size( Inventory inventory )
|
||||
{
|
||||
// Get appropriate inventory for source peripheral
|
||||
inventory = extractHandler(inventory);
|
||||
|
||||
return inventory.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the name of this inventory.
|
||||
*
|
||||
* @param inventory The current inventory.
|
||||
* @return The name of this inventory, or {@code nil} if not present.
|
||||
*/
|
||||
@LuaFunction( mainThread = true )
|
||||
public static String name( Nameable inventory )
|
||||
{
|
||||
return inventory.hasCustomName() ? inventory.getName().asString() : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* List all items in this inventory. This returns a table, with an entry for each slot.
|
||||
*
|
||||
* Each item in the inventory is represented by a table containing some basic information, much like
|
||||
* @link dan200.computercraft.shared.turtle.apis.TurtleAPI#getItemDetail includes. More information can be fetched
|
||||
* with {@link #getItemDetail}.
|
||||
*
|
||||
* The table is sparse, and so empty slots will be `nil` - it is recommended to loop over using `pairs` rather than
|
||||
* `ipairs`.
|
||||
*
|
||||
* @param inventory The current inventory.
|
||||
* @return All items in this inventory.
|
||||
* @cc.treturn { (table|nil)... } All items in this inventory.
|
||||
*/
|
||||
@LuaFunction( mainThread = true )
|
||||
public static Map<Integer, Map<String, ?>> list( Inventory inventory )
|
||||
{
|
||||
// Get appropriate inventory for source peripheral
|
||||
inventory = extractHandler(inventory);
|
||||
|
||||
Map<Integer, Map<String, ?>> result = new HashMap<>();
|
||||
int size = inventory.size();
|
||||
for( int i = 0; i < size; i++ )
|
||||
{
|
||||
ItemStack stack = inventory.getStack( i );
|
||||
if( !stack.isEmpty() ) result.put( i + 1, ItemData.fillBasic( new HashMap<>( 4 ), stack ) );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get detailed information about an item.
|
||||
*
|
||||
* @param inventory The current inventory.
|
||||
* @param slot The slot to get information about.
|
||||
* @return Information about the item in this slot, or {@code nil} if not present.
|
||||
* @throws LuaException If the slot is out of range.
|
||||
* @cc.treturn table Information about the item in this slot, or {@code nil} if not present.
|
||||
*/
|
||||
@Nullable
|
||||
@LuaFunction( mainThread = true )
|
||||
public static Map<String, ?> getItemDetail( Inventory inventory, int slot ) throws LuaException
|
||||
{
|
||||
// Get appropriate inventory
|
||||
inventory = extractHandler(inventory);
|
||||
|
||||
assertBetween( slot, 1, inventory.size(), "Slot out of range (%s)" );
|
||||
|
||||
ItemStack stack = inventory.getStack( slot - 1 );
|
||||
return stack.isEmpty() ? null : ItemData.fill( new HashMap<>(), stack );
|
||||
}
|
||||
|
||||
/**
|
||||
* Push items from one inventory to another connected one.
|
||||
*
|
||||
* This allows you to push an item in an inventory to another inventory <em>on the same wired network</em>. Both
|
||||
* inventories must attached to wired modems which are connected via a cable.
|
||||
*
|
||||
* @param from Inventory to move items from.
|
||||
* @param computer The current computer.
|
||||
* @param toName The name of the peripheral/inventory to push to. This is the string given to @{peripheral.wrap},
|
||||
* and displayed by the wired modem.
|
||||
* @param fromSlot The slot in the current inventory to move items to.
|
||||
* @param limit The maximum number of items to move. Defaults to the current stack limit.
|
||||
* @param toSlot The slot in the target inventory to move to. If not given, the item will be inserted into any slot.
|
||||
* @return The number of transferred items.
|
||||
* @throws LuaException If the peripheral to transfer to doesn't exist or isn't an inventory.
|
||||
* @throws LuaException If either source or destination slot is out of range.
|
||||
* @cc.see peripheral.getName Allows you to get the name of a @{peripheral.wrap|wrapped} peripheral.
|
||||
* @cc.usage Wrap two chests, and push an item from one to another.
|
||||
* <pre>{@code
|
||||
* local chest_a = peripheral.wrap("minecraft:chest_0")
|
||||
* local chest_b = peripheral.wrap("minecraft:chest_1")
|
||||
*
|
||||
* chest_a.pushItems(peripheral.getName(chest_b), 1)
|
||||
* }</pre>
|
||||
*/
|
||||
@LuaFunction( mainThread = true )
|
||||
public static int pushItems(
|
||||
Inventory from, IComputerAccess computer,
|
||||
String toName, int fromSlot, Optional<Integer> limit, Optional<Integer> toSlot
|
||||
) throws LuaException
|
||||
{
|
||||
// Get appropriate inventory for source peripheral
|
||||
from = extractHandler(from);
|
||||
|
||||
// Find location to transfer to
|
||||
IPeripheral location = computer.getAvailablePeripheral( toName );
|
||||
if( location == null ) throw new LuaException( "Target '" + toName + "' does not exist" );
|
||||
|
||||
Inventory to = extractHandler( location.getTarget() );
|
||||
if( to == null ) throw new LuaException( "Target '" + toName + "' is not an inventory" );
|
||||
|
||||
// Validate slots
|
||||
int actualLimit = limit.orElse( Integer.MAX_VALUE );
|
||||
assertBetween( fromSlot, 1, from.size(), "From slot out of range (%s)" );
|
||||
if( toSlot.isPresent() ) assertBetween( toSlot.get(), 1, to.size(), "To slot out of range (%s)" );
|
||||
|
||||
if( actualLimit <= 0 ) return 0;
|
||||
return moveItem( from, fromSlot - 1, to, toSlot.orElse( 0 ) - 1, actualLimit );
|
||||
}
|
||||
|
||||
/**
|
||||
* Pull items from a connected inventory into this one.
|
||||
*
|
||||
* This allows you to transfer items between inventories <em>on the same wired network</em>. Both this and the source
|
||||
* inventory must attached to wired modems which are connected via a cable.
|
||||
*
|
||||
* @param to Inventory to move items to.
|
||||
* @param computer The current computer.
|
||||
* @param fromName The name of the peripheral/inventory to pull from. This is the string given to @{peripheral.wrap},
|
||||
* and displayed by the wired modem.
|
||||
* @param fromSlot The slot in the source inventory to move items from.
|
||||
* @param limit The maximum number of items to move. Defaults to the current stack limit.
|
||||
* @param toSlot The slot in current inventory to move to. If not given, the item will be inserted into any slot.
|
||||
* @return The number of transferred items.
|
||||
* @throws LuaException If the peripheral to transfer to doesn't exist or isn't an inventory.
|
||||
* @throws LuaException If either source or destination slot is out of range.
|
||||
* @cc.see peripheral.getName Allows you to get the name of a @{peripheral.wrap|wrapped} peripheral.
|
||||
* @cc.usage Wrap two chests, and push an item from one to another.
|
||||
* <pre>{@code
|
||||
* local chest_a = peripheral.wrap("minecraft:chest_0")
|
||||
* local chest_b = peripheral.wrap("minecraft:chest_1")
|
||||
*
|
||||
* chest_a.pullItems(peripheral.getName(chest_b), 1)
|
||||
* }</pre>
|
||||
*/
|
||||
@LuaFunction( mainThread = true )
|
||||
public static int pullItems(
|
||||
Inventory to, IComputerAccess computer,
|
||||
String fromName, int fromSlot, Optional<Integer> limit, Optional<Integer> toSlot
|
||||
) throws LuaException
|
||||
{
|
||||
// Get appropriate inventory for source peripheral
|
||||
to = extractHandler(to);
|
||||
|
||||
// Find location to transfer to
|
||||
IPeripheral location = computer.getAvailablePeripheral( fromName );
|
||||
if( location == null ) throw new LuaException( "Source '" + fromName + "' does not exist" );
|
||||
|
||||
Inventory from = extractHandler( location.getTarget() );
|
||||
if( from == null ) throw new LuaException( "Source '" + fromName + "' is not an inventory" );
|
||||
|
||||
// Validate slots
|
||||
int actualLimit = limit.orElse( Integer.MAX_VALUE );
|
||||
assertBetween( fromSlot, 1, from.size(), "From slot out of range (%s)" );
|
||||
if( toSlot.isPresent() ) assertBetween( toSlot.get(), 1, to.size(), "To slot out of range (%s)" );
|
||||
|
||||
if( actualLimit <= 0 ) return 0;
|
||||
return moveItem( from, fromSlot - 1, to, toSlot.orElse( 0 ) - 1, actualLimit );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Extracts the most appropriate inventory from the object
|
||||
* e.g., the correct inventory for a double chest or a sided inventory.
|
||||
*
|
||||
* @param object The handler to move from.
|
||||
* @return The appropriate Inventory.
|
||||
*/
|
||||
@Nullable
|
||||
private static Inventory extractHandler( @Nullable Object object )
|
||||
{
|
||||
Inventory inventory = null;
|
||||
|
||||
if (object instanceof BlockEntity ) {
|
||||
BlockEntity blockEntity = (BlockEntity) object;
|
||||
World world = blockEntity.getWorld();
|
||||
BlockPos blockPos = blockEntity.getPos();
|
||||
BlockState blockState = world.getBlockState(blockPos);
|
||||
Block block = blockState.getBlock();
|
||||
|
||||
if (block instanceof InventoryProvider) {
|
||||
inventory = ((InventoryProvider)block).getInventory(blockState, world, blockPos);
|
||||
} else if (blockEntity instanceof Inventory) {
|
||||
inventory = (Inventory)blockEntity;
|
||||
if (inventory instanceof ChestBlockEntity && block instanceof ChestBlock) {
|
||||
inventory = ChestBlock.getInventory((ChestBlock) block, blockState, world, blockPos, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return inventory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move an item from one handler to another.
|
||||
*
|
||||
* @param from The handler to move from.
|
||||
* @param fromSlot The slot to move from.
|
||||
* @param to The handler to move to.
|
||||
* @param toSlot The slot to move to. Use any number < 0 to represent any slot.
|
||||
* @param limit The max number to move. {@link Integer#MAX_VALUE} for no limit.
|
||||
* @return The number of items moved.
|
||||
*/
|
||||
private static int moveItem( Inventory from, int fromSlot, Inventory to, int toSlot, final int limit )
|
||||
{
|
||||
|
||||
/* ORIGINAL FORGE CODE
|
||||
// See how much we can get out of this slot
|
||||
// ItemStack extracted = from.extractItem( fromSlot, limit, true );
|
||||
if( extracted.isEmpty() ) return 0;
|
||||
|
||||
// Limit the amount to extract
|
||||
int extractCount = Math.min( extracted.getCount(), limit );
|
||||
extracted.setCount( extractCount );
|
||||
|
||||
// ItemStack remainder = toSlot < 0 ? bItemHandlerHelper.insertItem( to, extracted, false ) : to.insertItem( toSlot, extracted, false );
|
||||
int inserted = remainder.isEmpty() ? extractCount : extractCount - remainder.getCount();
|
||||
if( inserted <= 0 ) return 0;
|
||||
|
||||
// Remove the item from the original inventory. Technically this could fail, but there's little we can do
|
||||
// about that.
|
||||
from.extractItem( fromSlot, inserted, false );
|
||||
*/
|
||||
|
||||
// Vanilla minecraft inventory manipulation code
|
||||
Boolean recurse = false;
|
||||
ItemStack source = from.getStack( fromSlot );
|
||||
int count = 0;
|
||||
|
||||
// If target slot was selected, only push items to that slot.
|
||||
if (toSlot >= 0) {
|
||||
int space = amountStackCanAddFrom(to.getStack(toSlot), source, to);
|
||||
if (space == 0) return 0;
|
||||
count = space;
|
||||
}
|
||||
// If target slot not selected, push items where they will fit, possibly
|
||||
// across slots (by recurring on this method).
|
||||
else if (toSlot < 0) {
|
||||
recurse = true;
|
||||
int[] result = getFirstValidSlotAndSpace(source, to);
|
||||
toSlot = result[0];
|
||||
if(toSlot < 0) return 0;
|
||||
count = result[1];
|
||||
}
|
||||
|
||||
// Respect slot restrictions
|
||||
if (!to.isValid(toSlot, source)) { return 0; }
|
||||
|
||||
// Compare count available in target ItemStack to limit specified.
|
||||
count = Math.min(count, limit);
|
||||
if (count == 0) return 0;
|
||||
|
||||
// Mutate destination and source ItemStack
|
||||
ItemStack destination = to.getStack(toSlot);
|
||||
if (destination.isEmpty()) {
|
||||
ItemStack newStack = source.copy();
|
||||
newStack.setCount(count);
|
||||
to.setStack(toSlot, newStack);
|
||||
} else {
|
||||
destination.increment(count);
|
||||
}
|
||||
source.decrement(count);
|
||||
if (source.isEmpty()) from.setStack(fromSlot, ItemStack.EMPTY);
|
||||
|
||||
to.markDirty();
|
||||
from.markDirty();
|
||||
|
||||
// Recurse if no explicit destination slot and more items exist in source slot
|
||||
// and limit hasn't been reached. Else, return items moved.
|
||||
if (recurse && !source.isEmpty()) return count + moveItem(from, fromSlot, to, -1, limit - count);
|
||||
return count;
|
||||
}
|
||||
|
||||
// Maybe there is a nicer existing way to do this in the minecraft codebase. I couldn't find it.
|
||||
private static int[] getFirstValidSlotAndSpace(ItemStack fromStack, Inventory inventory) {
|
||||
for (int i = 0; i < inventory.size(); i++) {
|
||||
ItemStack stack = inventory.getStack(i);
|
||||
int space = amountStackCanAddFrom(stack, fromStack, inventory);
|
||||
if (space > 0) {
|
||||
return new int[]{i, space};
|
||||
}
|
||||
}
|
||||
return new int[]{-1, 0};
|
||||
}
|
||||
|
||||
private static int amountStackCanAddFrom(ItemStack existingStack, ItemStack fromStack, Inventory inventory) {
|
||||
if (fromStack.isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
else if (existingStack.isEmpty()) {
|
||||
return Math.min(Math.min(existingStack.getMaxCount(),
|
||||
inventory.getMaxCountPerStack()),
|
||||
fromStack.getCount());
|
||||
}
|
||||
else if (InventoryMethods.areItemsEqual(existingStack, fromStack) &&
|
||||
existingStack.isStackable() &&
|
||||
existingStack.getCount() < existingStack.getMaxCount() &&
|
||||
existingStack.getCount() < inventory.getMaxCountPerStack()) {
|
||||
int stackSpace = existingStack.getMaxCount() - existingStack.getCount();
|
||||
int invSpace = inventory.getMaxCountPerStack() - existingStack.getCount();
|
||||
return Math.min(Math.min(stackSpace, invSpace), fromStack.getCount());
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static boolean areItemsEqual(ItemStack stack1, ItemStack stack2) {
|
||||
return stack1.getItem() == stack2.getItem() && ItemStack.areTagsEqual(stack1, stack2);
|
||||
}
|
||||
}
|
@ -181,17 +181,19 @@ public class BlockCable extends BlockGeneric implements Waterloggable {
|
||||
// : new ItemStack( ComputerCraftRegistry.ModItems.CABLE.get() );
|
||||
// }
|
||||
|
||||
@Override
|
||||
@Deprecated
|
||||
public boolean canPlaceAt(BlockState state, @Nonnull WorldView world, @Nonnull BlockPos pos) {
|
||||
Direction facing = state.get(MODEM)
|
||||
.getFacing();
|
||||
if (facing == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return sideCoversSmallSquare(world, pos.offset(facing), facing.getOpposite());
|
||||
}
|
||||
// Commenting override to allow cable modems to be placed on chests, so that chests can be generic inventory peripherals.
|
||||
// TODO Perhaps there is a more selective way to achieve this?
|
||||
// @Override
|
||||
// @Deprecated
|
||||
// public boolean canPlaceAt(BlockState state, @Nonnull WorldView world, @Nonnull BlockPos pos) {
|
||||
// Direction facing = state.get(MODEM)
|
||||
// .getFacing();
|
||||
// if (facing == null) {
|
||||
// return true;
|
||||
// }
|
||||
//
|
||||
// return sideCoversSmallSquare(world, pos.offset(facing), facing.getOpposite());
|
||||
// }
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
|
@ -18,6 +18,7 @@ import dan200.computercraft.api.turtle.TurtleCommandResult;
|
||||
import dan200.computercraft.api.turtle.event.TurtleBlockEvent;
|
||||
import dan200.computercraft.api.turtle.event.TurtleEvent;
|
||||
|
||||
import dan200.computercraft.shared.peripheral.generic.data.BlockData;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.state.property.Property;
|
||||
@ -49,20 +50,7 @@ public class TurtleInspectCommand implements ITurtleCommand {
|
||||
return TurtleCommandResult.failure("No block to inspect");
|
||||
}
|
||||
|
||||
Block block = state.getBlock();
|
||||
String name = Registry.BLOCK.getId(block)
|
||||
.toString();
|
||||
|
||||
Map<String, Object> table = new HashMap<>();
|
||||
table.put("name", name);
|
||||
|
||||
Map<Object, Object> stateTable = new HashMap<>();
|
||||
for (ImmutableMap.Entry<Property<?>, ? extends Comparable<?>> entry : state.getEntries()
|
||||
.entrySet()) {
|
||||
Property<?> property = entry.getKey();
|
||||
stateTable.put(property.getName(), getPropertyValue(property, entry.getValue()));
|
||||
}
|
||||
table.put("state", stateTable);
|
||||
Map<String, Object> table = BlockData.fill( new HashMap<>(), state );
|
||||
|
||||
// Fire the event, exiting if it is cancelled
|
||||
TurtlePlayer turtlePlayer = TurtlePlaceCommand.createPlayer(turtle, oldPosition, direction);
|
||||
@ -72,7 +60,6 @@ public class TurtleInspectCommand implements ITurtleCommand {
|
||||
}
|
||||
|
||||
return TurtleCommandResult.success(new Object[] {table});
|
||||
|
||||
}
|
||||
|
||||
@SuppressWarnings ({
|
||||
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* 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.util;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.ServiceLoader;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
public final class ServiceUtil
|
||||
{
|
||||
private static final Type AUTO_SERVICE = Type.getType( "Lcom/google/auto/service/AutoService;" );
|
||||
|
||||
private ServiceUtil()
|
||||
{
|
||||
}
|
||||
|
||||
public static <T> Stream<T> loadServices( Class<T> target )
|
||||
{
|
||||
return StreamSupport.stream( ServiceLoader.load( target, ServiceUtil.class.getClassLoader() ).spliterator(), false );
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user