1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-01-15 03:35:42 +00:00

Merge branch 'mc-1.17.x' into mc-1.18.x

This commit is contained in:
Jonathan Coates 2021-12-11 07:50:18 +00:00
commit 7c373c6e06
No known key found for this signature in database
GPG Key ID: B9E431FF07C98D06
25 changed files with 611 additions and 58 deletions

View File

@ -8,9 +8,9 @@ body:
label: Minecraft Version
description: What version of Minecraft are you using?
options:
- 1.15.x
- 1.16.x
- 1.17.x
- 1.18.x
validations:
required: true
- type: input

View File

@ -1,7 +1,7 @@
org.gradle.jvmargs=-Xmx3G
# Mod properties
mod_version=1.99.0
mod_version=1.99.1
# Minecraft properties (update mods.toml when changing)
mc_version=1.18.1

View File

@ -183,6 +183,24 @@ public interface IArguments
return (Map<?, ?>) value;
}
/**
* Get an argument as a table in an unsafe manner.
*
* Classes implementing this interface may choose to implement a more optimised version which does not copy the
* table, instead returning a wrapper version, making it more efficient. However, the caller must guarantee that
* they do not access off the computer thread (and so should not be used with main-thread functions) or once the
* function call has finished (for instance, in callbacks).
*
* @param index The argument number.
* @return The argument's value.
* @throws LuaException If the value is not a table.
*/
@Nonnull
default LuaTable<?, ?> getTableUnsafe( int index ) throws LuaException
{
return new ObjectLuaTable( getTable( index ) );
}
/**
* Get an argument as a double.
*
@ -314,6 +332,27 @@ public interface IArguments
return Optional.of( (Map<?, ?>) value );
}
/**
* Get an argument as a table in an unsafe manner.
*
* Classes implementing this interface may choose to implement a more optimised version which does not copy the
* table, instead returning a wrapper version, making it more efficient. However, the caller must guarantee that
* they do not access off the computer thread (and so should not be used with main-thread functions) or once the
* function call has finished (for instance, in callbacks).
*
* @param index The argument number.
* @return The argument's value, or {@link Optional#empty()} if not present.
* @throws LuaException If the value is not a table.
*/
@Nonnull
default Optional<LuaTable<?, ?>> optTableUnsafe( int index ) throws LuaException
{
Object value = get( index );
if( value == null ) return Optional.empty();
if( !(value instanceof Map) ) throw LuaValues.badArgumentOf( index, "map", value );
return Optional.of( new ObjectLuaTable( (Map<?, ?>) value ) );
}
/**
* Get an argument as a double.
*
@ -404,4 +443,13 @@ public interface IArguments
{
return optTable( index ).orElse( def );
}
/**
* This is called when the current function finishes, before any main thread tasks have run.
*
* Called when the current function returns, and so some values are no longer guaranteed to be safe to access.
*/
default void releaseImmediate()
{
}
}

View File

@ -51,8 +51,17 @@ public @interface LuaFunction
* Run this function on the main server thread. This should be specified for any method which interacts with
* Minecraft in a thread-unsafe manner.
*
* @return Whether this functi
* @return Whether this function should be run on the main thread.
* @see ILuaContext#issueMainThreadTask(ILuaTask)
*/
boolean mainThread() default false;
/**
* Allow using "unsafe" arguments, such {@link IArguments#getTableUnsafe(int)}.
*
* This is incompatible with {@link #mainThread()}.
*
* @return Whether this function supports unsafe arguments.
*/
boolean unsafe() default false;
}

View File

