mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-10-24 18:37:38 +00:00
Merge branch 'mc-1.15.x' into mc-1.16.x
This commit is contained in:
@@ -22,30 +22,17 @@ import net.minecraftforge.fml.common.Mod;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
@Mod( ComputerCraft.MOD_ID )
|
||||
public final class ComputerCraft
|
||||
{
|
||||
public static final String MOD_ID = "computercraft";
|
||||
|
||||
// Configuration options
|
||||
public static final String[] DEFAULT_HTTP_ALLOW = new String[] { "*" };
|
||||
public static final String[] DEFAULT_HTTP_DENY = new String[] {
|
||||
"127.0.0.0/8",
|
||||
"0.0.0.0/8",
|
||||
"10.0.0.0/8",
|
||||
"172.16.0.0/12",
|
||||
"192.168.0.0/16",
|
||||
"fd00::/8",
|
||||
};
|
||||
|
||||
public static int computerSpaceLimit = 1000 * 1000;
|
||||
public static int floppySpaceLimit = 125 * 1000;
|
||||
public static int maximumFilesOpen = 128;
|
||||
@@ -61,14 +48,10 @@ public final class ComputerCraft
|
||||
|
||||
public static boolean httpEnabled = true;
|
||||
public static boolean httpWebsocketEnabled = true;
|
||||
public static List<AddressRule> httpRules = Collections.unmodifiableList( Stream.concat(
|
||||
Stream.of( DEFAULT_HTTP_DENY )
|
||||
.map( x -> AddressRule.parse( x, null, Action.DENY.toPartial() ) )
|
||||
.filter( Objects::nonNull ),
|
||||
Stream.of( DEFAULT_HTTP_ALLOW )
|
||||
.map( x -> AddressRule.parse( x, null, Action.ALLOW.toPartial() ) )
|
||||
.filter( Objects::nonNull )
|
||||
).collect( Collectors.toList() ) );
|
||||
public static List<AddressRule> httpRules = Collections.unmodifiableList( Arrays.asList(
|
||||
AddressRule.parse( "$private", null, Action.DENY.toPartial() ),
|
||||
AddressRule.parse( "*", null, Action.ALLOW.toPartial() )
|
||||
) );
|
||||
|
||||
public static int httpMaxRequests = 16;
|
||||
public static int httpMaxWebsockets = 4;
|
||||
|
||||
86
src/main/java/dan200/computercraft/api/IUpgradeBase.java
Normal file
86
src/main/java/dan200/computercraft/api/IUpgradeBase.java
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* This file is part of the public ComputerCraft API - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2020. This API may be redistributed unmodified and in full only.
|
||||
* For help using the API, and posting your mods, visit the forums at computercraft.info.
|
||||
*/
|
||||
package dan200.computercraft.api;
|
||||
|
||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Common functionality between {@link ITurtleUpgrade} and {@link IPocketUpgrade}.
|
||||
*/
|
||||
public interface IUpgradeBase
|
||||
{
|
||||
/**
|
||||
* Gets a unique identifier representing this type of turtle upgrade. eg: "computercraft:wireless_modem"
|
||||
* or "my_mod:my_upgrade".
|
||||
*
|
||||
* You should use a unique resource domain to ensure this upgrade is uniquely identified.
|
||||
* The upgrade will fail registration if an already used ID is specified.
|
||||
*
|
||||
* @return The unique ID for this upgrade.
|
||||
*/
|
||||
@Nonnull
|
||||
ResourceLocation getUpgradeID();
|
||||
|
||||
/**
|
||||
* Return an unlocalised string to describe this type of computer in item names.
|
||||
*
|
||||
* Examples of built-in adjectives are "Wireless", "Mining" and "Crafty".
|
||||
*
|
||||
* @return The localisation key for this upgrade's adjective.
|
||||
*/
|
||||
@Nonnull
|
||||
String getUnlocalisedAdjective();
|
||||
|
||||
/**
|
||||
* Return an item stack representing the type of item that a computer must be crafted
|
||||
* with to create a version which holds this upgrade. This item stack is also used
|
||||
* to determine the upgrade given by {@code turtle.equipLeft()} or {@code pocket.equipBack()}
|
||||
*
|
||||
* This should be constant over a session (or at least a datapack reload). It is recommended
|
||||
* that you cache the stack too, in order to prevent constructing it every time the method
|
||||
* is called.
|
||||
*
|
||||
* @return The item stack to craft with, or {@link ItemStack#EMPTY} if it cannot be crafted.
|
||||
*/
|
||||
@Nonnull
|
||||
ItemStack getCraftingItem();
|
||||
|
||||
/**
|
||||
* Determine if an item is suitable for being used for this upgrade.
|
||||
*
|
||||
* When un-equipping an upgrade, we return {@link #getCraftingItem()} rather than
|
||||
* the original stack. In order to prevent people losing items with enchantments (or
|
||||
* repairing items with non-0 damage), we impose additional checks on the item.
|
||||
*
|
||||
* The default check requires that any non-capability NBT is exactly the same as the
|
||||
* crafting item, but this may be relaxed for your upgrade.
|
||||
*
|
||||
* @param stack The stack to check. This is guaranteed to be non-empty and have the same item as
|
||||
* {@link #getCraftingItem()}.
|
||||
* @return If this stack may be used to equip this upgrade.
|
||||
* @see net.minecraftforge.common.crafting.NBTIngredient#test(ItemStack) For the implementation of the default
|
||||
* check.
|
||||
*/
|
||||
default boolean isItemSuitable( @Nonnull ItemStack stack )
|
||||
{
|
||||
ItemStack crafting = getCraftingItem();
|
||||
|
||||
// A more expanded form of ItemStack.areShareTagsEqual, but allowing an empty tag to be equal to a
|
||||
// null one.
|
||||
CompoundNBT shareTag = stack.getItem().getShareTag( stack );
|
||||
CompoundNBT craftingShareTag = crafting.getItem().getShareTag( crafting );
|
||||
if( shareTag == craftingShareTag ) return true;
|
||||
if( shareTag == null ) return craftingShareTag.isEmpty();
|
||||
if( craftingShareTag == null ) return shareTag.isEmpty();
|
||||
return shareTag.equals( craftingShareTag );
|
||||
}
|
||||
}
|
||||
@@ -6,10 +6,8 @@
|
||||
package dan200.computercraft.api.pocket;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.IUpgradeBase;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
@@ -18,49 +16,10 @@ import javax.annotation.Nullable;
|
||||
/**
|
||||
* Additional peripherals for pocket computers.
|
||||
*
|
||||
* This is similar to {@link ITurtleUpgrade}.
|
||||
* @see ComputerCraftAPI#registerPocketUpgrade(IPocketUpgrade)
|
||||
*/
|
||||
public interface IPocketUpgrade
|
||||
public interface IPocketUpgrade extends IUpgradeBase
|
||||
{
|
||||
|
||||
/**
|
||||
* Gets a unique identifier representing this type of turtle upgrade. eg: "computercraft:wireless_modem" or
|
||||
* "my_mod:my_upgrade".
|
||||
*
|
||||
* You should use a unique resource domain to ensure this upgrade is uniquely identified. The upgrade will fail
|
||||
* registration if an already used ID is specified.
|
||||
*
|
||||
* @return The upgrade's id.
|
||||
* @see IPocketUpgrade#getUpgradeID()
|
||||
* @see ComputerCraftAPI#registerPocketUpgrade(IPocketUpgrade)
|
||||
*/
|
||||
@Nonnull
|
||||
ResourceLocation getUpgradeID();
|
||||
|
||||
/**
|
||||
* Return an unlocalised string to describe the type of pocket computer this upgrade provides.
|
||||
*
|
||||
* An example of a built-in adjectives is "Wireless" - this is converted to "Wireless Pocket Computer".
|
||||
*
|
||||
* @return The unlocalised adjective.
|
||||
* @see ITurtleUpgrade#getUnlocalisedAdjective()
|
||||
*/
|
||||
@Nonnull
|
||||
String getUnlocalisedAdjective();
|
||||
|
||||
/**
|
||||
* Return an item stack representing the type of item that a pocket computer must be crafted with to create a
|
||||
* pocket computer which holds this upgrade. This item stack is also used to determine the upgrade given by
|
||||
* {@code pocket.equip()}/{@code pocket.unequip()}.
|
||||
*
|
||||
* Ideally this should be constant over a session. It is recommended that you cache
|
||||
* the item too, in order to prevent constructing it every time the method is called.
|
||||
*
|
||||
* @return The item stack used for crafting. This can be {@link ItemStack#EMPTY} if crafting is disabled.
|
||||
*/
|
||||
@Nonnull
|
||||
ItemStack getCraftingItem();
|
||||
|
||||
/**
|
||||
* Creates a peripheral for the pocket computer.
|
||||
*
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
package dan200.computercraft.api.turtle;
|
||||
|
||||
import dan200.computercraft.api.ComputerCraftAPI;
|
||||
import dan200.computercraft.api.IUpgradeBase;
|
||||
import dan200.computercraft.api.client.TransformedModel;
|
||||
import dan200.computercraft.api.peripheral.IPeripheral;
|
||||
import dan200.computercraft.api.turtle.event.TurtleAttackEvent;
|
||||
@@ -13,7 +14,6 @@ import dan200.computercraft.api.turtle.event.TurtleBlockEvent;
|
||||
import net.minecraft.client.renderer.model.ModelResourceLocation;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.event.entity.player.AttackEntityEvent;
|
||||
@@ -28,29 +28,8 @@ import javax.annotation.Nullable;
|
||||
*
|
||||
* @see ComputerCraftAPI#registerTurtleUpgrade(ITurtleUpgrade)
|
||||
*/
|
||||
public interface ITurtleUpgrade
|
||||
public interface ITurtleUpgrade extends IUpgradeBase
|
||||
{
|
||||
/**
|
||||
* Gets a unique identifier representing this type of turtle upgrade. eg: "computercraft:wireless_modem" or "my_mod:my_upgrade".
|
||||
* You should use a unique resource domain to ensure this upgrade is uniquely identified.
|
||||
* The turtle will fail registration if an already used ID is specified.
|
||||
*
|
||||
* @return The unique ID for this upgrade.
|
||||
* @see ComputerCraftAPI#registerTurtleUpgrade(ITurtleUpgrade)
|
||||
*/
|
||||
@Nonnull
|
||||
ResourceLocation getUpgradeID();
|
||||
|
||||
/**
|
||||
* Return an unlocalised string to describe this type of turtle in turtle item names.
|
||||
*
|
||||
* Examples of built-in adjectives are "Wireless", "Mining" and "Crafty".
|
||||
*
|
||||
* @return The localisation key for this upgrade's adjective.
|
||||
*/
|
||||
@Nonnull
|
||||
String getUnlocalisedAdjective();
|
||||
|
||||
/**
|
||||
* Return whether this turtle adds a tool or a peripheral to the turtle.
|
||||
*
|
||||
@@ -60,19 +39,6 @@ public interface ITurtleUpgrade
|
||||
@Nonnull
|
||||
TurtleUpgradeType getType();
|
||||
|
||||
/**
|
||||
* Return an item stack representing the type of item that a turtle must be crafted
|
||||
* with to create a turtle which holds this upgrade. This item stack is also used
|
||||
* to determine the upgrade given by {@code turtle.equip()}
|
||||
*
|
||||
* Ideally this should be constant over a session. It is recommended that you cache
|
||||
* the item too, in order to prevent constructing it every time the method is called.
|
||||
*
|
||||
* @return The item stack to craft with, or {@link ItemStack#EMPTY} if it cannot be crafted.
|
||||
*/
|
||||
@Nonnull
|
||||
ItemStack getCraftingItem();
|
||||
|
||||
/**
|
||||
* Will only be called for peripheral upgrades. Creates a peripheral for a turtle being placed using this upgrade.
|
||||
*
|
||||
|
||||
@@ -55,7 +55,6 @@ public final class ComputerCraftProxyClient
|
||||
ClientRegistry.bindTileEntityRenderer( Registry.ModTiles.MONITOR_ADVANCED.get(), TileEntityMonitorRenderer::new );
|
||||
ClientRegistry.bindTileEntityRenderer( Registry.ModTiles.TURTLE_NORMAL.get(), TileEntityTurtleRenderer::new );
|
||||
ClientRegistry.bindTileEntityRenderer( Registry.ModTiles.TURTLE_ADVANCED.get(), TileEntityTurtleRenderer::new );
|
||||
// TODO: ClientRegistry.bindTileEntityRenderer( TileCable.FACTORY, x -> new TileEntityCableRenderer() );
|
||||
|
||||
RenderingRegistry.registerEntityRenderingHandler( Registry.ModEntities.TURTLE_PLAYER.get(), TurtlePlayerRenderer::new );
|
||||
|
||||
|
||||
@@ -50,7 +50,6 @@ class MonitorTextureBufferShader
|
||||
RenderSystem.glUniform1i( uniformWidth, width );
|
||||
RenderSystem.glUniform1i( uniformHeight, height );
|
||||
|
||||
// TODO: Cache this? Maybe??
|
||||
PALETTE_BUFFER.rewind();
|
||||
for( int i = 0; i < 16; i++ )
|
||||
{
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
package dan200.computercraft.core.apis;
|
||||
|
||||
import dan200.computercraft.api.lua.IArguments;
|
||||
import dan200.computercraft.api.lua.ILuaAPI;
|
||||
import dan200.computercraft.api.lua.LuaException;
|
||||
import dan200.computercraft.api.lua.LuaFunction;
|
||||
@@ -83,17 +84,29 @@ public class FSAPI implements ILuaAPI
|
||||
}
|
||||
|
||||
/**
|
||||
* Combines two parts of a path into one full path, adding separators as
|
||||
* Combines several parts of a path into one full path, adding separators as
|
||||
* needed.
|
||||
*
|
||||
* @param pathA The first part of the path. For example, a parent directory path.
|
||||
* @param pathB The second part of the path. For example, a file name.
|
||||
* @param arguments The paths to combine.
|
||||
* @return The new path, with separators added between parts as needed.
|
||||
* @cc.tparam string path The first part of the path. For example, a parent directory path.
|
||||
* @cc.tparam string ... Additional parts of the path to combine.
|
||||
* @throws LuaException On argument errors.
|
||||
*/
|
||||
@LuaFunction
|
||||
public final String combine( String pathA, String pathB )
|
||||
public final String combine( IArguments arguments ) throws LuaException
|
||||
{
|
||||
return fileSystem.combine( pathA, pathB );
|
||||
StringBuilder result = new StringBuilder();
|
||||
result.append( FileSystem.sanitizePath( arguments.getString( 0 ), true ) );
|
||||
|
||||
for( int i = 1, n = arguments.count(); i < n; i++ )
|
||||
{
|
||||
String part = FileSystem.sanitizePath( arguments.getString( i ), true );
|
||||
if( result.length() != 0 && !part.isEmpty() ) result.append( '/' );
|
||||
result.append( part );
|
||||
}
|
||||
|
||||
return FileSystem.sanitizePath( result.toString(), true );
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -381,11 +381,11 @@ public class OSAPI implements ILuaAPI
|
||||
* Returns the number of milliseconds since an epoch depending on the locale.
|
||||
*
|
||||
* * If called with {@code ingame}, returns the number of milliseconds since the
|
||||
* world was created. This is the default.
|
||||
* world was created. This is the default.
|
||||
* * If called with {@code utc}, returns the number of milliseconds since 1
|
||||
* January 1970 in the UTC timezone.
|
||||
* January 1970 in the UTC timezone.
|
||||
* * If called with {@code local}, returns the number of milliseconds since 1
|
||||
* January 1970 in the server's local timezone.
|
||||
* January 1970 in the server's local timezone.
|
||||
*
|
||||
* @param args The locale to get the milliseconds for. Defaults to {@code ingame} if not set.
|
||||
* @return The milliseconds since the epoch depending on the selected locale.
|
||||
@@ -412,7 +412,7 @@ public class OSAPI implements ILuaAPI
|
||||
// Get in-game epoch
|
||||
synchronized( m_alarms )
|
||||
{
|
||||
return m_day * 86400000 + (int) (m_time * 3600000.0f);
|
||||
return m_day * 86400000L + (long) (m_time * 3600000.0);
|
||||
}
|
||||
default:
|
||||
throw new LuaException( "Unsupported operation" );
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
|
||||
package dan200.computercraft.core.apis.http.options;
|
||||
|
||||
import com.google.common.net.InetAddresses;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* A predicate on an address. Matches against a domain and an ip address.
|
||||
*
|
||||
* @see AddressRule#apply(Iterable, String, InetSocketAddress) for the actual handling of this rule.
|
||||
*/
|
||||
interface AddressPredicate
|
||||
{
|
||||
default boolean matches( String domain )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
default boolean matches( InetAddress socketAddress )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
final class HostRange implements AddressPredicate
|
||||
{
|
||||
private final byte[] min;
|
||||
private final byte[] max;
|
||||
|
||||
HostRange( byte[] min, byte[] max )
|
||||
{
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches( InetAddress address )
|
||||
{
|
||||
byte[] entry = address.getAddress();
|
||||
if( entry.length != min.length ) return false;
|
||||
|
||||
for( int i = 0; i < entry.length; i++ )
|
||||
{
|
||||
int value = 0xFF & entry[i];
|
||||
if( value < (0xFF & min[i]) || value > (0xFF & max[i]) ) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static HostRange parse( String addressStr, String prefixSizeStr )
|
||||
{
|
||||
int prefixSize;
|
||||
try
|
||||
{
|
||||
prefixSize = Integer.parseInt( prefixSizeStr );
|
||||
}
|
||||
catch( NumberFormatException e )
|
||||
{
|
||||
ComputerCraft.log.error(
|
||||
"Malformed http whitelist/blacklist entry '{}': Cannot extract size of CIDR mask from '{}'.",
|
||||
addressStr + '/' + prefixSizeStr, prefixSizeStr
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
InetAddress address;
|
||||
try
|
||||
{
|
||||
address = InetAddresses.forString( addressStr );
|
||||
}
|
||||
catch( IllegalArgumentException e )
|
||||
{
|
||||
ComputerCraft.log.error(
|
||||
"Malformed http whitelist/blacklist entry '{}': Cannot extract IP address from '{}'.",
|
||||
addressStr + '/' + prefixSizeStr, prefixSizeStr
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Mask the bytes of the IP address.
|
||||
byte[] minBytes = address.getAddress(), maxBytes = address.getAddress();
|
||||
int size = prefixSize;
|
||||
for( int i = 0; i < minBytes.length; i++ )
|
||||
{
|
||||
if( size <= 0 )
|
||||
{
|
||||
minBytes[i] &= 0;
|
||||
maxBytes[i] |= 0xFF;
|
||||
}
|
||||
else if( size < 8 )
|
||||
{
|
||||
minBytes[i] &= 0xFF << (8 - size);
|
||||
maxBytes[i] |= ~(0xFF << (8 - size));
|
||||
}
|
||||
|
||||
size -= 8;
|
||||
}
|
||||
|
||||
return new HostRange( minBytes, maxBytes );
|
||||
}
|
||||
}
|
||||
|
||||
final class DomainPattern implements AddressPredicate
|
||||
{
|
||||
private final Pattern pattern;
|
||||
|
||||
DomainPattern( Pattern pattern )
|
||||
{
|
||||
this.pattern = pattern;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches( String domain )
|
||||
{
|
||||
return pattern.matcher( domain ).matches();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches( InetAddress socketAddress )
|
||||
{
|
||||
return pattern.matcher( socketAddress.getHostAddress() ).matches();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
final class PrivatePattern implements AddressPredicate
|
||||
{
|
||||
static final PrivatePattern INSTANCE = new PrivatePattern();
|
||||
|
||||
@Override
|
||||
public boolean matches( InetAddress socketAddress )
|
||||
{
|
||||
return socketAddress.isAnyLocalAddress()
|
||||
|| socketAddress.isLoopbackAddress()
|
||||
|| socketAddress.isLinkLocalAddress()
|
||||
|| socketAddress.isSiteLocalAddress();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,10 +6,13 @@
|
||||
package dan200.computercraft.core.apis.http.options;
|
||||
|
||||
import com.google.common.net.InetAddresses;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.core.apis.http.options.AddressPredicate.DomainPattern;
|
||||
import dan200.computercraft.core.apis.http.options.AddressPredicate.HostRange;
|
||||
import dan200.computercraft.core.apis.http.options.AddressPredicate.PrivatePattern;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.net.Inet4Address;
|
||||
import java.net.Inet6Address;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
@@ -25,45 +28,13 @@ public final class AddressRule
|
||||
public static final int TIMEOUT = 30_000;
|
||||
public static final int WEBSOCKET_MESSAGE = 128 * 1024;
|
||||
|
||||
private static final class HostRange
|
||||
{
|
||||
private final byte[] min;
|
||||
private final byte[] max;
|
||||
|
||||
private HostRange( byte[] min, byte[] max )
|
||||
{
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
public boolean contains( InetAddress address )
|
||||
{
|
||||
byte[] entry = address.getAddress();
|
||||
if( entry.length != min.length ) return false;
|
||||
|
||||
for( int i = 0; i < entry.length; i++ )
|
||||
{
|
||||
int value = 0xFF & entry[i];
|
||||
if( value < (0xFF & min[i]) || value > (0xFF & max[i]) ) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private final HostRange ip;
|
||||
private final Pattern domainPattern;
|
||||
private final AddressPredicate predicate;
|
||||
private final Integer port;
|
||||
private final PartialOptions partial;
|
||||
|
||||
private AddressRule(
|
||||
@Nullable HostRange ip,
|
||||
@Nullable Pattern domainPattern,
|
||||
@Nullable Integer port,
|
||||
@Nonnull PartialOptions partial )
|
||||
private AddressRule( @Nonnull AddressPredicate predicate, @Nullable Integer port, @Nonnull PartialOptions partial )
|
||||
{
|
||||
this.ip = ip;
|
||||
this.domainPattern = domainPattern;
|
||||
this.predicate = predicate;
|
||||
this.partial = partial;
|
||||
this.port = port;
|
||||
}
|
||||
@@ -76,103 +47,50 @@ public final class AddressRule
|
||||
{
|
||||
String addressStr = filter.substring( 0, cidr );
|
||||
String prefixSizeStr = filter.substring( cidr + 1 );
|
||||
|
||||
int prefixSize;
|
||||
try
|
||||
{
|
||||
prefixSize = Integer.parseInt( prefixSizeStr );
|
||||
}
|
||||
catch( NumberFormatException e )
|
||||
{
|
||||
ComputerCraft.log.error(
|
||||
"Malformed http whitelist/blacklist entry '{}': Cannot extract size of CIDR mask from '{}'.",
|
||||
filter, prefixSizeStr
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
InetAddress address;
|
||||
try
|
||||
{
|
||||
address = InetAddresses.forString( addressStr );
|
||||
}
|
||||
catch( IllegalArgumentException e )
|
||||
{
|
||||
ComputerCraft.log.error(
|
||||
"Malformed http whitelist/blacklist entry '{}': Cannot extract IP address from '{}'.",
|
||||
filter, prefixSizeStr
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Mask the bytes of the IP address.
|
||||
byte[] minBytes = address.getAddress(), maxBytes = address.getAddress();
|
||||
int size = prefixSize;
|
||||
for( int i = 0; i < minBytes.length; i++ )
|
||||
{
|
||||
if( size <= 0 )
|
||||
{
|
||||
minBytes[i] &= 0;
|
||||
maxBytes[i] |= 0xFF;
|
||||
}
|
||||
else if( size < 8 )
|
||||
{
|
||||
minBytes[i] &= 0xFF << (8 - size);
|
||||
maxBytes[i] |= ~(0xFF << (8 - size));
|
||||
}
|
||||
|
||||
size -= 8;
|
||||
}
|
||||
|
||||
return new AddressRule( new HostRange( minBytes, maxBytes ), null, port, partial );
|
||||
HostRange range = HostRange.parse( addressStr, prefixSizeStr );
|
||||
return range == null ? null : new AddressRule( range, port, partial );
|
||||
}
|
||||
else if( filter.equalsIgnoreCase( "$private" ) )
|
||||
{
|
||||
return new AddressRule( PrivatePattern.INSTANCE, port, partial );
|
||||
}
|
||||
else
|
||||
{
|
||||
Pattern pattern = Pattern.compile( "^\\Q" + filter.replaceAll( "\\*", "\\\\E.*\\\\Q" ) + "\\E$" );
|
||||
return new AddressRule( null, pattern, port, partial );
|
||||
Pattern pattern = Pattern.compile( "^\\Q" + filter.replaceAll( "\\*", "\\\\E.*\\\\Q" ) + "\\E$", Pattern.CASE_INSENSITIVE );
|
||||
return new AddressRule( new DomainPattern( pattern ), port, partial );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine whether the given address matches a series of patterns.
|
||||
*
|
||||
* @param domain The domain to match
|
||||
* @param socketAddress The address to check.
|
||||
* @param domain The domain to match
|
||||
* @param port The port of the address.
|
||||
* @param address The address to check.
|
||||
* @param ipv4Address An ipv4 version of the address, if the original was an ipv6 address.
|
||||
* @return Whether it matches any of these patterns.
|
||||
*/
|
||||
private boolean matches( String domain, InetSocketAddress socketAddress )
|
||||
private boolean matches( String domain, int port, InetAddress address, Inet4Address ipv4Address )
|
||||
{
|
||||
InetAddress address = socketAddress.getAddress();
|
||||
if( port != null && port != socketAddress.getPort() ) return false;
|
||||
|
||||
if( domainPattern != null )
|
||||
{
|
||||
if( domainPattern.matcher( domain ).matches() ) return true;
|
||||
if( domainPattern.matcher( address.getHostName() ).matches() ) return true;
|
||||
}
|
||||
|
||||
// Match the normal address
|
||||
if( matchesAddress( address ) ) return true;
|
||||
|
||||
// If we're an IPv4 address in disguise then let's check that.
|
||||
return address instanceof Inet6Address && InetAddresses.is6to4Address( (Inet6Address) address )
|
||||
&& matchesAddress( InetAddresses.get6to4IPv4Address( (Inet6Address) address ) );
|
||||
if( this.port != null && this.port != port ) return false;
|
||||
return predicate.matches( domain )
|
||||
|| predicate.matches( address )
|
||||
|| (ipv4Address != null && predicate.matches( ipv4Address ));
|
||||
}
|
||||
|
||||
private boolean matchesAddress( InetAddress address )
|
||||
{
|
||||
if( domainPattern != null && domainPattern.matcher( address.getHostAddress() ).matches() ) return true;
|
||||
return ip != null && ip.contains( address );
|
||||
}
|
||||
|
||||
public static Options apply( Iterable<? extends AddressRule> rules, String domain, InetSocketAddress address )
|
||||
public static Options apply( Iterable<? extends AddressRule> rules, String domain, InetSocketAddress socketAddress )
|
||||
{
|
||||
PartialOptions options = null;
|
||||
boolean hasMany = false;
|
||||
|
||||
int port = socketAddress.getPort();
|
||||
InetAddress address = socketAddress.getAddress();
|
||||
Inet4Address ipv4Address = address instanceof Inet6Address && InetAddresses.is6to4Address( (Inet6Address) address )
|
||||
? InetAddresses.get6to4IPv4Address( (Inet6Address) address ) : null;
|
||||
|
||||
for( AddressRule rule : rules )
|
||||
{
|
||||
if( !rule.matches( domain, address ) ) continue;
|
||||
if( !rule.matches( domain, port, address, ipv4Address ) ) continue;
|
||||
|
||||
if( options == null )
|
||||
{
|
||||
|
||||
@@ -21,10 +21,8 @@ import io.netty.channel.SimpleChannelInboundHandler;
|
||||
import io.netty.handler.codec.http.*;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.HashMap;
|
||||
@@ -244,9 +242,9 @@ public final class HttpRequestHandler extends SimpleChannelInboundHandler<HttpOb
|
||||
|
||||
try
|
||||
{
|
||||
return uri.resolve( new URI( URLDecoder.decode( location, "UTF-8" ) ) );
|
||||
return uri.resolve( new URI( location ) );
|
||||
}
|
||||
catch( UnsupportedEncodingException | IllegalArgumentException | URISyntaxException e )
|
||||
catch( IllegalArgumentException | URISyntaxException e )
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ public class FileSystem
|
||||
mounts.remove( sanitizePath( path ) );
|
||||
}
|
||||
|
||||
public synchronized String combine( String path, String childPath )
|
||||
public String combine( String path, String childPath )
|
||||
{
|
||||
path = sanitizePath( path, true );
|
||||
childPath = sanitizePath( childPath, true );
|
||||
@@ -479,7 +479,7 @@ public class FileSystem
|
||||
|
||||
private static final Pattern threeDotsPattern = Pattern.compile( "^\\.{3,}$" );
|
||||
|
||||
private static String sanitizePath( String path, boolean allowWildcards )
|
||||
public static String sanitizePath( String path, boolean allowWildcards )
|
||||
{
|
||||
// Allow windowsy slashes
|
||||
path = path.replace( '\\', '/' );
|
||||
|
||||
163
src/main/java/dan200/computercraft/data/BlockModelProvider.java
Normal file
163
src/main/java/dan200/computercraft/data/BlockModelProvider.java
Normal file
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* This file is part of ComputerCraft - http://www.computercraft.info
|
||||
* Copyright Daniel Ratcliffe, 2011-2020. Do not distribute without permission.
|
||||
* Send enquiries to dratcliffe@gmail.com
|
||||
*/
|
||||
|
||||
package dan200.computercraft.data;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.shared.Registry;
|
||||
import dan200.computercraft.shared.computer.blocks.BlockComputer;
|
||||
import dan200.computercraft.shared.computer.core.ComputerState;
|
||||
import dan200.computercraft.shared.peripheral.monitor.BlockMonitor;
|
||||
import dan200.computercraft.shared.peripheral.monitor.MonitorEdgeState;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.data.DataGenerator;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraftforge.client.model.generators.*;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class BlockModelProvider extends BlockStateProvider
|
||||
{
|
||||
private final ModelFile monitorBase;
|
||||
private final ModelFile orientable;
|
||||
|
||||
public BlockModelProvider( DataGenerator generator, ExistingFileHelper existingFileHelper )
|
||||
{
|
||||
super( generator, ComputerCraft.MOD_ID, existingFileHelper );
|
||||
monitorBase = models().getExistingFile( new ResourceLocation( ComputerCraft.MOD_ID, "block/monitor_base" ) );
|
||||
orientable = models().getExistingFile( new ResourceLocation( "block/orientable" ) );
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getName()
|
||||
{
|
||||
return "Block states and models";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void registerStatesAndModels()
|
||||
{
|
||||
registerMonitors( Registry.ModBlocks.MONITOR_NORMAL.get() );
|
||||
registerMonitors( Registry.ModBlocks.MONITOR_ADVANCED.get() );
|
||||
|
||||
registerComputer( Registry.ModBlocks.COMPUTER_NORMAL.get() );
|
||||
registerComputer( Registry.ModBlocks.COMPUTER_ADVANCED.get() );
|
||||
registerComputer( Registry.ModBlocks.COMPUTER_COMMAND.get() );
|
||||
}
|
||||
|
||||
private void registerComputer( BlockComputer block )
|
||||
{
|
||||
VariantBlockStateBuilder builder = getVariantBuilder( block );
|
||||
for( ComputerState state : BlockComputer.STATE.getAllowedValues() )
|
||||
{
|
||||
BlockModelBuilder model = models()
|
||||
.getBuilder( suffix( block, "_" + state ) )
|
||||
.parent( orientable )
|
||||
.texture( "top", suffix( block, "_top" ) )
|
||||
.texture( "side", suffix( block, "_side" ) )
|
||||
.texture( "front", suffix( block, "_front" + toSuffix( state ) ) );
|
||||
|
||||
for( Direction facing : BlockComputer.FACING.getAllowedValues() )
|
||||
{
|
||||
builder.partialState()
|
||||
.with( BlockComputer.STATE, state )
|
||||
.with( BlockComputer.FACING, facing )
|
||||
.addModels( new ConfiguredModel( model, 0, toYAngle( facing ), false ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void registerMonitors( BlockMonitor block )
|
||||
{
|
||||
String name = block.getRegistryName().getPath();
|
||||
registerMonitorModel( name, "", 16, 4, 0, 32 );
|
||||
registerMonitorModel( name, "_d", 20, 7, 0, 36 );
|
||||
registerMonitorModel( name, "_l", 19, 4, 1, 33 );
|
||||
registerMonitorModel( name, "_ld", 31, 7, 1, 45 );
|
||||
registerMonitorModel( name, "_lr", 18, 4, 2, 34 );
|
||||
registerMonitorModel( name, "_lrd", 30, 7, 2, 46 );
|
||||
registerMonitorModel( name, "_lru", 24, 5, 2, 40 );
|
||||
registerMonitorModel( name, "_lrud", 27, 6, 2, 43 );
|
||||
registerMonitorModel( name, "_lu", 25, 5, 1, 39 );
|
||||
registerMonitorModel( name, "_lud", 28, 6, 1, 42 );
|
||||
registerMonitorModel( name, "_r", 17, 4, 3, 35 );
|
||||
registerMonitorModel( name, "_rd", 29, 7, 3, 47 );
|
||||
registerMonitorModel( name, "_ru", 23, 5, 3, 41 );
|
||||
registerMonitorModel( name, "_rud", 26, 6, 3, 44 );
|
||||
registerMonitorModel( name, "_u", 22, 5, 0, 38 );
|
||||
registerMonitorModel( name, "_ud", 21, 6, 0, 37 );
|
||||
|
||||
VariantBlockStateBuilder builder = getVariantBuilder( block );
|
||||
for( MonitorEdgeState edge : BlockMonitor.STATE.getAllowedValues() )
|
||||
{
|
||||
String suffix = edge == MonitorEdgeState.NONE ? "" : "_" + edge.getName();
|
||||
ModelFile model = models().getBuilder( suffix( block, suffix ) );
|
||||
|
||||
for( Direction facing : BlockMonitor.FACING.getAllowedValues() )
|
||||
{
|
||||
for( Direction orientation : BlockMonitor.ORIENTATION.getAllowedValues() )
|
||||
{
|
||||
builder.partialState()
|
||||
.with( BlockMonitor.STATE, edge )
|
||||
.with( BlockMonitor.FACING, facing )
|
||||
.with( BlockMonitor.ORIENTATION, orientation )
|
||||
.addModels( new ConfiguredModel( model, toXAngle( orientation ), toYAngle( facing ), false ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void registerMonitorModel( String prefix, String corners, int front, int side, int top, int back )
|
||||
{
|
||||
String texturePrefix = ComputerCraft.MOD_ID + ":block/" + prefix + "_";
|
||||
models().getBuilder( prefix + corners )
|
||||
.parent( monitorBase )
|
||||
.texture( "front", texturePrefix + front )
|
||||
.texture( "side", texturePrefix + side )
|
||||
.texture( "top", texturePrefix + top )
|
||||
.texture( "back", texturePrefix + back );
|
||||
}
|
||||
|
||||
private static int toXAngle( Direction direction )
|
||||
{
|
||||
switch( direction )
|
||||
{
|
||||
default:
|
||||
return 0;
|
||||
case UP:
|
||||
return 270;
|
||||
case DOWN:
|
||||
return 90;
|
||||
}
|
||||
}
|
||||
|
||||
private static int toYAngle( Direction direction )
|
||||
{
|
||||
return ((int) direction.getHorizontalAngle() + 180) % 360;
|
||||
}
|
||||
|
||||
private static String toSuffix( ComputerState state )
|
||||
{
|
||||
switch( state )
|
||||
{
|
||||
default:
|
||||
case OFF:
|
||||
return "";
|
||||
case ON:
|
||||
return "_on";
|
||||
case BLINKING:
|
||||
return "_blink";
|
||||
}
|
||||
}
|
||||
|
||||
private static String suffix( Block block, String suffix )
|
||||
{
|
||||
ResourceLocation id = block.getRegistryName();
|
||||
return new ResourceLocation( id.getNamespace(), "block/" + id.getPath() + suffix ).toString();
|
||||
}
|
||||
}
|
||||
@@ -24,5 +24,6 @@ public class Generators
|
||||
generator.addProvider( new Recipes( generator ) );
|
||||
generator.addProvider( new LootTables( generator ) );
|
||||
generator.addProvider( new Tags( generator, event.getExistingFileHelper() ) );
|
||||
generator.addProvider( new BlockModelProvider( generator, event.getExistingFileHelper() ) );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ package dan200.computercraft.data;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.data.Tags.CCTags;
|
||||
import dan200.computercraft.shared.PocketUpgrades;
|
||||
import dan200.computercraft.shared.Registry;
|
||||
import dan200.computercraft.shared.TurtleUpgrades;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
@@ -117,7 +118,7 @@ public class Recipes extends RecipeProvider
|
||||
|
||||
String nameId = family.name().toLowerCase( Locale.ROOT );
|
||||
|
||||
TurtleUpgrades.getVanillaUpgrades().forEach( upgrade -> {
|
||||
PocketUpgrades.getVanillaUpgrades().forEach( upgrade -> {
|
||||
ItemStack result = PocketComputerItemFactory.create( -1, null, -1, family, null );
|
||||
ShapedRecipeBuilder
|
||||
.shapedRecipe( result.getItem() )
|
||||
|
||||
@@ -21,12 +21,12 @@ import net.minecraftforge.fml.ModLoadingContext;
|
||||
import net.minecraftforge.fml.common.Mod;
|
||||
import net.minecraftforge.fml.config.ModConfig;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static net.minecraftforge.common.ForgeConfigSpec.Builder;
|
||||
import static net.minecraftforge.common.ForgeConfigSpec.ConfigValue;
|
||||
@@ -180,12 +180,10 @@ public final class Config
|
||||
"Each rule is an item with a 'host' to match against, and a series of properties. " +
|
||||
"The host may be a domain name (\"pastebin.com\"),\n" +
|
||||
"wildcard (\"*.pastebin.com\") or CIDR notation (\"127.0.0.0/8\"). If no rules, the domain is blocked." )
|
||||
.defineList( "rules",
|
||||
Stream.concat(
|
||||
Stream.of( ComputerCraft.DEFAULT_HTTP_DENY ).map( x -> AddressRuleConfig.makeRule( x, Action.DENY ) ),
|
||||
Stream.of( ComputerCraft.DEFAULT_HTTP_ALLOW ).map( x -> AddressRuleConfig.makeRule( x, Action.ALLOW ) )
|
||||
).collect( Collectors.toList() ),
|
||||
x -> x instanceof UnmodifiableConfig && AddressRuleConfig.checkRule( (UnmodifiableConfig) x ) );
|
||||
.defineList( "rules", Arrays.asList(
|
||||
AddressRuleConfig.makeRule( "$private", Action.DENY ),
|
||||
AddressRuleConfig.makeRule( "*", Action.ALLOW )
|
||||
), x -> x instanceof UnmodifiableConfig && AddressRuleConfig.checkRule( (UnmodifiableConfig) x ) );
|
||||
|
||||
httpMaxRequests = builder
|
||||
.comment( "The number of http requests a computer can make at one time. Additional requests will be queued, and sent when the running requests have finished. Set to 0 for unlimited." )
|
||||
|
||||
@@ -67,7 +67,7 @@ public final class Peripherals
|
||||
}
|
||||
}
|
||||
|
||||
return CapabilityUtil.unwrap( GenericPeripheralProvider.getPeripheral( world, pos, side ), invalidate );
|
||||
return GenericPeripheralProvider.getPeripheral( world, pos, side, invalidate );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ package dan200.computercraft.shared;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||
import dan200.computercraft.shared.util.InventoryUtil;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.fml.ModContainer;
|
||||
import net.minecraftforge.fml.ModLoadingContext;
|
||||
@@ -55,7 +54,7 @@ public final class PocketUpgrades
|
||||
for( IPocketUpgrade upgrade : upgrades.values() )
|
||||
{
|
||||
ItemStack craftingStack = upgrade.getCraftingItem();
|
||||
if( !craftingStack.isEmpty() && InventoryUtil.areItemsSimilar( stack, craftingStack ) )
|
||||
if( !craftingStack.isEmpty() && craftingStack.getItem() == stack.getItem() && upgrade.isItemSuitable( stack ) )
|
||||
{
|
||||
return upgrade;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,6 @@ package dan200.computercraft.shared;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.shared.computer.core.ComputerFamily;
|
||||
import dan200.computercraft.shared.util.InventoryUtil;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraftforge.fml.ModLoadingContext;
|
||||
|
||||
@@ -83,7 +82,7 @@ public final class TurtleUpgrades
|
||||
if( !wrapper.enabled ) continue;
|
||||
|
||||
ItemStack craftingStack = wrapper.upgrade.getCraftingItem();
|
||||
if( !craftingStack.isEmpty() && InventoryUtil.areItemsSimilar( stack, craftingStack ) )
|
||||
if( !craftingStack.isEmpty() && craftingStack.getItem() == stack.getItem() && wrapper.upgrade.isItemSuitable( stack ) )
|
||||
{
|
||||
return wrapper.upgrade;
|
||||
}
|
||||
|
||||
@@ -39,6 +39,8 @@ import net.minecraft.util.text.ITextComponent;
|
||||
import net.minecraft.util.text.StringTextComponent;
|
||||
import net.minecraft.util.text.TranslationTextComponent;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.util.LazyOptional;
|
||||
import net.minecraftforge.common.util.NonNullConsumer;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
@@ -56,6 +58,7 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
|
||||
private boolean m_on = false;
|
||||
boolean m_startOn = false;
|
||||
private boolean m_fresh = false;
|
||||
private final NonNullConsumer<LazyOptional<IPeripheral>>[] invalidate;
|
||||
|
||||
private final ComputerFamily family;
|
||||
|
||||
@@ -63,6 +66,14 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
|
||||
{
|
||||
super( type );
|
||||
this.family = family;
|
||||
|
||||
// We cache these so we can guarantee we only ever register one listener for adjacent capabilities.
|
||||
@SuppressWarnings( { "unchecked", "rawtypes" } )
|
||||
NonNullConsumer<LazyOptional<IPeripheral>>[] invalidate = this.invalidate = new NonNullConsumer[6];
|
||||
for( Direction direction : Direction.values() )
|
||||
{
|
||||
invalidate[direction.ordinal()] = o -> updateInput( direction );
|
||||
}
|
||||
}
|
||||
|
||||
protected void unload()
|
||||
@@ -225,7 +236,7 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT
|
||||
computer.setBundledRedstoneInput( localDir, BundledRedstone.getOutput( getWorld(), offset, offsetSide ) );
|
||||
if( !isPeripheralBlockedOnSide( localDir ) )
|
||||
{
|
||||
IPeripheral peripheral = Peripherals.getPeripheral( getWorld(), offset, offsetSide, o -> updateInput( dir ) );
|
||||
IPeripheral peripheral = Peripherals.getPeripheral( getWorld(), offset, offsetSide, invalidate[dir.ordinal()] );
|
||||
computer.setPeripheral( localDir, peripheral );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -97,8 +97,11 @@ public class JEIComputerCraft implements IModPlugin
|
||||
{
|
||||
if( !(wrapper instanceof IRecipe) ) continue;
|
||||
ResourceLocation id = ((IRecipe) wrapper).getId();
|
||||
if( id.getNamespace().equals( ComputerCraft.MOD_ID )
|
||||
&& (id.getPath().startsWith( "generated/turtle_" ) || id.getPath().startsWith( "generated/pocket_" )) )
|
||||
if( !id.getNamespace().equals( ComputerCraft.MOD_ID ) ) continue;
|
||||
|
||||
String path = id.getPath();
|
||||
if( path.startsWith( "turtle_normal/" ) || path.startsWith( "turtle_advanced/" )
|
||||
|| path.startsWith( "pocket_normal/" ) || path.startsWith( "pocket_advanced/" ) )
|
||||
{
|
||||
registry.hideRecipe( wrapper, VanillaRecipeCategoryUid.CRAFTING );
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
package dan200.computercraft.shared.integration.jei;
|
||||
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.IUpgradeBase;
|
||||
import dan200.computercraft.api.pocket.IPocketUpgrade;
|
||||
import dan200.computercraft.api.turtle.ITurtleUpgrade;
|
||||
import dan200.computercraft.api.turtle.TurtleSide;
|
||||
@@ -16,7 +17,6 @@ import dan200.computercraft.shared.pocket.items.ItemPocketComputer;
|
||||
import dan200.computercraft.shared.pocket.items.PocketComputerItemFactory;
|
||||
import dan200.computercraft.shared.turtle.items.ITurtleItem;
|
||||
import dan200.computercraft.shared.turtle.items.TurtleItemFactory;
|
||||
import dan200.computercraft.shared.util.InventoryUtil;
|
||||
import mezz.jei.api.constants.VanillaRecipeCategoryUid;
|
||||
import mezz.jei.api.recipe.IFocus;
|
||||
import mezz.jei.api.recipe.advanced.IRecipeManagerPlugin;
|
||||
@@ -83,7 +83,10 @@ class RecipeResolver implements IRecipeManagerPlugin
|
||||
for( UpgradeInfo upgrade : upgrades )
|
||||
{
|
||||
ItemStack craftingStack = upgrade.stack;
|
||||
if( !craftingStack.isEmpty() && InventoryUtil.areItemsSimilar( stack, craftingStack ) ) return true;
|
||||
if( !craftingStack.isEmpty() && craftingStack.getItem() == stack.getItem() && upgrade.upgrade.isItemSuitable( stack ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
@@ -194,11 +197,10 @@ class RecipeResolver implements IRecipeManagerPlugin
|
||||
|
||||
List<Shaped> recipes = null;
|
||||
boolean multiple = false;
|
||||
Ingredient ingredient = fromStacks( stack );
|
||||
for( UpgradeInfo upgrade : upgrades )
|
||||
{
|
||||
ItemStack craftingStack = upgrade.stack;
|
||||
if( craftingStack.isEmpty() || !InventoryUtil.areItemsSimilar( stack, craftingStack ) )
|
||||
if( !craftingStack.isEmpty() && craftingStack.getItem() == stack.getItem() && upgrade.upgrade.isItemSuitable( stack ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -332,42 +334,29 @@ class RecipeResolver implements IRecipeManagerPlugin
|
||||
}
|
||||
}
|
||||
|
||||
private static final class Upgrade<T>
|
||||
{
|
||||
final T upgrade;
|
||||
final ItemStack stack;
|
||||
final Ingredient ingredient;
|
||||
|
||||
private Upgrade( T upgrade, ItemStack stack )
|
||||
{
|
||||
this.upgrade = upgrade;
|
||||
this.stack = stack;
|
||||
ingredient = fromStacks( stack );
|
||||
}
|
||||
}
|
||||
|
||||
private static class UpgradeInfo
|
||||
{
|
||||
final ItemStack stack;
|
||||
final Ingredient ingredient;
|
||||
final ITurtleUpgrade turtle;
|
||||
final IPocketUpgrade pocket;
|
||||
final IUpgradeBase upgrade;
|
||||
ArrayList<Shaped> recipes;
|
||||
|
||||
UpgradeInfo( ItemStack stack, ITurtleUpgrade turtle )
|
||||
{
|
||||
this.stack = stack;
|
||||
ingredient = fromStacks( stack );
|
||||
this.turtle = turtle;
|
||||
pocket = null;
|
||||
this.ingredient = fromStacks( stack );
|
||||
this.upgrade = this.turtle = turtle;
|
||||
this.pocket = null;
|
||||
}
|
||||
|
||||
UpgradeInfo( ItemStack stack, IPocketUpgrade pocket )
|
||||
{
|
||||
this.stack = stack;
|
||||
ingredient = fromStacks( stack );
|
||||
turtle = null;
|
||||
this.pocket = pocket;
|
||||
this.ingredient = fromStacks( stack );
|
||||
this.turtle = null;
|
||||
this.upgrade = this.pocket = pocket;
|
||||
}
|
||||
|
||||
List<Shaped> getRecipes()
|
||||
|
||||
@@ -15,11 +15,13 @@ import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.capabilities.Capability;
|
||||
import net.minecraftforge.common.util.LazyOptional;
|
||||
import net.minecraftforge.common.util.NonNullConsumer;
|
||||
import net.minecraftforge.energy.CapabilityEnergy;
|
||||
import net.minecraftforge.fluids.capability.CapabilityFluidHandler;
|
||||
import net.minecraftforge.items.CapabilityItemHandler;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -31,14 +33,13 @@ public class GenericPeripheralProvider
|
||||
CapabilityFluidHandler.FLUID_HANDLER_CAPABILITY,
|
||||
};
|
||||
|
||||
@Nonnull
|
||||
public static LazyOptional<IPeripheral> getPeripheral( @Nonnull World world, @Nonnull BlockPos pos, @Nonnull Direction side )
|
||||
@Nullable
|
||||
public static IPeripheral getPeripheral( @Nonnull World world, @Nonnull BlockPos pos, @Nonnull Direction side, NonNullConsumer<LazyOptional<IPeripheral>> invalidate )
|
||||
{
|
||||
TileEntity tile = world.getTileEntity( pos );
|
||||
if( tile == null ) return LazyOptional.empty();
|
||||
if( tile == null ) return null;
|
||||
|
||||
ArrayList<SaturatedMethod> saturated = new ArrayList<>( 0 );
|
||||
LazyOptional<IPeripheral> peripheral = LazyOptional.of( () -> new GenericPeripheral( tile, saturated ) );
|
||||
|
||||
List<NamedMethod<PeripheralMethod>> tileMethods = PeripheralMethod.GENERATOR.getMethods( tile.getClass() );
|
||||
if( !tileMethods.isEmpty() ) addSaturated( saturated, tile, tileMethods );
|
||||
@@ -51,11 +52,11 @@ public class GenericPeripheralProvider
|
||||
if( capabilityMethods.isEmpty() ) return;
|
||||
|
||||
addSaturated( saturated, contents, capabilityMethods );
|
||||
wrapper.addListener( x -> peripheral.invalidate() );
|
||||
wrapper.addListener( cast( invalidate ) );
|
||||
} );
|
||||
}
|
||||
|
||||
return saturated.isEmpty() ? LazyOptional.empty() : peripheral;
|
||||
return saturated.isEmpty() ? null : new GenericPeripheral( tile, saturated );
|
||||
}
|
||||
|
||||
private static void addSaturated( ArrayList<SaturatedMethod> saturated, Object target, List<NamedMethod<PeripheralMethod>> methods )
|
||||
@@ -66,4 +67,10 @@ public class GenericPeripheralProvider
|
||||
saturated.add( new SaturatedMethod( target, method ) );
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings( { "unchecked", "rawtypes" } )
|
||||
private static <T> NonNullConsumer<T> cast( NonNullConsumer<?> consumer )
|
||||
{
|
||||
return (NonNullConsumer) consumer;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,18 @@ import net.minecraftforge.versions.forge.ForgeVersion;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
/**
|
||||
* Methods for interacting with blocks using Forge's energy storage system.
|
||||
*
|
||||
* This works with energy storage blocks, as well as generators and machines which consume energy.
|
||||
*
|
||||
* <blockquote>
|
||||
* <strong>Note:</strong> Due to limitations with Forge's energy API, it is not possible to measure throughput (i.e. RF
|
||||
* used/generated per tick).
|
||||
* </blockquote>
|
||||
*
|
||||
* @cc.module energy_storage
|
||||
*/
|
||||
@AutoService( GenericSource.class )
|
||||
public class EnergyMethods implements GenericSource
|
||||
{
|
||||
@@ -25,12 +37,24 @@ public class EnergyMethods implements GenericSource
|
||||
return new ResourceLocation( ForgeVersion.MOD_ID, "energy" );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the energy of this block.
|
||||
*
|
||||
* @param energy The current energy storage.
|
||||
* @return The energy stored in this block, in FE.
|
||||
*/
|
||||
@LuaFunction( mainThread = true )
|
||||
public static int getEnergy( IEnergyStorage energy )
|
||||
{
|
||||
return energy.getEnergyStored();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the maximum amount of energy this block can store.
|
||||
*
|
||||
* @param energy The current energy storage.
|
||||
* @return The energy capacity of this block.
|
||||
*/
|
||||
@LuaFunction( mainThread = true )
|
||||
public static int getEnergyCapacity( IEnergyStorage energy )
|
||||
{
|
||||
|
||||
@@ -31,6 +31,11 @@ import java.util.Optional;
|
||||
|
||||
import static dan200.computercraft.shared.peripheral.generic.methods.ArgumentHelpers.getRegistryEntry;
|
||||
|
||||
/**
|
||||
* Methods for interacting with tanks and other fluid storage blocks.
|
||||
*
|
||||
* @cc.module fluid_storage
|
||||
*/
|
||||
@AutoService( GenericSource.class )
|
||||
public class FluidMethods implements GenericSource
|
||||
{
|
||||
|
||||
@@ -32,6 +32,11 @@ 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
|
||||
{
|
||||
@@ -42,12 +47,32 @@ public class InventoryMethods implements GenericSource
|
||||
return new ResourceLocation( ForgeVersion.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( IItemHandler inventory )
|
||||
{
|
||||
return inventory.getSlots();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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( IItemHandler inventory )
|
||||
{
|
||||
@@ -62,6 +87,16 @@ public class InventoryMethods implements GenericSource
|
||||
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( IItemHandler inventory, int slot ) throws LuaException
|
||||
{
|
||||
@@ -71,6 +106,32 @@ public class InventoryMethods implements GenericSource
|
||||
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(
|
||||
IItemHandler from, IComputerAccess computer,
|
||||
@@ -86,13 +147,39 @@ public class InventoryMethods implements GenericSource
|
||||
|
||||
// Validate slots
|
||||
int actualLimit = limit.orElse( Integer.MAX_VALUE );
|
||||
if( actualLimit <= 0 ) throw new LuaException( "Limit must be > 0" );
|
||||
assertBetween( fromSlot, 1, from.getSlots(), "From slot out of range (%s)" );
|
||||
if( toSlot.isPresent() ) assertBetween( toSlot.get(), 1, to.getSlots(), "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(
|
||||
IItemHandler to, IComputerAccess computer,
|
||||
@@ -108,10 +195,10 @@ public class InventoryMethods implements GenericSource
|
||||
|
||||
// Validate slots
|
||||
int actualLimit = limit.orElse( Integer.MAX_VALUE );
|
||||
if( actualLimit <= 0 ) throw new LuaException( "Limit must be > 0" );
|
||||
assertBetween( fromSlot, 1, from.getSlots(), "From slot out of range (%s)" );
|
||||
if( toSlot.isPresent() ) assertBetween( toSlot.get(), 1, to.getSlots(), "To slot out of range (%s)" );
|
||||
|
||||
if( actualLimit <= 0 ) return 0;
|
||||
return moveItem( from, fromSlot - 1, to, toSlot.orElse( 0 ) - 1, actualLimit );
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ public class BlockMonitor extends BlockGeneric
|
||||
|
||||
public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;
|
||||
|
||||
static final EnumProperty<MonitorEdgeState> STATE = EnumProperty.create( "state", MonitorEdgeState.class );
|
||||
public static final EnumProperty<MonitorEdgeState> STATE = EnumProperty.create( "state", MonitorEdgeState.class );
|
||||
|
||||
public BlockMonitor( Properties settings, RegistryObject<? extends TileEntityType<? extends TileMonitor>> type )
|
||||
{
|
||||
|
||||
@@ -57,6 +57,12 @@ public enum MonitorEdgeState implements IStringSerializable
|
||||
return BY_FLAG[(up ? UP : 0) | (down ? DOWN : 0) | (left ? LEFT : 0) | (right ? RIGHT : 0)];
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return getName();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public String getString()
|
||||
|
||||
@@ -327,12 +327,16 @@ public class TileMonitor extends TileGeneric
|
||||
// region Sizing and placement stuff
|
||||
public Direction getDirection()
|
||||
{
|
||||
return getBlockState().get( BlockMonitor.FACING );
|
||||
// Ensure we're actually a monitor block. This _should_ always be the case, but sometimes there's
|
||||
// fun problems with the block being missing on the client.
|
||||
BlockState state = getBlockState();
|
||||
return state.has( BlockMonitor.FACING ) ? state.get( BlockMonitor.FACING ) : Direction.NORTH;
|
||||
}
|
||||
|
||||
public Direction getOrientation()
|
||||
{
|
||||
return getBlockState().get( BlockMonitor.ORIENTATION );
|
||||
BlockState state = getBlockState();
|
||||
return state.has( BlockMonitor.ORIENTATION ) ? state.get( BlockMonitor.ORIENTATION ) : Direction.NORTH;
|
||||
}
|
||||
|
||||
public Direction getFront()
|
||||
|
||||
@@ -257,6 +257,9 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I
|
||||
|
||||
public static ServerComputer getServerComputer( @Nonnull ItemStack stack )
|
||||
{
|
||||
int session = getSessionID( stack );
|
||||
if( session != ComputerCraft.serverComputerRegistry.getSessionID() ) return null;
|
||||
|
||||
int instanceID = getInstanceID( stack );
|
||||
return instanceID >= 0 ? ComputerCraft.serverComputerRegistry.get( instanceID ) : null;
|
||||
}
|
||||
|
||||
@@ -501,7 +501,7 @@ public class TurtleAPI implements ILuaAPI
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the currently sleected slot.
|
||||
* Get the currently selected slot.
|
||||
*
|
||||
* @return The current slot.
|
||||
* @see #select
|
||||
|
||||
@@ -128,6 +128,10 @@ public class TurtleBrain implements ITurtleAccess
|
||||
{
|
||||
// Advance movement
|
||||
updateCommands();
|
||||
|
||||
// The block may have been broken while the command was executing (for instance, if a block explodes
|
||||
// when being mined). If so, abort.
|
||||
if( m_owner.isRemoved() ) return;
|
||||
}
|
||||
|
||||
// Advance animation
|
||||
|
||||
@@ -11,6 +11,7 @@ import dan200.computercraft.api.turtle.*;
|
||||
import dan200.computercraft.api.turtle.event.TurtleAttackEvent;
|
||||
import dan200.computercraft.api.turtle.event.TurtleBlockEvent;
|
||||
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;
|
||||
@@ -25,6 +26,7 @@ import net.minecraft.entity.item.ArmorStandEntity;
|
||||
import net.minecraft.fluid.FluidState;
|
||||
import net.minecraft.item.Item;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.Direction;
|
||||
@@ -37,6 +39,7 @@ import net.minecraft.world.World;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.common.util.Constants;
|
||||
import net.minecraftforge.event.entity.player.AttackEntityEvent;
|
||||
import net.minecraftforge.event.world.BlockEvent;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
@@ -67,6 +70,24 @@ public class TurtleTool extends AbstractTurtleUpgrade
|
||||
this.item = toolItem;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isItemSuitable( @Nonnull ItemStack stack )
|
||||
{
|
||||
CompoundNBT tag = stack.getTag();
|
||||
if( tag == null || tag.isEmpty() ) return true;
|
||||
|
||||
// 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.hasDisplayName() ) return false;
|
||||
if( tag.contains( "AttributeModifiers", Constants.NBT.TAG_LIST ) &&
|
||||
!tag.getList( "AttributeModifiers", Constants.NBT.TAG_COMPOUND ).isEmpty() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
@OnlyIn( Dist.CLIENT )
|
||||
@@ -111,11 +132,14 @@ public class TurtleTool extends AbstractTurtleUpgrade
|
||||
return 3.0f;
|
||||
}
|
||||
|
||||
private TurtleCommandResult attack( final ITurtleAccess turtle, Direction direction, TurtleSide side )
|
||||
private TurtleCommandResult attack( ITurtleAccess turtle, Direction direction, TurtleSide side )
|
||||
{
|
||||
// Create a fake player, and orient it appropriately
|
||||
final World world = turtle.getWorld();
|
||||
final BlockPos position = turtle.getPosition();
|
||||
World world = turtle.getWorld();
|
||||
BlockPos position = turtle.getPosition();
|
||||
TileEntity turtleTile = turtle instanceof TurtleBrain ? ((TurtleBrain) turtle).getOwner() : world.getTileEntity( position );
|
||||
if( turtleTile == null ) return TurtleCommandResult.failure( "Turtle has vanished from existence." );
|
||||
|
||||
final TurtlePlayer turtlePlayer = TurtlePlaceCommand.createPlayer( turtle, position, direction );
|
||||
|
||||
// See if there is an entity present
|
||||
@@ -143,7 +167,7 @@ public class TurtleTool extends AbstractTurtleUpgrade
|
||||
}
|
||||
|
||||
// Start claiming entity drops
|
||||
DropConsumer.set( hitEntity, turtleDropConsumer( turtle ) );
|
||||
DropConsumer.set( hitEntity, turtleDropConsumer( turtleTile, turtle ) );
|
||||
|
||||
// Attack the entity
|
||||
boolean attacked = false;
|
||||
@@ -175,7 +199,7 @@ public class TurtleTool extends AbstractTurtleUpgrade
|
||||
}
|
||||
|
||||
// Stop claiming drops
|
||||
stopConsuming( turtle );
|
||||
stopConsuming( turtleTile, turtle );
|
||||
|
||||
// Put everything we collected into the turtles inventory, then return
|
||||
if( attacked )
|
||||
@@ -193,8 +217,10 @@ public class TurtleTool extends AbstractTurtleUpgrade
|
||||
// Get ready to dig
|
||||
World world = turtle.getWorld();
|
||||
BlockPos turtlePosition = turtle.getPosition();
|
||||
BlockPos blockPosition = turtlePosition.offset( direction );
|
||||
TileEntity turtleTile = turtle instanceof TurtleBrain ? ((TurtleBrain) turtle).getOwner() : world.getTileEntity( turtlePosition );
|
||||
if( turtleTile == null ) return TurtleCommandResult.failure( "Turtle has vanished from existence." );
|
||||
|
||||
BlockPos blockPosition = turtlePosition.offset( direction );
|
||||
if( world.isAirBlock( blockPosition ) || WorldUtil.isLiquidBlock( world, blockPosition ) )
|
||||
{
|
||||
return TurtleCommandResult.failure( "Nothing to dig here" );
|
||||
@@ -234,7 +260,7 @@ public class TurtleTool extends AbstractTurtleUpgrade
|
||||
}
|
||||
|
||||
// Consume the items the block drops
|
||||
DropConsumer.set( world, blockPosition, turtleDropConsumer( turtle ) );
|
||||
DropConsumer.set( world, blockPosition, turtleDropConsumer( turtleTile, turtle ) );
|
||||
|
||||
TileEntity tile = world.getTileEntity( blockPosition );
|
||||
|
||||
@@ -253,23 +279,24 @@ public class TurtleTool extends AbstractTurtleUpgrade
|
||||
state.getBlock().harvestBlock( world, turtlePlayer, blockPosition, state, tile, turtlePlayer.getHeldItemMainhand() );
|
||||
}
|
||||
|
||||
stopConsuming( turtle );
|
||||
stopConsuming( turtleTile, turtle );
|
||||
|
||||
return TurtleCommandResult.success();
|
||||
|
||||
}
|
||||
|
||||
private static Function<ItemStack, ItemStack> turtleDropConsumer( ITurtleAccess turtle )
|
||||
private static Function<ItemStack, ItemStack> turtleDropConsumer( TileEntity tile, ITurtleAccess turtle )
|
||||
{
|
||||
return drop -> InventoryUtil.storeItems( drop, turtle.getItemHandler(), turtle.getSelectedSlot() );
|
||||
return drop -> tile.isRemoved() ? drop : InventoryUtil.storeItems( drop, turtle.getItemHandler(), turtle.getSelectedSlot() );
|
||||
}
|
||||
|
||||
private static void stopConsuming( ITurtleAccess turtle )
|
||||
private static void stopConsuming( TileEntity tile, ITurtleAccess turtle )
|
||||
{
|
||||
Direction direction = tile.isRemoved() ? null : turtle.getDirection().getOpposite();
|
||||
List<ItemStack> extra = DropConsumer.clear();
|
||||
for( ItemStack remainder : extra )
|
||||
{
|
||||
WorldUtil.dropItemStack( remainder, turtle.getWorld(), turtle.getPosition(), turtle.getDirection().getOpposite() );
|
||||
WorldUtil.dropItemStack( remainder, turtle.getWorld(), turtle.getPosition(), direction );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import net.minecraft.entity.Entity;
|
||||
import net.minecraft.inventory.IInventory;
|
||||
import net.minecraft.inventory.ISidedInventory;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.nbt.CompoundNBT;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
@@ -40,35 +39,6 @@ public final class InventoryUtil
|
||||
return a == b || ItemHandlerHelper.canItemStacksStack( a, b );
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines if two items are "mostly" equivalent. Namely, they have the same item and damage, and identical
|
||||
* share stacks.
|
||||
*
|
||||
* This is largely based on {@link net.minecraftforge.common.crafting.IngredientNBT#test(ItemStack)}. It is
|
||||
* sufficient to ensure basic information (such as enchantments) are the same, while not having to worry about
|
||||
* capabilities.
|
||||
*
|
||||
* @param a The first stack to check
|
||||
* @param b The second stack to check
|
||||
* @return If these items are largely the same.
|
||||
*/
|
||||
public static boolean areItemsSimilar( @Nonnull ItemStack a, @Nonnull ItemStack b )
|
||||
{
|
||||
if( a == b ) return true;
|
||||
if( a.isEmpty() ) return !b.isEmpty();
|
||||
|
||||
if( a.getItem() != b.getItem() ) return false;
|
||||
|
||||
// A more expanded form of ItemStack.areShareTagsEqual, but allowing an empty tag to be equal to a
|
||||
// null one.
|
||||
CompoundNBT shareTagA = a.getItem().getShareTag( a );
|
||||
CompoundNBT shareTagB = b.getItem().getShareTag( b );
|
||||
if( shareTagA == shareTagB ) return true;
|
||||
if( shareTagA == null ) return shareTagB.isEmpty();
|
||||
if( shareTagB == null ) return shareTagA.isEmpty();
|
||||
return shareTagA.equals( shareTagB );
|
||||
}
|
||||
|
||||
// Methods for finding inventories:
|
||||
|
||||
public static IItemHandler getInventory( World world, BlockPos pos, Direction side )
|
||||
|
||||
Reference in New Issue
Block a user