mirror of
				https://github.com/SquidDev-CC/CC-Tweaked
				synced 2025-10-26 03:17:38 +00:00 
			
		
		
		
	Merge branch 'mc-1.16.x' into mc-1.17.x
This commit is contained in:
		
							
								
								
									
										2
									
								
								.github/ISSUE_TEMPLATE/bug_report.yaml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.github/ISSUE_TEMPLATE/bug_report.yaml
									
									
									
									
										vendored
									
									
								
							| @@ -8,9 +8,9 @@ body: | |||||||
|     label: Minecraft Version |     label: Minecraft Version | ||||||
|     description: What version of Minecraft are you using? |     description: What version of Minecraft are you using? | ||||||
|     options: |     options: | ||||||
|       - 1.15.x |  | ||||||
|       - 1.16.x |       - 1.16.x | ||||||
|       - 1.17.x |       - 1.17.x | ||||||
|  |       - 1.18.x | ||||||
|   validations: |   validations: | ||||||
|     required: true |     required: true | ||||||
| - type: input | - type: input | ||||||
|   | |||||||
| @@ -1,7 +1,7 @@ | |||||||
| org.gradle.jvmargs=-Xmx3G | org.gradle.jvmargs=-Xmx3G | ||||||
|  |  | ||||||
| # Mod properties | # Mod properties | ||||||
| mod_version=1.99.0 | mod_version=1.99.1 | ||||||
|  |  | ||||||
| # Minecraft properties (update mods.toml when changing) | # Minecraft properties (update mods.toml when changing) | ||||||
| mc_version=1.17.1 | mc_version=1.17.1 | ||||||
|   | |||||||
| @@ -183,6 +183,24 @@ public interface IArguments | |||||||
|         return (Map<?, ?>) value; |         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. |      * Get an argument as a double. | ||||||
|      * |      * | ||||||
| @@ -314,6 +332,27 @@ public interface IArguments | |||||||
|         return Optional.of( (Map<?, ?>) value ); |         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. |      * Get an argument as a double. | ||||||
|      * |      * | ||||||
| @@ -404,4 +443,13 @@ public interface IArguments | |||||||
|     { |     { | ||||||
|         return optTable( index ).orElse( def ); |         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() | ||||||
|  |     { | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -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 |      * Run this function on the main server thread. This should be specified for any method which interacts with | ||||||
|      * Minecraft in a thread-unsafe manner. |      * 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) |      * @see ILuaContext#issueMainThreadTask(ILuaTask) | ||||||
|      */ |      */ | ||||||
|     boolean mainThread() default false; |     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; | ||||||
| } | } | ||||||
|   | |||||||
							
								
								
									
										114
									
								
								src/main/java/dan200/computercraft/api/lua/LuaTable.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								src/main/java/dan200/computercraft/api/lua/LuaTable.java
									
									
									
									
									
										Normal 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" ); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -102,6 +102,34 @@ public final class LuaValues | |||||||