@ -0,0 +1,114 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. This API may be redistributed unmodified and in full only.
* For help using the API, and posting your mods, visit the forums at computercraft.info.
*/
package dan200.computercraft.api.lua;
import org.jetbrains.annotations.Nullable;
import javax.annotation.Nonnull;
import java.util.Map;
import static dan200.computercraft.api.lua.LuaValues.*;
public interface LuaTable<K, V> extends Map<K, V>
{
/**
* Compute the length of the array part of this table.
*
* @return This table's length.
*/
default int length()
{
int size = 0;
while( containsKey( (double) (size + 1) ) ) size++;
return size;
}
/**
* Get an array entry as an integer.
*
* @param index The index in the table, starting at 1.
* @return The table's value.
* @throws LuaException If the value is not an integer.
*/
default long getLong( int index ) throws LuaException
{
Object value = get( (double) index );
if( !(value instanceof Number) ) throw badTableItem( index, "number", getType( value ) );
Number number = (Number) value;
double asDouble = number.doubleValue();
if( !Double.isFinite( asDouble ) ) throw badTableItem( index, "number", getNumericType( asDouble ) );
return number.longValue();
}
/**
* Get a table entry as an integer.
*
* @param key The name of the field in the table.
* @return The table's value.
* @throws LuaException If the value is not an integer.
*/
default long getLong( String key ) throws LuaException
{
Object value = get( key );
if( !(value instanceof Number) ) throw badField( key, "number", getType( value ) );
Number number = (Number) value;
double asDouble = number.doubleValue();
if( !Double.isFinite( asDouble ) ) throw badField( key, "number", getNumericType( asDouble ) );
return number.longValue();
}
/**
* Get an array entry as an integer.
*
* @param index The index in the table, starting at 1.
* @return The table's value.
* @throws LuaException If the value is not an integer.
*/
default int getInt( int index ) throws LuaException
{
return (int) getLong( index );
}
/**
* Get a table entry as an integer.
*
* @param key The name of the field in the table.
* @return The table's value.
* @throws LuaException If the value is not an integer.
*/
default int getInt( String key ) throws LuaException
{
return (int) getLong( key );
}
@Nullable
@Override
default V put( K o, V o2 )
{
throw new UnsupportedOperationException( "Cannot modify LuaTable" );
}
@Override
default V remove( Object o )
{
throw new UnsupportedOperationException( "Cannot modify LuaTable" );
}
@Override
default void putAll( @Nonnull Map<? extends K, ? extends V> map )
{
throw new UnsupportedOperationException( "Cannot modify LuaTable" );
}
@Override
default void clear()
{
throw new UnsupportedOperationException( "Cannot modify LuaTable" );
}
}

View File

@ -102,6 +102,34 @@ public final class LuaValues
return new LuaException( "bad argument #" + (index + 1) + " (" + expected + " expected, got " + actual + ")" );
}
/**
* Construct a table item exception, from an expected and actual type.
*
* @param index The index into the table, starting from 1.
* @param expected The expected type for this table item.
* @param actual The provided type for this table item.
* @return The constructed exception, which should be thrown immediately.
*/
@Nonnull
public static LuaException badTableItem( int index, @Nonnull String expected, @Nonnull String actual )
{
return new LuaException( "table item #" + index + " is not " + expected + " (got " + actual + ")" );
}
/**
* Construct a field exception, from an expected and actual type.
*
* @param key The name of the field.
* @param expected The expected type for this table item.
* @param actual The provided type for this table item.
* @return The constructed exception, which should be thrown immediately.
*/
@Nonnull
public static LuaException badField( String key, @Nonnull String expected, @Nonnull String actual )
{
return new LuaException( "field " + key + " is not " + expected + " (got " + actual + ")" );
}
/**
* Ensure a numeric argument is finite (i.e. not infinite or {@link Double#NaN}.
*

View File

@ -98,7 +98,10 @@ public final class MethodResult
{
Objects.requireNonNull( callback, "callback cannot be null" );
return new MethodResult( new Object[] { filter }, results -> {
if( results.length >= 1 && results[0].equals( "terminate" ) ) throw new LuaException( "Terminated", 0 );
if( results.length >= 1 && Objects.equals( results[0], "terminate" ) )
{
throw new LuaException( "Terminated", 0 );
}
return callback.resume( results );
} );
}

View File

@ -5,10 +5,12 @@
*/
package dan200.computercraft.api.lua;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
/**
* An implementation of {@link IArguments} which wraps an array of {@link Object}.
@ -16,6 +18,8 @@ import java.util.Objects;
public final class ObjectArguments implements IArguments
{
private static final IArguments EMPTY = new ObjectArguments();
private boolean released = false;
private final List<Object> args;
@Deprecated
@ -63,4 +67,34 @@ public final class ObjectArguments implements IArguments
{
return args.toArray();
}
@Nonnull
@Override
public LuaTable<?, ?> getTableUnsafe( int index ) throws LuaException
{
if( released )
{
throw new IllegalStateException( "Cannot use getTableUnsafe after IArguments has been released" );
}
return IArguments.super.getTableUnsafe( index );
}
@Nonnull
@Override
public Optional<LuaTable<?, ?>> optTableUnsafe( int index ) throws LuaException
{
if( released )
{
throw new IllegalStateException( "Cannot use optTableUnsafe after IArguments has been released" );
}
return IArguments.super.optTableUnsafe( index );
}
@Override
public void releaseImmediate()
{
released = true;
}
}

View File

@ -0,0 +1,73 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2021. This API may be redistributed unmodified and in full only.
* For help using the API, and posting your mods, visit the forums at computercraft.info.
*/
package dan200.computercraft.api.lua;
import javax.annotation.Nonnull;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
public class ObjectLuaTable implements LuaTable<Object, Object>
{
private final Map<Object, Object> map;
public ObjectLuaTable( Map<?, ?> map )
{
this.map = Collections.unmodifiableMap( map );
}
@Override
public int size()
{
return map.size();
}
@Override
public boolean isEmpty()
{
return map.isEmpty();
}
@Override
public boolean containsKey( Object o )
{
return map.containsKey( o );
}
@Override
public boolean containsValue( Object o )
{
return map.containsKey( o );
}
@Override
public Object get( Object o )
{
return map.get( o );
}
@Nonnull
@Override
public Set<Object> keySet()
{
return map.keySet();
}
@Nonnull
@Override
public Collection<Object> values()
{
return map.values();
}
@Nonnull
@Override
public Set<Entry<Object, Object>> entrySet()
{
return map.entrySet();
}
}

