1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-10-16 14:37:39 +00:00

Compare commits

..

26 Commits

Author SHA1 Message Date
Merith-TK
19273b3696 CC:R 1.92.0 2021-02-22 01:42:34 -08:00
Merith-TK
4643641d51 Update Patchwork, Handle Tabs when Parsing Json 2021-02-22 01:23:39 -08:00
Merith-TK
63cd9c5bc7 Update Patchwork.md with format 2021-02-22 01:20:51 -08:00
Merith-TK
e9c11ff325 Translations for Vienamese 2021-02-22 01:13:56 -08:00
Merith-TK
452464aa01 add changelog + vienamese support 2021-02-22 01:11:29 -08:00
Merith-TK
8885462175 Don't use entity.captureDrops at all (removed line) 2021-02-22 01:05:22 -08:00
Merith-TK
b8bd64913b Add date-specific MOTDs (like Minecraft) (CCT#533) 2021-02-22 01:02:56 -08:00
Devan-Kerman
88722d484f Merge pull request #29 from davidqueneau/fabric
Porting GenericPeripherals from upstream CC: Tweaked
2021-02-03 10:44:02 -06:00
David Queneau
6d103e2114 Item movement methods now respect inventory slot rules (i.e. no pickaxes in the fuel slot of a furnace). 2021-02-01 23:21:25 -08:00
David Queneau
42f23d56ae Double chest inventories are now handled correctly. 2021-02-01 19:04:02 -08:00
David Queneau
89d5211bd7 Fixed bad assumption about empty ItemStacks being reset to ItemStack.EMPTY. Items no longer transfer into an inventory as a different item than they began. 2021-02-01 00:02:28 -08:00
David Queneau
83e70377f7 Fixed off by one error. Commented not very nice inventory code. 2021-01-31 20:13:59 -08:00
David Queneau
f6a26f75c3 Reverted change to how GenericPeripherals report type. Instead, added generic lua function that returns the name of Nameable targets. Might not be at home in InventoryMethods since it could apply to other types of targets. 2021-01-30 20:45:08 -08:00
David Queneau
664df62d5d Cable modems can be placed against all blocks, including chests. 2021-01-30 17:12:09 -08:00
David Queneau
1348ee0588 Ported the generic peripheral feature from upstream forge version along with initial implementation of generic inventory peripherals. 2021-01-30 15:28:11 -08:00
Jacob Farley
145dce7653 Merge pull request #27 from techninja1008/fix-http-config
Fix http config
2021-01-21 20:09:23 -06:00
Jacob Farley
7f2651c23e Merge pull request #28 from techninja1008/fix-turtle-breaking-computer
Change turtle block breaking to call onBreak
2021-01-20 15:06:17 -06:00
Danny Wensley
05464107a8 Update ComputerCraft.httpRules on config sync.
Also removes a bit of http config related legacy code.
2021-01-20 20:03:00 +00:00
Danny Wensley
86705787f0 Change turtle block breaking to call onBreak
Fixes #25
2021-01-20 14:30:11 +00:00
Devan-Kerman
b34d8387d9 Update README.md 2020-12-29 15:17:00 -06:00
Devan-Kerman
4d00969ef0 fix #10
Signed-off-by: Devan-Kerman <devan@cleverpath.com>
2020-09-15 11:52:23 -05:00
Devan-Kerman
01d3d12992 update commits
Signed-off-by: Devan-Kerman <devan@cleverpath.com>
2020-09-15 11:38:16 -05:00
Devan-Kerman
5e31dcde83 fix CCE
Signed-off-by: Devan-Kerman <devan@cleverpath.com>
2020-09-12 20:20:38 -05:00
Devan-Kerman
5184883af1 fix potential bug
Signed-off-by: Devan-Kerman <devan@cleverpath.com>
2020-09-09 10:07:38 -05:00
Devan-Kerman
0c45112262 fix npe 2020-09-09 09:36:47 -05:00
Jacob Farley
0bf1672f45 Update README.md 2020-09-08 12:31:17 -05:00
34 changed files with 1123 additions and 261 deletions

1
.gitignore vendored
View File

@@ -15,3 +15,4 @@
.idea
.gradle
*.DS_Store
.project

3
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,3 @@
{
"java.configuration.updateBuildConfiguration": "automatic"
}

View File

@@ -1,7 +1,9 @@
# CC:T for Fabric
# CC:Restitched Patchwork
# This is a Work In Progress Port
*it runs and works-ish*
* [Download on CurseForge](https://www.curseforge.com/minecraft/mc-mods/cc-tweaked-fabric)
## Reached Parity with CC:T 1.92.0
A fork of [CC: Tweaked](https://github.com/SquidDev-CC/CC-Tweaked) for use with the latest Fabric.
THis is just a quick patchwork of my attempts at getting CC:R up to date with CC:T
NOTE: This project is currently in alpha stage. It may not be well-tested and stable, so use it at your own peril!
The changelog is located at [PatchWork.md](patchwork.md)

View File

@@ -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'

View File

@@ -2,10 +2,10 @@
org.gradle.jvmargs=-Xmx1G
# Mod properties
mod_version=1.91.1
mod_version=1.92.0
# Minecraft properties
mc_version=1.16.2
mc_version=1.16.3
mappings_version=31
# Dependencies

48
patchwork.md Normal file
View File

@@ -0,0 +1,48 @@
# Just my list of things I have ported over
Format for the changelog of ported stuff
```
commit // Shows commit from CC:T
commit2 // Shows a commit that is the same thing, just a clean up, only if right after
Title // Commit Title
SubScript // Desc of commit
```
If a edit that is present in CC:T is not needed, I will skip over it.
Any and all references to an issue number, are to be found on CC:T's repo. not this oen
```md
5155e18de279a193c558aa029963486fd1294769
Added translation for Vietnamese
Co-authored-by: Boom <boom@flyingpackets.net>
```
```
7e121ff72f2b1504cd6af47b57500876682bac45
ae6124d1f477487abab1858abde8c4ec49dfee3c
Translations for Vienamese
Co-authored-by: Boom <boom@flyingpackets.net>
```
```
59de21eae29849988e77fad6bc335f5ce78dfec7
Handle tabs when parsing JSON
Fixes #539
```
```
748ebbe66bf0a4239bde34f557e4b4b75d61d990
Bump to 1.92.0
A tiny release, but there's new features so it's technically a minor
bump.
```
Cherry Picked because this update was partially related to forge updates rather than mod updates
```
8b4a01df27ff7f6fa9ffd9c2188c6e3166edd515
Update to Minecraft 1.16.3
I hope the Fabric folks now realise this is gonna be a race of who can
update first :p. Either way, this was a very easy update - only changes
were due to unrelated Forge changes.
```

View File

@@ -10,7 +10,6 @@ import static dan200.computercraft.shared.ComputerCraftRegistry.ModBlocks;
import static dan200.computercraft.shared.ComputerCraftRegistry.init;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.EnumSet;
import java.util.List;
import java.util.Objects;
@@ -19,10 +18,10 @@ import java.util.stream.Collectors;
import java.util.stream.Stream;
import dan200.computercraft.api.turtle.event.TurtleAction;
import dan200.computercraft.core.apis.AddressPredicate;
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;
@@ -50,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;
@@ -72,6 +72,7 @@ public final class ComputerCraft implements ModInitializer {
"172.16.0.0/12",
"192.168.0.0/16",
"fd00::/8",
"0.0.0.0/8"
};
public static final int terminalWidth_computer = 51;
public static final int terminalHeight_computer = 19;
@@ -85,13 +86,6 @@ public final class ComputerCraft implements ModInitializer {
// Logging
public static final Logger log = LogManager.getLogger(MOD_ID);
public static ItemGroup MAIN_GROUP = FabricItemGroupBuilder.build(new Identifier(MOD_ID, "main"), () -> new ItemStack(ModBlocks.COMPUTER_NORMAL));
public static List<AddressRule> httpRules = Collections.unmodifiableList(Stream.concat(Stream.of(DEFAULT_HTTP_BLACKLIST)
.map(x -> AddressRule.parse(x, Action.DENY.toPartial()))
.filter(Objects::nonNull),
Stream.of(DEFAULT_HTTP_WHITELIST)
.map(x -> AddressRule.parse(x, Action.ALLOW.toPartial()))
.filter(Objects::nonNull))
.collect(Collectors.toList()));
public static boolean commandRequireCreative = false;
public static MonitorRenderer monitorRenderer = MonitorRenderer.BEST;
public static int computerSpaceLimit = 1000 * 1000;
@@ -106,8 +100,6 @@ public final class ComputerCraft implements ModInitializer {
public static long maxMainComputerTime = TimeUnit.MILLISECONDS.toNanos(5);
public static boolean http_enable = true;
public static boolean http_websocket_enable = true;
public static AddressPredicate http_whitelist = new AddressPredicate(DEFAULT_HTTP_WHITELIST);
public static AddressPredicate http_blacklist = new AddressPredicate(DEFAULT_HTTP_BLACKLIST);
public static int httpTimeout = 30000;
public static int httpMaxRequests = 16;
public static long httpMaxDownload = 16 * 1024 * 1024;
@@ -130,6 +122,18 @@ public final class ComputerCraft implements ModInitializer {
public static int monitorHeight = 6;
public static double monitorDistanceSq = 4096;
public static List<AddressRule> httpRules = buildHttpRulesFromConfig(DEFAULT_HTTP_BLACKLIST, DEFAULT_HTTP_WHITELIST);
public static List<AddressRule> buildHttpRulesFromConfig(String[] blacklist, String[] whitelist) {
return Stream.concat(Stream.of(blacklist)
.map(x -> AddressRule.parse(x, Action.DENY.toPartial()))
.filter(Objects::nonNull),
Stream.of(whitelist)
.map(x -> AddressRule.parse(x, Action.ALLOW.toPartial()))
.filter(Objects::nonNull))
.collect(Collectors.toList());
}
@Override
public void onInitialize() {
Config.load(Paths.get(FabricLoader.getInstance()
@@ -152,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 ));
}
}

View File

@@ -68,7 +68,7 @@ public abstract class ItemMapLikeRenderer {
float tZ = -0.4f * MathHelper.sin(swingRt * (float) Math.PI);
transform.translate(0, -tX / 2, tZ);
HeldItemRendererAccess access = (HeldItemRendererAccess) render;
HeldItemRendererAccess access = (HeldItemRendererAccess) renderer;
float pitchAngle = access.callGetMapAngle(pitch);
transform.translate(0, 0.04F + equipProgress * -1.2f + pitchAngle * -0.5f, -0.72f);
transform.multiply(Vector3f.POSITIVE_X.getDegreesQuaternion(pitchAngle * -85.0f));

View File

@@ -1,167 +0,0 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2019. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.core.apis;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;
import com.google.common.net.InetAddresses;
import dan200.computercraft.ComputerCraft;
/**
* Used to determine whether a domain or IP address matches a series of patterns.
*/
public class AddressPredicate {
private final List<Pattern> wildcards;
private final List<HostRange> ranges;
public AddressPredicate(String... filters) {
this(Arrays.asList(filters));
}
public AddressPredicate(Iterable<? extends String> filters) {
List<Pattern> wildcards = this.wildcards = new ArrayList<>();
List<HostRange> ranges = this.ranges = new ArrayList<>();
for (String filter : filters) {
int cidr = filter.indexOf('/');
if (cidr >= 0) {
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);
continue;
}
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);
continue;
}
// 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;
}
ranges.add(new HostRange(minBytes, maxBytes));
} else {
wildcards.add(Pattern.compile("^\\Q" + filter.replaceAll("\\*", "\\\\E.*\\\\Q") + "\\E$"));
}
}
}
/**
* Determine whether the given address matches a series of patterns
*
* @param address The address to check.
* @return Whether it matches any of these patterns.
*/
public boolean matches(InetAddress address) {
// Match the host name
String host = address.getHostName();
if (host != null && this.matches(host)) {
return true;
}
// Match the normal address
if (this.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) && this.matchesAddress(InetAddresses.get6to4IPv4Address(
(Inet6Address) address));
}
/**
* Determine whether a host name matches a series of patterns.
*
* This is intended to allow early exiting, before one has to look up the IP address. You should use {@link #matches(InetAddress)} instead of/in
* addition to this one.
*
* @param domain The domain to match.
* @return Whether the patterns were matched.
*/
public boolean matches(String domain) {
for (Pattern domainPattern : this.wildcards) {
if (domainPattern.matcher(domain)
.matches()) {
return true;
}
}
return false;
}
private boolean matchesAddress(InetAddress address) {
String addressString = address.getHostAddress();
for (Pattern domainPattern : this.wildcards) {
if (domainPattern.matcher(addressString)
.matches()) {
return true;
}
}
for (HostRange range : this.ranges) {
if (range.contains(address)) {
return true;
}
}
return false;
}
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 != this.min.length) {
return false;
}
for (int i = 0; i < entry.length; i++) {
int value = 0xFF & entry[i];
if (value < (0xFF & this.min[i]) || value > (0xFF & this.max[i])) {
return false;
}
}
return true;
}
}
}