|         return new LuaException( "bad argument #" + (index + 1) + " (" + expected + " expected, got " + actual + ")" ); |         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}. |      * Ensure a numeric argument is finite (i.e. not infinite or {@link Double#NaN}. | ||||||
|      * |      * | ||||||
|   | |||||||
| @@ -98,7 +98,10 @@ public final class MethodResult | |||||||
|     { |     { | ||||||
|         Objects.requireNonNull( callback, "callback cannot be null" ); |         Objects.requireNonNull( callback, "callback cannot be null" ); | ||||||
|         return new MethodResult( new Object[] { filter }, results -> { |         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 ); |             return callback.resume( results ); | ||||||
|         } ); |         } ); | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -5,10 +5,12 @@ | |||||||
|  */ |  */ | ||||||
| package dan200.computercraft.api.lua; | package dan200.computercraft.api.lua; | ||||||
| 
 | 
 | ||||||
|  | import javax.annotation.Nonnull; | ||||||
| import javax.annotation.Nullable; | import javax.annotation.Nullable; | ||||||
| import java.util.Arrays; | import java.util.Arrays; | ||||||
| import java.util.List; | import java.util.List; | ||||||
| import java.util.Objects; | import java.util.Objects; | ||||||
|  | import java.util.Optional; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * An implementation of {@link IArguments} which wraps an array of {@link Object}. |  * 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 | public final class ObjectArguments implements IArguments | ||||||
| { | { | ||||||
|     private static final IArguments EMPTY = new ObjectArguments(); |     private static final IArguments EMPTY = new ObjectArguments(); | ||||||
|  | 
 | ||||||
|  |     private boolean released = false; | ||||||
|     private final List<Object> args; |     private final List<Object> args; | ||||||
| 
 | 
 | ||||||
|     @Deprecated |     @Deprecated | ||||||
| @@ -63,4 +67,34 @@ public final class ObjectArguments implements IArguments | |||||||
|     { |     { | ||||||
|         return args.toArray(); |         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; | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -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(); | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -8,6 +8,7 @@ package dan200.computercraft.client.gui; | |||||||
| import com.mojang.blaze3d.vertex.PoseStack; | import com.mojang.blaze3d.vertex.PoseStack; | ||||||
| import dan200.computercraft.ComputerCraft; | import dan200.computercraft.ComputerCraft; | ||||||
| import dan200.computercraft.client.gui.widgets.ComputerSidebar; | import dan200.computercraft.client.gui.widgets.ComputerSidebar; | ||||||
|  | import dan200.computercraft.client.gui.widgets.DynamicImageButton; | ||||||
| import dan200.computercraft.client.gui.widgets.WidgetTerminal; | import dan200.computercraft.client.gui.widgets.WidgetTerminal; | ||||||
| import dan200.computercraft.shared.computer.core.ClientComputer; | import dan200.computercraft.shared.computer.core.ClientComputer; | ||||||
| import dan200.computercraft.shared.computer.core.ComputerFamily; | import dan200.computercraft.shared.computer.core.ComputerFamily; | ||||||
| @@ -102,6 +103,16 @@ public abstract class ComputerScreenBase<T extends ContainerComputerBase> extend | |||||||
|         renderTooltip( stack, mouseX, mouseY ); |         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 |     @Override | ||||||
|     public final boolean mouseDragged( double x, double y, int button, double deltaX, double deltaY ) |     public final boolean mouseDragged( double x, double y, int button, double deltaX, double deltaY ) | ||||||
|     { |     { | ||||||
|   | |||||||
| @@ -10,6 +10,7 @@ import dan200.computercraft.ComputerCraft; | |||||||
| import dan200.computercraft.client.gui.widgets.WidgetTerminal; | import dan200.computercraft.client.gui.widgets.WidgetTerminal; | ||||||
| import dan200.computercraft.shared.computer.core.ClientComputer; | import dan200.computercraft.shared.computer.core.ClientComputer; | ||||||
| import dan200.computercraft.shared.computer.inventory.ContainerComputerBase; | import dan200.computercraft.shared.computer.inventory.ContainerComputerBase; | ||||||
|  | import net.minecraft.client.KeyMapping; | ||||||
| import net.minecraft.client.gui.Font; | import net.minecraft.client.gui.Font; | ||||||
| import net.minecraft.client.gui.screens.Screen; | import net.minecraft.client.gui.screens.Screen; | ||||||
| import net.minecraft.client.gui.screens.inventory.MenuAccess; | import net.minecraft.client.gui.screens.inventory.MenuAccess; | ||||||
| @@ -44,8 +45,11 @@ public class NoTermComputerScreen<T extends ContainerComputerBase> extends Scree | |||||||
|     protected void init() |     protected void init() | ||||||
|     { |     { | ||||||
|         passEvents = true; // Pass mouse vents through to the game's mouse handler. |         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.mouseHandler.grabMouse(); | ||||||
|         minecraft.screen = this; |         minecraft.screen = this; | ||||||
|  |         KeyMapping.releaseAll(); | ||||||
| 
 | 
 | ||||||
|         super.init(); |         super.init(); | ||||||
|         minecraft.keyboardHandler.setSendRepeatsToGui( true ); |         minecraft.keyboardHandler.setSendRepeatsToGui( true ); | ||||||
|   | |||||||
| @@ -130,8 +130,6 @@ public final class Generator<T> | |||||||
| 
 | 
 | ||||||
|     private void addMethod( List<NamedMethod<T>> methods, Method method, LuaFunction annotation, PeripheralType genericType, T instance ) |     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(); |         String[] names = annotation.value(); | ||||||
|         boolean isSimple = method.getReturnType() != MethodResult.class && !annotation.mainThread(); |         boolean isSimple = method.getReturnType() != MethodResult.class && !annotation.mainThread(); | ||||||
|         if( names.length == 0 ) |         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 |         // 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. |         // only come from generic sources, so this should be safe. | ||||||
|         Class<?> target = Modifier.isStatic( modifiers ) ? method.getParameterTypes()[0] : method.getDeclaringClass(); |         Class<?> target = Modifier.isStatic( modifiers ) ? method.getParameterTypes()[0] : method.getDeclaringClass(); | ||||||
| @@ -190,11 +195,13 @@ public final class Generator<T> | |||||||
|         try |         try | ||||||
|         { |         { | ||||||
|             String className = method.getDeclaringClass().getName() + "$cc$" + method.getName() + METHOD_ID.getAndIncrement(); |             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(); |             if( bytes == null ) return Optional.empty(); | ||||||
| 
 | 
 | ||||||
|             Class<?> klass = DeclaringClassLoader.INSTANCE.define( className, bytes, method.getDeclaringClass().getProtectionDomain() ); |             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 ) |         catch( ReflectiveOperationException | ClassFormatError | RuntimeException e ) | ||||||
|         { |         { | ||||||
| @@ -205,7 +212,7 @@ public final class Generator<T> | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Nullable |     @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( ".", "/" ); |         String internalName = className.replace( ".", "/" ); | ||||||
| 
 | 
 | ||||||
| @@ -238,7 +245,7 @@ public final class Generator<T> | |||||||
|             int argIndex = 0; |             int argIndex = 0; | ||||||
|             for( java.lang.reflect.Type genericArg : method.getGenericParameterTypes() ) |             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 == null ) return null; | ||||||
|                 if( loadedArg ) argIndex++; |                 if( loadedArg ) argIndex++; | ||||||
|             } |             } | ||||||
| @@ -285,7 +292,7 @@ public final class Generator<T> | |||||||
|         return cw.toByteArray(); |         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 ) |         if( genericArg == target ) | ||||||
|         { |         { | ||||||
| @@ -324,7 +331,7 @@ public final class Generator<T> | |||||||
|                 return true; |                 return true; | ||||||
|             } |             } | ||||||
| 
 | 
 | ||||||
|             String name = Reflect.getLuaName( Primitives.unwrap( klass ) ); |             String name = Reflect.getLuaName( Primitives.unwrap( klass ), unsafe ); | ||||||
|             if( name != null ) |             if( name != null ) | ||||||
|             { |             { | ||||||
|                 mw.visitVarInsn( ALOAD, 2 + context.size() ); |                 mw.visitVarInsn( ALOAD, 2 + context.size() ); | ||||||
| @@ -344,7 +351,7 @@ public final class Generator<T> | |||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         String name = arg == Object.class ? "" : Reflect.getLuaName( arg ); |         String name = arg == Object.class ? "" : Reflect.getLuaName( arg, unsafe ); | ||||||
|         if( name != null ) |         if( name != null ) | ||||||
|         { |         { | ||||||
|             if( Reflect.getRawType( method, genericArg, false ) == null ) return null; |             if( Reflect.getRawType( method, genericArg, false ) == null ) return null; | ||||||
|   | |||||||
| @@ -6,6 +6,7 @@ | |||||||
| package dan200.computercraft.core.asm; | package dan200.computercraft.core.asm; | ||||||
| 
 | 
 | ||||||
| import dan200.computercraft.ComputerCraft; | import dan200.computercraft.ComputerCraft; | ||||||
|  | import dan200.computercraft.api.lua.LuaTable; | ||||||
| import org.objectweb.asm.MethodVisitor; | import org.objectweb.asm.MethodVisitor; | ||||||
| 
 | 
 | ||||||
| import javax.annotation.Nullable; | import javax.annotation.Nullable; | ||||||
| @@ -25,7 +26,7 @@ final class Reflect | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     @Nullable |     @Nullable | ||||||
|     static String getLuaName( Class<?> klass ) |     static String getLuaName( Class<?> klass, boolean unsafe ) | ||||||
|     { |     { | ||||||
|         if( klass.isPrimitive() ) |         if( klass.isPrimitive() ) | ||||||
|         { |         { | ||||||
| @@ -39,6 +40,7 @@ final class Reflect | |||||||
|             if( klass == Map.class ) return "Table"; |             if( klass == Map.class ) return "Table"; | ||||||
|             if( klass == String.class ) return "String"; |             if( klass == String.class ) return "String"; | ||||||
|             if( klass == ByteBuffer.class ) return "Bytes"; |             if( klass == ByteBuffer.class ) return "Bytes"; | ||||||
|  |             if( klass == LuaTable.class && unsafe ) return "TableUnsafe"; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         return null; |         return null; | ||||||
|   | |||||||
| @@ -59,6 +59,10 @@ class BasicFunction extends VarArgFunction | |||||||
|             } |             } | ||||||
|             throw new LuaError( "Java Exception Thrown: " + t, 0 ); |             throw new LuaError( "Java Exception Thrown: " + t, 0 ); | ||||||
|         } |         } | ||||||
|  |         finally | ||||||
|  |         { | ||||||
|  |             arguments.releaseImmediate(); | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         if( results.getCallback() != null ) |         if( results.getCallback() != null ) | ||||||
|         { |         { | ||||||
|   | |||||||
| @@ -15,6 +15,7 @@ import dan200.computercraft.core.tracking.Tracking; | |||||||
| import dan200.computercraft.core.tracking.TrackingField; | import dan200.computercraft.core.tracking.TrackingField; | ||||||
| import dan200.computercraft.shared.util.ThreadUtils; | import dan200.computercraft.shared.util.ThreadUtils; | ||||||
| import org.squiddev.cobalt.*; | import org.squiddev.cobalt.*; | ||||||
|  | import org.squiddev.cobalt.LuaTable; | ||||||
| import org.squiddev.cobalt.compiler.CompileException; | import org.squiddev.cobalt.compiler.CompileException; | ||||||
| import org.squiddev.cobalt.compiler.LoadState; | import org.squiddev.cobalt.compiler.LoadState; | ||||||
| import org.squiddev.cobalt.debug.DebugFrame; | import org.squiddev.cobalt.debug.DebugFrame; | ||||||
|   | |||||||
| @@ -69,6 +69,10 @@ class ResultInterpreterFunction extends ResumableVarArgFunction<ResultInterprete | |||||||
|             } |             } | ||||||
|             throw new LuaError( "Java Exception Thrown: " + t, 0 ); |             throw new LuaError( "Java Exception Thrown: " + t, 0 ); | ||||||
|         } |         } | ||||||
|  |         finally | ||||||
|  |         { | ||||||
|  |             arguments.releaseImmediate(); | ||||||
|  |         } | ||||||
| 
 | 
 | ||||||
|         ILuaCallback callback = results.getCallback(); |         ILuaCallback callback = results.getCallback(); | ||||||
|         Varargs ret = machine.toValues( results.getResult() ); |         Varargs ret = machine.toValues( results.getResult() ); | ||||||
|   | |||||||
							
								
								
									
										141
									
								
								src/main/java/dan200/computercraft/core/lua/TableImpl.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								src/main/java/dan200/computercraft/core/lua/TableImpl.java
									
									
									
									
									
										Normal 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" ); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -19,6 +19,7 @@ class VarargArguments implements IArguments | |||||||
| { | { | ||||||
|     static final IArguments EMPTY = new VarargArguments( Constants.NONE ); |     static final IArguments EMPTY = new VarargArguments( Constants.NONE ); | ||||||
| 
 | 
 | ||||||
|  |     boolean released; | ||||||
|     private final Varargs varargs; |     private final Varargs varargs; | ||||||
|     private Object[] cache; |     private Object[] cache; | ||||||
| 
 | 
 | ||||||
| @@ -98,4 +99,39 @@ class VarargArguments implements IArguments | |||||||
|         LuaString str = ((LuaBaseString) value).strvalue(); |         LuaString str = ((LuaBaseString) value).strvalue(); | ||||||
|         return Optional.of( ByteBuffer.wrap( str.bytes, str.offset, str.length ).asReadOnlyBuffer() ); |         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; | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -94,9 +94,12 @@ class Expander | |||||||
|         if( !isPositive ) |         if( !isPositive ) | ||||||
|         { |         { | ||||||
|             BlockEntity otherOrigin = level.getBlockEntity( otherMonitor.toWorldPos( 0, 0 ) ); |             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; |         this.width = width; | ||||||
|   | |||||||
| @@ -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 | @{peripheral.call} function. This takes the name of our peripheral, the name of | ||||||
| the function we want to call, and then its arguments. | the function we want to call, and then its arguments. | ||||||
|  |  | ||||||
| > Some bits of the peripheral API call peripheral functions *methods* instead | :::info | ||||||
| > (for example, the @{peripheral.getMethods} function). Don't worry, they're the | Some bits of the peripheral API call peripheral functions *methods* instead | ||||||
| > same thing! | (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 | 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: | @{monitor.write|write some text to it}. We'd write the following: | ||||||
|   | |||||||
| @@ -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 | # 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). | * 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). | ||||||
|   | |||||||
| @@ -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). | * Add package.searchpath to the cc.require API. (MCJack123) | ||||||
| * Peripherals can now have multiple types. `peripheral.getType` now returns multiple values, and `peripheral.hasType` checks if a peripheral has a specific type. | * Provide a more efficient way for the Java API to consume Lua tables in certain restricted cases. | ||||||
| * 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) |  | ||||||
| 
 | 
 | ||||||
| And several bug fixes: | Several bug fixes: | ||||||
| * Fix various computer commands failing when OP level was 4. | * Fix keys being "sticky" when opening the off-hand pocket computer GUI. | ||||||
| * Various documentation fixes. (xXTurnerLP, MCJack123) | * Correctly handle broken coroutine managers resuming Java code with a `nil` event. | ||||||
| * Fix `textutils.serialize` not serialising infinity and nan values. (Wojbie) | * Prevent computer buttons stealing focus from the terminal. | ||||||
| * Wired modems now correctly clean up mounts when a peripheral is detached. | * Fix a class cast exception when a monitor is malformed in ways I do not quite understand. | ||||||
| * 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). |  | ||||||
| 
 | 
 | ||||||
| Type "help changelog" to see the full version history. | Type "help changelog" to see the full version history. | ||||||
|   | |||||||
| @@ -31,22 +31,37 @@ local function preload(package) | |||||||
|     end |     end | ||||||
| end | end | ||||||
|  |  | ||||||
| local function from_file(package, env, dir) | local function from_file(package, env) | ||||||
|     return function(name) |     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 = "" |         local sError = "" | ||||||
|         for pattern in string.gmatch(package.path, "[^;]+") do |         for pattern in string.gmatch(path, "[^;]+") do | ||||||
|             local sPath = string.gsub(pattern, "%?", fname) |             local sPath = string.gsub(pattern, "%?", fname) | ||||||
|             if sPath:sub(1, 1) ~= "/" then |             if sPath:sub(1, 1) ~= "/" then | ||||||
|                 sPath = fs.combine(dir, sPath) |                 sPath = fs.combine(dir, sPath) | ||||||
|             end |             end | ||||||
|             if fs.exists(sPath) and not fs.isDir(sPath) then |             if fs.exists(sPath) and not fs.isDir(sPath) then | ||||||
|                 local fnFile, sError = loadfile(sPath, nil, env) |                 return sPath | ||||||
|                 if fnFile then |  | ||||||
|                     return fnFile, sPath |  | ||||||
|                 else |  | ||||||
|                     return nil, sError |  | ||||||
|                 end |  | ||||||
|             else |             else | ||||||
|                 if #sError > 0 then |                 if #sError > 0 then | ||||||
|                     sError = sError .. "\n  " |                     sError = sError .. "\n  " | ||||||
| @@ -118,7 +133,8 @@ local function make_package(env, dir) | |||||||
|     end |     end | ||||||
|     package.config = "/\n;\n?\n!\n-" |     package.config = "/\n;\n?\n!\n-" | ||||||
|     package.preload = {} |     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 |     return make_require(package), package | ||||||
| end | end | ||||||
|   | |||||||
| @@ -97,7 +97,7 @@ public class ComputerTestDelegate | |||||||
| 
 | 
 | ||||||
|         if( REPORT_PATH.delete() ) ComputerCraft.log.info( "Deleted previous coverage report." ); |         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 ); |         IWritableMount mount = new FileMount( new File( "test-files/mount" ), 10_000_000 ); | ||||||
| 
 | 
 | ||||||
|         // Remove any existing files |         // Remove any existing files | ||||||
|   | |||||||
| @@ -120,6 +120,13 @@ public class GeneratorTest | |||||||
|             contramap( notNullValue(), "callback", MethodResult::getCallback ) ); |             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 |     public static class Basic | ||||||
|     { |     { | ||||||
|         @LuaFunction |         @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 ) |     private static <T> T find( Collection<NamedMethod<T>> methods, String name ) | ||||||
|     { |     { | ||||||
|         return methods.stream() |         return methods.stream() | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Jonathan Coates
					Jonathan Coates