View File

@ -8,6 +8,7 @@ package dan200.computercraft.client.gui;
import com.mojang.blaze3d.vertex.PoseStack;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.gui.widgets.ComputerSidebar;
import dan200.computercraft.client.gui.widgets.DynamicImageButton;
import dan200.computercraft.client.gui.widgets.WidgetTerminal;
import dan200.computercraft.shared.computer.core.ClientComputer;
import dan200.computercraft.shared.computer.core.ComputerFamily;
@ -102,6 +103,16 @@ public abstract class ComputerScreenBase<T extends ContainerComputerBase> extend
renderTooltip( stack, mouseX, mouseY );
}
@Override
public boolean mouseClicked( double x, double y, int button )
{
boolean changed = super.mouseClicked( x, y, button );
// Clicking the terminate/shutdown button steals focus, which means then pressing "enter" will click the button
// again. Restore the focus to the terminal in these cases.
if( getFocused() instanceof DynamicImageButton ) setFocused( terminal );
return changed;
}
@Override
public final boolean mouseDragged( double x, double y, int button, double deltaX, double deltaY )
{

View File

@ -10,6 +10,7 @@ import dan200.computercraft.ComputerCraft;
import dan200.computercraft.client.gui.widgets.WidgetTerminal;
import dan200.computercraft.shared.computer.core.ClientComputer;
import dan200.computercraft.shared.computer.inventory.ContainerComputerBase;
import net.minecraft.client.KeyMapping;
import net.minecraft.client.gui.Font;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.gui.screens.inventory.MenuAccess;
@ -44,8 +45,11 @@ public class NoTermComputerScreen<T extends ContainerComputerBase> extends Scree
protected void init()
{
passEvents = true; // Pass mouse vents through to the game's mouse handler.
// First ensure we're still grabbing the mouse, so the user can look around. Then reset bits of state that
// grabbing unsets.
minecraft.mouseHandler.grabMouse();
minecraft.screen = this;
KeyMapping.releaseAll();
super.init();
minecraft.keyboardHandler.setSendRepeatsToGui( true );

View File

@ -130,8 +130,6 @@ public final class Generator<T>
private void addMethod( List<NamedMethod<T>> methods, Method method, LuaFunction annotation, PeripheralType genericType, T instance )
{
if( annotation.mainThread() ) instance = wrap.apply( instance );
String[] names = annotation.value();
boolean isSimple = method.getReturnType() != MethodResult.class && !annotation.mainThread();
if( names.length == 0 )
@ -183,6 +181,13 @@ public final class Generator<T>
}
}
LuaFunction annotation = method.getAnnotation( LuaFunction.class );
if( annotation.unsafe() && annotation.mainThread() )
{
ComputerCraft.log.error( "Lua Method {} cannot use unsafe and mainThread", name );
return Optional.empty();
}
// We have some rather ugly handling of static methods in both here and the main generate function. Static methods
// only come from generic sources, so this should be safe.
Class<?> target = Modifier.isStatic( modifiers ) ? method.getParameterTypes()[0] : method.getDeclaringClass();
@ -190,11 +195,13 @@ public final class Generator<T>
try
{
String className = method.getDeclaringClass().getName() + "$cc$" + method.getName() + METHOD_ID.getAndIncrement();
byte[] bytes = generate( className, target, method );
byte[] bytes = generate( className, target, method, annotation.unsafe() );
if( bytes == null ) return Optional.empty();
Class<?> klass = DeclaringClassLoader.INSTANCE.define( className, bytes, method.getDeclaringClass().getProtectionDomain() );
return Optional.of( klass.asSubclass( base ).getDeclaredConstructor().newInstance() );
T instance = klass.asSubclass( base ).getDeclaredConstructor().newInstance();
return Optional.of( annotation.mainThread() ? wrap.apply( instance ) : instance );
}
catch( ReflectiveOperationException | ClassFormatError | RuntimeException e )
{
@ -205,7 +212,7 @@ public final class Generator<T>
}
@Nullable
private byte[] generate( String className, Class<?> target, Method method )
private byte[] generate( String className, Class<?> target, Method method, boolean unsafe )
{
String internalName = className.replace( ".", "/" );
@ -238,7 +245,7 @@ public final class Generator<T>
int argIndex = 0;
for( java.lang.reflect.Type genericArg : method.getGenericParameterTypes() )
{
Boolean loadedArg = loadArg( mw, target, method, genericArg, argIndex );
Boolean loadedArg = loadArg( mw, target, method, unsafe, genericArg, argIndex );
if( loadedArg == null ) return null;
if( loadedArg ) argIndex++;
}
@ -285,7 +292,7 @@ public final class Generator<T>
return cw.toByteArray();
}
private Boolean loadArg( MethodVisitor mw, Class<?> target, Method method, java.lang.reflect.Type genericArg, int argIndex )
private Boolean loadArg( MethodVisitor mw, Class<?> target, Method method, boolean unsafe, java.lang.reflect.Type genericArg, int argIndex )
{
if( genericArg == target )
{
@ -324,7 +331,7 @@ public final class Generator<T>
return true;
}
String name = Reflect.getLuaName( Primitives.unwrap( klass ) );
String name = Reflect.getLuaName( Primitives.unwrap( klass ), unsafe );
if( name != null )
{
mw.visitVarInsn( ALOAD, 2 + context.size() );
@ -344,7 +351,7 @@ public final class Generator<T>
return true;
}
String name = arg == Object.class ? "" : Reflect.getLuaName( arg );
String name = arg == Object.class ? "" : Reflect.getLuaName( arg, unsafe );
if( name != null )
{
if( Reflect.getRawType( method, genericArg, false ) == null ) return null;

View File

@ -6,6 +6,7 @@
package dan200.computercraft.core.asm;
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.lua.LuaTable;
import org.objectweb.asm.MethodVisitor;
import javax.annotation.Nullable;
@ -25,7 +26,7 @@ final class Reflect
}
@Nullable
static String getLuaName( Class<?> klass )
static String getLuaName( Class<?> klass, boolean unsafe )
{
if( klass.isPrimitive() )
{
@ -39,6 +40,7 @@ final class Reflect
if( klass == Map.class ) return "Table";
if( klass == String.class ) return "String";
if( klass == ByteBuffer.class ) return "Bytes";
if( klass == LuaTable.class && unsafe ) return "TableUnsafe";
}
return null;

View File

@ -59,6 +59,10 @@ class BasicFunction extends VarArgFunction
}
throw new LuaError( "Java Exception Thrown: " + t, 0 );
}
finally
{
arguments.releaseImmediate();
}
if( results.getCallback() != null )
{

View File

@ -15,6 +15,7 @@ import dan200.computercraft.core.tracking.Tracking;
import dan200.computercraft.core.tracking.TrackingField;
import dan200.computercraft.shared.util.ThreadUtils;
import org.squiddev.cobalt.*;
import org.squiddev.cobalt.LuaTable;
import org.squiddev.cobalt.compiler.CompileException;
import org.squiddev.cobalt.compiler.LoadState;
import org.squiddev.cobalt.debug.DebugFrame;

View File

@ -69,6 +69,10 @@ class ResultInterpreterFunction extends ResumableVarArgFunction<ResultInterprete
}
throw new LuaError( "Java Exception Thrown: " + t, 0 );
}
finally
{
arguments.releaseImmediate();
}
ILuaCallback callback = results.getCallback();
Varargs ret = machine.toValues( results.getResult() );

