1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2025-11-02 14:43:00 +00:00

Compare commits

...

11 Commits

Author SHA1 Message Date
Jonathan Coates
6196aae488 Merge branch 'mc-1.16.x' into mc-1.17.x 2021-12-11 07:49:33 +00:00
Jonathan Coates
92a0ef2b75 Bump CC:T version 2021-12-11 07:37:10 +00:00
Jonathan Coates
1f6e0f287d Ensure the origin monitor is valid too
Blurh, still not sure if this is Correct or anything, but have no clue
what's causing this. Fixes #985. Hopefully.
2021-12-10 13:13:31 +00:00
Jonathan Coates
0e4b7a5a75 Prevent terminal buttons stealing focus
I have shutdown my computer by accident far too many times now.
2021-12-08 23:16:53 +00:00
Jonathan Coates
47ad7a35dc Fix NPE when pulling an event with no type
I assume people have broken coroutine dispatchers - I didn't think it
was possible to queue an actual event with no type.

See cc-tweaked/cc-restitched#31. Will fix it too once merged downstream!
2021-12-08 22:47:21 +00:00
Jonathan Coates
3eab2a9b57 Add support for a zero-copy Lua table
The API is entirely designed for the needs of the speaker right now, so
doesn't do much else.
2021-12-07 18:27:29 +00:00
Jonathan Coates
c4024a4c4c Use an admonition instead 2021-12-02 22:41:58 +00:00
Jonathan Coates
f5fb82cd7d Merge pull request #977 from MCJack123/patch-9
Add package.searchpath
2021-12-02 12:34:07 +00:00
MCJack123
e18ba8a2c2 Add package.searchpath 2021-12-01 18:55:24 -05:00
Jonathan Coates
422bfdb60d Add 1.18 and remove 1.15 from the issue template
No, we're not pushing it to Curse yet, but while I remember.
2021-12-01 20:24:37 +00:00
Jonathan Coates
1851ed31cd Release keys when opening the offhand pocket computer screen
Opening a screen KeyBinding.releaseAll(), which forces all inputs to be
considered released. However, our init() function then calls
grabMouse(), which calls Keybinding.setAll(), undoing this work.

The fix we're going for here is to call releaseAll() one more time[^1]
after grabbing the mouse. I think if this becomes any more of a problem,
we should roll our own grabMouse which _doesn't_ implement any specific
behaviour.

Fixes #975

[^1]: Obvious problem here is that we do minecraft.screen=xyz rather
      than setScreen. We need to - otherwise we'd just hit a stack
      overflow - but it's not great.
2021-12-01 20:09:38 +00:00
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.17.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()