View File

@@ -106,7 +106,7 @@ public class FileSystemWrapperMount implements IFileSystem {
@Override
public boolean isDirectory(@Nonnull String path) throws IOException {
try {
return this.m_filesystem.exists(path);
return this.m_filesystem.isDir(path);
} catch (FileSystemException e) {
throw new IOException(e.getMessage());
}

View File

@@ -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);
}
}

View File

@@ -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 );
}
}

View File

@@ -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 ) );
}
}
}

View File

@@ -0,0 +1,58 @@
/*
* This file is part of ComputerCraft - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission.
* Send enquiries to dratcliffe@gmail.com
*/
package dan200.computercraft.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();
}
}

View File

@@ -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 );
}
}

View File

@@ -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();
}
}

View File

@@ -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 );
}
}
}

View File

@@ -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 ) );
}
}
}

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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 ({

View File

@@ -228,9 +228,9 @@ public class TurtlePlaceCommand implements ITurtleCommand {
cancelResult = hitEntity.interactAt(turtlePlayer, hitPos, Hand.MAIN_HAND);
}
if (cancelResult.isAccepted()) {
if (cancelResult != null && cancelResult.isAccepted()) {
placed = true;
} else {
} else if (hitEntity instanceof LivingEntity) {
// See EntityPlayer.interactOn
cancelResult = stackCopy.useOnEntity(turtlePlayer, (LivingEntity) hitEntity, Hand.MAIN_HAND);
if (cancelResult != null && cancelResult.isAccepted()) {
@@ -238,7 +238,7 @@ public class TurtlePlaceCommand implements ITurtleCommand {
} else if (cancelResult == null) {
if (hitEntity.interact(turtlePlayer, Hand.MAIN_HAND) == ActionResult.CONSUME) {
placed = true;
} else if (hitEntity instanceof LivingEntity) {
} else {
placed = stackCopy.useOnEntity(turtlePlayer, (LivingEntity) hitEntity, Hand.MAIN_HAND)
.isAccepted();
if (placed) {
@@ -312,17 +312,11 @@ public class TurtlePlaceCommand implements ITurtleCommand {
}
if (!placed && (item instanceof BucketItem || item instanceof BoatItem || item instanceof LilyPadItem || item instanceof GlassBottleItem)) {
TypedActionResult<ItemStack> actionResult = stackCopy.use(turtle.getWorld(), turtlePlayer, Hand.MAIN_HAND);
if (actionResult != null && actionResult.getResult()
.isAccepted()) {
TypedActionResult<ItemStack> result = stackCopy.use(turtle.getWorld(), turtlePlayer, Hand.MAIN_HAND);
if (result.getResult()
.isAccepted() && !ItemStack.areEqual(stack, result.getValue())) {
placed = true;
} else if (actionResult == null) {
TypedActionResult<ItemStack> result = stackCopy.use(turtle.getWorld(), turtlePlayer, Hand.MAIN_HAND);
if (result.getResult()
.isAccepted() && !ItemStack.areEqual(stack, result.getValue())) {
placed = true;
turtlePlayer.loadInventory(result.getValue());
}
turtlePlayer.loadInventory(result.getValue());
}
}

View File

@@ -26,12 +26,14 @@ import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityDimensions;
import net.minecraft.entity.EntityPose;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.effect.StatusEffectInstance;
import net.minecraft.entity.passive.HorseBaseEntity;
import net.minecraft.inventory.Inventory;
import net.minecraft.item.ItemStack;
import net.minecraft.screen.NamedScreenHandlerFactory;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;

View File

@@ -210,7 +210,7 @@ public class TurtleTool extends AbstractTurtleUpgrade {
// Destroy the block
state.getBlock()
.onBroken(world, blockPosition, state);
.onBreak(world, blockPosition, state, turtlePlayer);
if (world.removeBlock(blockPosition, false)) {
state.getBlock()
.onBroken(world, blockPosition, state);

View File

@@ -13,7 +13,6 @@ import com.google.common.base.CaseFormat;
import com.google.common.base.Converter;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.turtle.event.TurtleAction;
import dan200.computercraft.core.apis.AddressPredicate;
import dan200.computercraft.core.apis.http.websocket.Websocket;
public class Config {
@@ -84,8 +83,7 @@ public class Config {
// HTTP
ComputerCraft.http_enable = config.http.enabled;
ComputerCraft.http_websocket_enable = config.http.websocket_enabled;
ComputerCraft.http_whitelist = new AddressPredicate(config.http.whitelist);
ComputerCraft.http_blacklist = new AddressPredicate(config.http.blacklist);
ComputerCraft.httpRules = ComputerCraft.buildHttpRulesFromConfig(config.http.blacklist, config.http.whitelist);
ComputerCraft.httpTimeout = Math.max(0, config.http.timeout);
ComputerCraft.httpMaxRequests = Math.max(1, config.http.max_requests);

View File

@@ -35,8 +35,6 @@ public final class DropConsumer {
dropWorld = new WeakReference<>(entity.world);
dropPos = null;
dropBounds = new Box(entity.getBlockPos()).expand(2, 2, 2);
// entity.captureDrops( new ArrayList<>() );
}
public static void set(World world, BlockPos pos, Function<ItemStack, ItemStack> consumer) {

View File

@@ -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 );
}
}

View File

@@ -0,0 +1,48 @@
{
"gui.computercraft.tooltip.disk_id": "ID của đĩa: %s",
"upgrade.computercraft.speaker.adjective": "Ồn ào",
"upgrade.computercraft.wireless_modem_advanced.adjective": "Ender",
"upgrade.computercraft.wireless_modem_normal.adjective": "Không dây",
"upgrade.minecraft.crafting_table.adjective": "Chế tạo",
"upgrade.minecraft.diamond_hoe.adjective": "Trồng trọt",
"upgrade.minecraft.diamond_axe.adjective": "Đốn",
"upgrade.minecraft.diamond_pickaxe.adjective": "Khai thác",
"upgrade.minecraft.diamond_shovel.adjective": "Đào",
"item.computercraft.pocket_computer_advanced.upgraded": "Máy tính bỏ túi tiên tiến %s",
"item.computercraft.pocket_computer_advanced": "Máy tính bỏ túi tiên tiến",
"item.computercraft.pocket_computer_normal.upgraded": "Máy tính bỏ túi %s",
"item.computercraft.pocket_computer_normal": "Máy tính bỏ túi",
"item.computercraft.printed_book": "Sách in",
"item.computercraft.printed_page": "Trang in",
"item.computercraft.treasure_disk": "Đĩa mềm",
"item.computercraft.disk": "Đĩa mềm",
"block.computercraft.turtle_advanced.upgraded_twice": "Rùa tiên tiến %s %s",
"block.computercraft.turtle_advanced.upgraded": "Rùa tiên tiến %s",
"block.computercraft.turtle_advanced": "Rùa tiên tiến",
"block.computercraft.turtle_normal.upgraded_twice": "Rùa %s %s",
"block.computercraft.turtle_normal.upgraded": "Rùa %s",
"block.computercraft.turtle_normal": "Rùa",
"block.computercraft.wired_modem_full": "Modem có dây",
"block.computercraft.cable": "Dây cáp mạng",
"block.computercraft.wired_modem": "Modem có dây",
"block.computercraft.wireless_modem_advanced": "Modem Ender",
"block.computercraft.wireless_modem_normal": "Modem không dây",
"block.computercraft.monitor_advanced": "Màn hình tiên tiếng",
"block.computercraft.monitor_normal": "Màn hình",
"block.computercraft.speaker": "Loa",
"block.computercraft.printer": "Máy in",
"block.computercraft.disk_drive": "Ỗ đĩa",
"block.computercraft.computer_command": "Máy tính điều khiển",
"block.computercraft.computer_normal": "Máy tính",
"itemGroup.computercraft": "ComputerCraft",
"block.computercraft.computer_advanced": "Máy tính tiên tiến",
"tracking_field.computercraft.websocket_incoming.name": "Websocket đến",
"tracking_field.computercraft.websocket_outgoing.name": "Websocket đi",
"gui.computercraft.tooltip.computer_id": "ID của máy tính: %s",
"tracking_field.computercraft.coroutines_dead.name": "Coroutine bỏ đi",
"tracking_field.computercraft.coroutines_created.name": "Coroutine đã tạo",
"tracking_field.computercraft.http_download.name": "HTTP tải xuống",
"tracking_field.computercraft.http_upload.name": "HTTP tải lên",
"tracking_field.computercraft.http.name": "Yêu cầu HTTP",
"gui.computercraft.tooltip.copy": "Sao chép vào clipboard"
}

View File

@@ -1,15 +1,24 @@
local tMotd = {}
local date = os.date("*t")
if date.month == 1 and date.day == 1 then
print("Happy new year!")
elseif date.month == 12 and date.day == 24 then
print("Merry X-mas!")
elseif date.month == 10 and date.day == 31 then
print("OOoooOOOoooo! Spooky!")
else
local tMotd = {}
for sPath in string.gmatch(settings.get( "motd.path" ), "[^:]+") do
if fs.exists(sPath) then
for sLine in io.lines(sPath) do
table.insert(tMotd,sLine)
for sPath in string.gmatch(settings.get("motd.path"), "[^:]+") do
if fs.exists(sPath) then
for sLine in io.lines(sPath) do
table.insert(tMotd, sLine)
end
end
end
end
if #tMotd == 0 then
print("missingno")
else
print(tMotd[math.random(1,#tMotd)])
if #tMotd == 0 then
print("missingno")
else
print(tMotd[math.random(1, #tMotd)])
end
end

View File

@@ -3,10 +3,6 @@
"package": "dan200.computercraft.mixin",
"compatibilityLevel": "JAVA_8",
"mixins": [
"AffineTransformationAccess",
"BakedQuadAccess",
"ChatHudAccess",
"HeldItemRendererAccess",
"MinecraftServerAccess",
"MixinBlock",
"MixinEntity",
@@ -17,6 +13,10 @@
"WorldSavePathAccess"
],
"client": [
"AffineTransformationAccess",
"BakedQuadAccess",
"ChatHudAccess",
"HeldItemRendererAccess",
"MixinHeldItemRenderer",
"MixinItemFrameEntityRenderer",
"MixinMinecraftGame",

View File

@@ -432,7 +432,7 @@ do
--- Skip any whitespace
local function skip(str, pos)
local _, last = find(str, "^[ \n\r\v]+", pos)
local _, last = find(str, "^[ \n\r\t]+", pos)
if last then return last + 1 else return pos end
end
@@ -472,7 +472,7 @@ do
buf[n], n, pos = utf8.char(tonumber(num_str, 16)), n + 1, pos + 6
else
local unesc = escapes[c]
if not unesc then error_at(pos + 1, "Unknown escape character %q.", unesc) end
if not unesc then error_at(pos + 1, "Unknown escape character %q.", c) end
buf[n], n, pos = unesc, n + 1, pos + 2
end
elseif c >= '\x20' then

View File

@@ -1,15 +1,24 @@
local tMotd = {}
local date = os.date("*t")
if date.month == 1 and date.day == 1 then
print("Happy new year!")
elseif date.month == 12 and date.day == 24 then
print("Merry X-mas!")
elseif date.month == 10 and date.day == 31 then
print("OOoooOOOoooo! Spooky!")
else
local tMotd = {}
for sPath in string.gmatch(settings.get("motd.path"), "[^:]+") do
if fs.exists(sPath) then
for sLine in io.lines(sPath) do
table.insert(tMotd, sLine)
for sPath in string.gmatch(settings.get("motd.path"), "[^:]+") do
if fs.exists(sPath) then
for sLine in io.lines(sPath) do
table.insert(tMotd, sLine)
end
end
end
end
if #tMotd == 0 then
print("missingno")
else
print(tMotd[math.random(1, #tMotd)])
if #tMotd == 0 then
print("missingno")
else
print(tMotd[math.random(1, #tMotd)])
end
end

View File

@@ -1,6 +1,5 @@
{
"schemaVersion": 1,
"id": "computercraft",
"name": "CC:T for Fabric",
"version": "${version}",
@@ -26,8 +25,12 @@
},
"environment": "*",
"entrypoints": {
"main": [ "dan200.computercraft.ComputerCraft" ],
"client": ["dan200.computercraft.client.proxy.ComputerCraftProxyClient"]
"main": [
"dan200.computercraft.ComputerCraft"
],
"client": [
"dan200.computercraft.client.proxy.ComputerCraftProxyClient"
]
},
"mixins": [
"computercraft.mixins.json"

View File

@@ -0,0 +1,36 @@
local capture = require "test_helpers".capture_program
describe("The motd program", function()
local function setup_date(month, day)
stub(os, "date", function() return { month = month, day = day } end)
end
it("displays MOTD", function()
setup_date(0, 0)
local file = fs.open("/motd_check.txt", "w")
file.write("Hello World!")
file.close()
settings.set("motd.path", "/motd_check.txt")
expect(capture(stub, "motd"))
:matches { ok = true, output = "Hello World!\n", error = "" }
end)
it("displays date-specific MOTD (1/1)", function()
setup_date(1, 1)
expect(capture(stub, "motd"))
:matches { ok = true, output = "Happy new year!\n", error = "" }
end)
it("displays date-specific MOTD (10/31)", function()
setup_date(10, 31)
expect(capture(stub, "motd"))
:matches { ok = true, output = "OOoooOOOoooo! Spooky!\n", error = "" }
end)
it("displays date-specific MOTD (12/24)", function()
setup_date(12, 24)
expect(capture(stub, "motd"))
:matches { ok = true, output = "Merry X-mas!\n", error = "" }
end)
end)