View File

@ -0,0 +1,141 @@
/*
* 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.core.lua;
import dan200.computercraft.api.lua.LuaException;
import dan200.computercraft.api.lua.LuaValues;
import org.squiddev.cobalt.*;
import javax.annotation.Nonnull;
import java.util.*;
import static dan200.computercraft.api.lua.LuaValues.badTableItem;
import static dan200.computercraft.api.lua.LuaValues.getNumericType;
class TableImpl implements dan200.computercraft.api.lua.LuaTable<Object, Object>
{
private final VarargArguments arguments;
private final LuaTable table;
private Map<Object, Object> backingMap;
TableImpl( VarargArguments arguments, LuaTable table )
{
this.arguments = arguments;
this.table = table;
}
@Override
public int size()
{
checkValid();
try
{
return table.keyCount();
}
catch( LuaError e )
{
throw new IllegalStateException( e );
}
}
@Override
public int length()
{
return table.length();
}
@Override
public long getLong( int index ) throws LuaException
{
LuaValue value = table.rawget( index );
if( !(value instanceof LuaNumber) ) throw LuaValues.badTableItem( index, "number", value.typeName() );
if( value instanceof LuaInteger ) return value.toInteger();
double number = value.toDouble();
if( !Double.isFinite( number ) ) throw badTableItem( index, "number", getNumericType( number ) );
return (long) number;
}
@Override
public boolean isEmpty()
{
checkValid();
try
{
return table.next( Constants.NIL ).first().isNil();
}
catch( LuaError e )
{
throw new IllegalStateException( e );
}
}
@Nonnull
private LuaValue getImpl( Object o )
{
checkValid();
if( o instanceof String ) return table.rawget( (String) o );
if( o instanceof Integer ) return table.rawget( (Integer) o );
return Constants.NIL;
}
@Override
public boolean containsKey( Object o )
{
return !getImpl( o ).isNil();
}
@Override
public Object get( Object o )
{
return CobaltLuaMachine.toObject( getImpl( o ), null );
}
@Nonnull
private Map<Object, Object> getBackingMap()
{
checkValid();
if( backingMap != null ) return backingMap;
return backingMap = Collections.unmodifiableMap(
Objects.requireNonNull( (Map<?, ?>) CobaltLuaMachine.toObject( table, null ) )
);
}
@Override
public boolean containsValue( Object o )
{
return getBackingMap().containsKey( o );
}
@Nonnull
@Override
public Set<Object> keySet()
{
return getBackingMap().keySet();
}
@Nonnull
@Override
public Collection<Object> values()
{
return getBackingMap().values();
}
@Nonnull
@Override
public Set<Entry<Object, Object>> entrySet()
{
return getBackingMap().entrySet();
}
private void checkValid()
{
if( arguments.released )
{
throw new IllegalStateException( "Cannot use LuaTable after IArguments has been released" );
}
}
}

View File

@ -19,6 +19,7 @@ class VarargArguments implements IArguments
{
static final IArguments EMPTY = new VarargArguments( Constants.NONE );
boolean released;
private final Varargs varargs;
private Object[] cache;
@ -98,4 +99,39 @@ class VarargArguments implements IArguments
LuaString str = ((LuaBaseString) value).strvalue();
return Optional.of( ByteBuffer.wrap( str.bytes, str.offset, str.length ).asReadOnlyBuffer() );
}
@Nonnull
@Override
public dan200.computercraft.api.lua.LuaTable<?, ?> getTableUnsafe( int index ) throws LuaException
{
if( released )
{
throw new IllegalStateException( "Cannot use getTableUnsafe after IArguments has been released" );
}
LuaValue value = varargs.arg( index + 1 );
if( !(value instanceof LuaTable) ) throw LuaValues.badArgument( index, "table", value.typeName() );
return new TableImpl( this, (LuaTable) value );
}
@Nonnull
@Override
public Optional<dan200.computercraft.api.lua.LuaTable<?, ?>> optTableUnsafe( int index ) throws LuaException
{
if( released )
{
throw new IllegalStateException( "Cannot use optTableUnsafe after IArguments has been released" );
}
LuaValue value = varargs.arg( index + 1 );
if( value.isNil() ) return Optional.empty();
if( !(value instanceof LuaTable) ) throw LuaValues.badArgument( index, "table", value.typeName() );
return Optional.of( new TableImpl( this, (LuaTable) value ) );
}
@Override
public void releaseImmediate()
{
released = true;
}
}

View File

@ -94,9 +94,12 @@ class Expander
if( !isPositive )
{
BlockEntity otherOrigin = level.getBlockEntity( otherMonitor.toWorldPos( 0, 0 ) );
if( otherOrigin == null || !origin.isCompatible( (TileMonitor) otherOrigin ) ) return false;
if( !(otherOrigin instanceof TileMonitor originMonitor) || !origin.isCompatible( originMonitor ) )
{
return false;
}
origin = (TileMonitor) otherOrigin;
origin = originMonitor;
}
this.width = width;

View File

@ -28,9 +28,11 @@ Once you have the name of a peripheral, you can call functions on it using the
@{peripheral.call} function. This takes the name of our peripheral, the name of
the function we want to call, and then its arguments.
> Some bits of the peripheral API call peripheral functions *methods* instead
> (for example, the @{peripheral.getMethods} function). Don't worry, they're the
> same thing!
:::info
Some bits of the peripheral API call peripheral functions *methods* instead
(for example, the @{peripheral.getMethods} function). Don't worry, they're the
same thing!
:::
Let's say we have a monitor above our computer (and so "top") and want to
@{monitor.write|write some text to it}. We'd write the following:

View File

@ -1,3 +1,14 @@
# New features in CC: Tweaked 1.99.1
* Add package.searchpath to the cc.require API. (MCJack123)
* Provide a more efficient way for the Java API to consume Lua tables in certain restricted cases.
Several bug fixes:
* Fix keys being "sticky" when opening the off-hand pocket computer GUI.
* Correctly handle broken coroutine managers resuming Java code with a `nil` event.
* Prevent computer buttons stealing focus from the terminal.
* Fix a class cast exception when a monitor is malformed in ways I do not quite understand.
# New features in CC: Tweaked 1.99.0
* Pocket computers in their offhand will open without showing a terminal. You can look around and interact with the world, but your keyboard will be forwarded to the computer. (Wojbie, MagGen-hub).

View File

@ -1,32 +1,12 @@
New features in CC: Tweaked 1.99.0
New features in CC: Tweaked 1.99.1
* Pocket computers in their offhand will open without showing a terminal. You can look around and interact with the world, but your keyboard will be forwarded to the computer. (Wojbie, MagGen-hub).
* Peripherals can now have multiple types. `peripheral.getType` now returns multiple values, and `peripheral.hasType` checks if a peripheral has a specific type.
* Add several missing keys to the `keys` table. (ralphgod3)
* Add feature introduction/changed version information to the documentation. (MCJack123)
* Increase the file upload limit to 512KiB.
* Rednet can now handle computer IDs larger than 65535. (Ale32bit)
* Optimise deduplication of rednet messages (MCJack123)
* Make `term.blit` colours case insensitive. (Ocawesome101)
* Add a new `about` program for easier version identification. (MCJack123)
* Optimise peripheral calls in `rednet.run`. (xAnavrins)
* Add dimension parameter to `commands.getBlockInfo`.
* Add `cc.pretty.pretty_print` helper function (Lupus590).
* Add back JEI integration.
* Turtle and pocket computer upgrades can now be added and modified with data packs.
* Various translation updates (MORIMORI3017, Ale2Bit, mindy15963)
* Add package.searchpath to the cc.require API. (MCJack123)
* Provide a more efficient way for the Java API to consume Lua tables in certain restricted cases.
And several bug fixes:
* Fix various computer commands failing when OP level was 4.
* Various documentation fixes. (xXTurnerLP, MCJack123)
* Fix `textutils.serialize` not serialising infinity and nan values. (Wojbie)
* Wired modems now correctly clean up mounts when a peripheral is detached.
* Fix incorrect turtle and pocket computer upgrade recipes in the recipe book.
* Fix speakers not playing sounds added via resource packs which are not registered in-game.
* Fix speaker upgrades sending packets after the server has stopped.
* Monitor sizing has been rewritten, hopefully making it more stable.
* Peripherals are now invalidated when the computer ticks, rather than when the peripheral changes.
* Fix printouts and pocket computers rendering at fullbright when in item frames.
* All mod blocks now have an effective tool (pickaxe).
Several bug fixes:
* Fix keys being "sticky" when opening the off-hand pocket computer GUI.
* Correctly handle broken coroutine managers resuming Java code with a `nil` event.
* Prevent computer buttons stealing focus from the terminal.
* Fix a class cast exception when a monitor is malformed in ways I do not quite understand.
Type "help changelog" to see the full version history.

View File

@ -31,22 +31,37 @@ local function preload(package)
end
end
local function from_file(package, env, dir)
local function from_file(package, env)
return function(name)
local fname = string.gsub(name, "%.", "/")
local sPath, sError = package.searchpath(name, package.path)
if not sPath then
return nil, sError
end
local fnFile, sError = loadfile(sPath, nil, env)
if fnFile then
return fnFile, sPath
else
return nil, sError
end
end
end
local function make_searchpath(dir)
return function(name, path, sep, rep)
expect(1, name, "string")
expect(2, path, "string")
sep = expect(3, sep, "string", "nil") or "."
rep = expect(4, rep, "string", "nil") or "/"
local fname = string.gsub(name, sep:gsub("%.", "%%%."), rep)
local sError = ""
for pattern in string.gmatch(package.path, "[^;]+") do
for pattern in string.gmatch(path, "[^;]+") do
local sPath = string.gsub(pattern, "%?", fname)
if sPath:sub(1, 1) ~= "/" then
sPath = fs.combine(dir, sPath)
end
if fs.exists(sPath) and not fs.isDir(sPath) then
local fnFile, sError = loadfile(sPath, nil, env)
if fnFile then
return fnFile, sPath
else
return nil, sError
end
return sPath
else
if #sError > 0 then
sError = sError .. "\n "
@ -118,7 +133,8 @@ local function make_package(env, dir)
end
package.config = "/\n;\n?\n!\n-"
package.preload = {}
package.loaders = { preload(package), from_file(package, env, dir) }
package.loaders = { preload(package), from_file(package, env) }
package.searchpath = make_searchpath(dir)
return make_require(package), package
end

View File

@ -97,7 +97,7 @@ public class ComputerTestDelegate
if( REPORT_PATH.delete() ) ComputerCraft.log.info( "Deleted previous coverage report." );
Terminal term = new Terminal( 80, 30 );
Terminal term = new Terminal( 80, 100 );
IWritableMount mount = new FileMount( new File( "test-files/mount" ), 10_000_000 );
// Remove any existing files

View File

@ -120,6 +120,13 @@ public class GeneratorTest
contramap( notNullValue(), "callback", MethodResult::getCallback ) );
}
@Test
public void testUnsafe()
{
List<NamedMethod<LuaMethod>> methods = LuaMethod.GENERATOR.getMethods( Unsafe.class );
assertThat( methods, contains( named( "withUnsafe" ) ) );
}
public static class Basic
{
@LuaFunction
@ -222,6 +229,21 @@ public class GeneratorTest
{}
}
public static class Unsafe
{
@LuaFunction( unsafe = true )
public final void withUnsafe( LuaTable<?, ?> table )
{}
@LuaFunction
public final void withoutUnsafe( LuaTable<?, ?> table )
{}
@LuaFunction( unsafe = true, mainThread = true )
public final void invalid( LuaTable<?, ?> table )
{}
}
private static <T> T find( Collection<NamedMethod<T>> methods, String name )
{
return methods.stream()