diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index f43a6ad0e..580780514 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -156,9 +156,13 @@ + + + + - + diff --git a/config/pre-commit/config.yml b/config/pre-commit/config.yml index 9eee19122..639668531 100644 --- a/config/pre-commit/config.yml +++ b/config/pre-commit/config.yml @@ -2,7 +2,7 @@ # See https://pre-commit.com/hooks.html for more hooks repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v3.2.0 + rev: v4.0.1 hooks: - id: trailing-whitespace - id: end-of-file-fixer @@ -16,7 +16,7 @@ repos: exclude: "tsconfig\\.json$" - repo: https://github.com/editorconfig-checker/editorconfig-checker.python - rev: 2.3.5 + rev: 2.3.54 hooks: - id: editorconfig-checker args: ['-disable-indentation'] diff --git a/package-lock.json b/package-lock.json index 8a1a10d03..9a4786579 100644 --- a/package-lock.json +++ b/package-lock.json @@ -21,9 +21,9 @@ } }, "node_modules/@rollup/plugin-typescript": { - "version": "8.2.5", - "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.2.5.tgz", - "integrity": "sha512-QL/LvDol/PAGB2O0S7/+q2HpSUNodpw7z6nGn9BfoVCPOZ0r4EALrojFU29Bkoi2Hr2jgTocTejJ5GGWZfOxbQ==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.3.0.tgz", + "integrity": "sha512-I5FpSvLbtAdwJ+naznv+B4sjXZUcIvLLceYpITAn7wAP8W0wqc5noLdGIp9HGVntNhRWXctwPYrSSFQxtl0FPA==", "dev": true, "dependencies": { "@rollup/pluginutils": "^3.1.0", @@ -112,9 +112,9 @@ } }, "node_modules/is-core-module": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz", - "integrity": "sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", + "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", "dev": true, "dependencies": { "has": "^1.0.3" @@ -142,9 +142,9 @@ } }, "node_modules/preact": { - "version": "10.5.14", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.5.14.tgz", - "integrity": "sha512-KojoltCrshZ099ksUZ2OQKfbH66uquFoxHSbnwKbTJHeQNvx42EmC7wQVWNuDt6vC5s3nudRHFtKbpY4ijKlaQ==", + "version": "10.5.15", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.5.15.tgz", + "integrity": "sha512-5chK29n6QcJc3m1lVrKQSQ+V7K1Gb8HeQY6FViQ5AxCAEGu3DaHffWNDkC9+miZgsLvbvU9rxbV1qinGHMHzqA==", "funding": { "type": "opencollective", "url": "https://opencollective.com/preact" @@ -177,9 +177,9 @@ } }, "node_modules/rollup": { - "version": "2.56.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.56.2.tgz", - "integrity": "sha512-s8H00ZsRi29M2/lGdm1u8DJpJ9ML8SUOpVVBd33XNeEeL3NVaTiUcSBHzBdF3eAyR0l7VSpsuoVUGrRHq7aPwQ==", + "version": "2.60.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.60.0.tgz", + "integrity": "sha512-cHdv9GWd58v58rdseC8e8XIaPUo8a9cgZpnCMMDGZFDZKEODOiPPEQFXLriWr/TjXzhPPmG5bkAztPsOARIcGQ==", "dev": true, "bin": { "rollup": "dist/bin/rollup" @@ -201,9 +201,9 @@ } }, "node_modules/source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "dependencies": { "buffer-from": "^1.0.0", @@ -220,20 +220,28 @@ } }, "node_modules/terser": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.7.1.tgz", - "integrity": "sha512-b3e+d5JbHAe/JSjwsC3Zn55wsBIM7AsHLjKxT31kGCldgbpFePaFo+PiddtO6uwRZWRw7sPXmAN8dTW61xmnSg==", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz", + "integrity": "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==", "dev": true, "dependencies": { "commander": "^2.20.0", "source-map": "~0.7.2", - "source-map-support": "~0.5.19" + "source-map-support": "~0.5.20" }, "bin": { "terser": "bin/terser" }, "engines": { "node": ">=10" + }, + "peerDependencies": { + "acorn": "^8.5.0" + }, + "peerDependenciesMeta": { + "acorn": { + "optional": true + } } }, "node_modules/tslib": { @@ -242,9 +250,9 @@ "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" }, "node_modules/typescript": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", - "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz", + "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -257,9 +265,9 @@ }, "dependencies": { "@rollup/plugin-typescript": { - "version": "8.2.5", - "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.2.5.tgz", - "integrity": "sha512-QL/LvDol/PAGB2O0S7/+q2HpSUNodpw7z6nGn9BfoVCPOZ0r4EALrojFU29Bkoi2Hr2jgTocTejJ5GGWZfOxbQ==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-8.3.0.tgz", + "integrity": "sha512-I5FpSvLbtAdwJ+naznv+B4sjXZUcIvLLceYpITAn7wAP8W0wqc5noLdGIp9HGVntNhRWXctwPYrSSFQxtl0FPA==", "dev": true, "requires": { "@rollup/pluginutils": "^3.1.0", @@ -324,9 +332,9 @@ } }, "is-core-module": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.6.0.tgz", - "integrity": "sha512-wShG8vs60jKfPWpF2KZRaAtvt3a20OAn7+IJ6hLPECpSABLcKtFKTTI4ZtH5QcBruBHlq+WsdHWyz0BCZW7svQ==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.8.0.tgz", + "integrity": "sha512-vd15qHsaqrRL7dtH6QNuy0ndJmRDrS9HAM1CAiSifNUFv4x1a0CCVsj18hJ1mShxIG6T2i1sO78MkP56r0nYRw==", "dev": true, "requires": { "has": "^1.0.3" @@ -345,9 +353,9 @@ "dev": true }, "preact": { - "version": "10.5.14", - "resolved": "https://registry.npmjs.org/preact/-/preact-10.5.14.tgz", - "integrity": "sha512-KojoltCrshZ099ksUZ2OQKfbH66uquFoxHSbnwKbTJHeQNvx42EmC7wQVWNuDt6vC5s3nudRHFtKbpY4ijKlaQ==" + "version": "10.5.15", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.5.15.tgz", + "integrity": "sha512-5chK29n6QcJc3m1lVrKQSQ+V7K1Gb8HeQY6FViQ5AxCAEGu3DaHffWNDkC9+miZgsLvbvU9rxbV1qinGHMHzqA==" }, "requirejs": { "version": "2.3.6", @@ -366,9 +374,9 @@ } }, "rollup": { - "version": "2.56.2", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.56.2.tgz", - "integrity": "sha512-s8H00ZsRi29M2/lGdm1u8DJpJ9ML8SUOpVVBd33XNeEeL3NVaTiUcSBHzBdF3eAyR0l7VSpsuoVUGrRHq7aPwQ==", + "version": "2.60.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.60.0.tgz", + "integrity": "sha512-cHdv9GWd58v58rdseC8e8XIaPUo8a9cgZpnCMMDGZFDZKEODOiPPEQFXLriWr/TjXzhPPmG5bkAztPsOARIcGQ==", "dev": true, "requires": { "fsevents": "~2.3.2" @@ -381,9 +389,9 @@ "dev": true }, "source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -399,14 +407,14 @@ } }, "terser": { - "version": "5.7.1", - "resolved": "https://registry.npmjs.org/terser/-/terser-5.7.1.tgz", - "integrity": "sha512-b3e+d5JbHAe/JSjwsC3Zn55wsBIM7AsHLjKxT31kGCldgbpFePaFo+PiddtO6uwRZWRw7sPXmAN8dTW61xmnSg==", + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.10.0.tgz", + "integrity": "sha512-AMmF99DMfEDiRJfxfY5jj5wNH/bYO09cniSqhfoyxc8sFoYIgkJy86G04UoZU5VjlpnplVu0K6Tx6E9b5+DlHA==", "dev": true, "requires": { "commander": "^2.20.0", "source-map": "~0.7.2", - "source-map-support": "~0.5.19" + "source-map-support": "~0.5.20" } }, "tslib": { @@ -415,9 +423,9 @@ "integrity": "sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==" }, "typescript": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", - "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "version": "4.5.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.5.2.tgz", + "integrity": "sha512-5BlMof9H1yGt0P8/WF+wPNw6GfctgGjXp5hkblpyT+8rkASSmkUKMXrxR0Xg8ThVCi/JnHQiKXeBaEwCeQwMFw==", "dev": true } } diff --git a/src/main/java/dan200/computercraft/ComputerCraftAPIImpl.java b/src/main/java/dan200/computercraft/ComputerCraftAPIImpl.java index c8572a4b6..eda35ee9a 100644 --- a/src/main/java/dan200/computercraft/ComputerCraftAPIImpl.java +++ b/src/main/java/dan200/computercraft/ComputerCraftAPIImpl.java @@ -31,6 +31,7 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.packs.resources.ReloadableResourceManager; +import net.minecraft.server.packs.resources.ResourceManager; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; @@ -101,7 +102,7 @@ public final class ComputerCraftAPIImpl implements IComputerCraftAPI @Override public IMount createResourceMount( @Nonnull String domain, @Nonnull String subPath ) { - ReloadableResourceManager manager = (ReloadableResourceManager) ServerLifecycleHooks.getCurrentServer().getResourceManager(); + ResourceManager manager = ServerLifecycleHooks.getCurrentServer().getResourceManager(); ResourceMount mount = ResourceMount.get( domain, subPath, manager ); return mount.exists( "" ) ? mount : null; } diff --git a/src/main/java/dan200/computercraft/api/lua/ILuaContext.java b/src/main/java/dan200/computercraft/api/lua/ILuaContext.java index 70502b535..4d29b57d1 100644 --- a/src/main/java/dan200/computercraft/api/lua/ILuaContext.java +++ b/src/main/java/dan200/computercraft/api/lua/ILuaContext.java @@ -40,5 +40,8 @@ public interface ILuaContext * @throws LuaException If the task could not be queued, or if the task threw an exception. */ @Nonnull - MethodResult executeMainThreadTask( @Nonnull ILuaTask task ) throws LuaException; + default MethodResult executeMainThreadTask( @Nonnull ILuaTask task ) throws LuaException + { + return TaskCallback.make( this, task ); + } } diff --git a/src/main/java/dan200/computercraft/core/asm/TaskCallback.java b/src/main/java/dan200/computercraft/api/lua/TaskCallback.java similarity index 58% rename from src/main/java/dan200/computercraft/core/asm/TaskCallback.java rename to src/main/java/dan200/computercraft/api/lua/TaskCallback.java index 1298391e4..85870a221 100644 --- a/src/main/java/dan200/computercraft/core/asm/TaskCallback.java +++ b/src/main/java/dan200/computercraft/api/lua/TaskCallback.java @@ -1,16 +1,14 @@ /* - * 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 + * 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.core.asm; - -import dan200.computercraft.api.lua.*; +package dan200.computercraft.api.lua; import javax.annotation.Nonnull; import java.util.Arrays; -public final class TaskCallback implements ILuaCallback +final class TaskCallback implements ILuaCallback { private final MethodResult pull = MethodResult.pullEvent( "task_complete", this ); private final long task; @@ -47,19 +45,7 @@ public final class TaskCallback implements ILuaCallback } } - static Object[] checkUnwrap( MethodResult result ) - { - if( result.getCallback() != null ) - { - // Due to how tasks are implemented, we can't currently return a MethodResult. This is an - // entirely artificial limitation - we can remove it if it ever becomes an issue. - throw new IllegalStateException( "Cannot return MethodResult for mainThread task." ); - } - - return result.getResult(); - } - - public static MethodResult make( ILuaContext context, ILuaTask func ) throws LuaException + static MethodResult make( ILuaContext context, ILuaTask func ) throws LuaException { long task = context.issueMainThreadTask( func ); return new TaskCallback( task ).pull; diff --git a/src/main/java/dan200/computercraft/api/peripheral/GenericPeripheral.java b/src/main/java/dan200/computercraft/api/peripheral/GenericPeripheral.java new file mode 100644 index 000000000..4140597ec --- /dev/null +++ b/src/main/java/dan200/computercraft/api/peripheral/GenericPeripheral.java @@ -0,0 +1,45 @@ +/* + * 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.peripheral; + +import dan200.computercraft.api.lua.GenericSource; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraftforge.items.IItemHandler; + +import javax.annotation.Nonnull; + +/** + * A {@link GenericSource} which provides methods for a peripheral. + * + * Unlike a {@link GenericSource}, all methods should target the same type, for instance a + * {@link BlockEntity} subclass or a capability interface. This is not currently enforced. + */ +public interface GenericPeripheral extends GenericSource +{ + /** + * Get the type of the exposed peripheral. + * + * Unlike normal {@link IPeripheral}s, {@link GenericPeripheral} do not have to have a type. By default, the + * resulting peripheral uses the resource name of the wrapped {@link BlockEntity} (for instance {@literal minecraft:chest}). + * + * However, in some cases it may be more appropriate to specify a more readable name. Overriding this method allows + * you to do so. + * + * When multiple {@link GenericPeripheral}s return a non-empty peripheral type for a single tile entity, the + * lexicographically smallest will be chosen. In order to avoid this conflict, this method should only be + * implemented when your peripheral targets a single tile entity AND it's likely that you're the + * only mod to do so. Similarly this should NOT be implemented when your methods target a + * capability or other interface (i.e. {@link IItemHandler}). + * + * @return The type of this peripheral or {@link PeripheralType#untyped()}. + * @see IPeripheral#getType() + */ + @Nonnull + default PeripheralType getType() + { + return PeripheralType.untyped(); + } +} diff --git a/src/main/java/dan200/computercraft/api/peripheral/PeripheralType.java b/src/main/java/dan200/computercraft/api/peripheral/PeripheralType.java new file mode 100644 index 000000000..2780534c2 --- /dev/null +++ b/src/main/java/dan200/computercraft/api/peripheral/PeripheralType.java @@ -0,0 +1,62 @@ +/* + * 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.peripheral; + +import com.google.common.base.Strings; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * The type of a {@link GenericPeripheral}. + * + * When determining the final type of the resulting peripheral, the union of all types is taken, with the + * lexicographically smallest non-empty name being chosen. + */ +public final class PeripheralType +{ + private static final PeripheralType UNTYPED = new PeripheralType( null ); + + private final String type; + + public PeripheralType( String type ) + { + this.type = type; + } + + /** + * An empty peripheral type, used when a {@link GenericPeripheral} does not have an explicit type. + * + * @return The empty peripheral type. + */ + public static PeripheralType untyped() + { + return UNTYPED; + } + + /** + * Create a new non-empty peripheral type. + * + * @param type The name of the type. + * @return The constructed peripheral type. + */ + public static PeripheralType ofType( @Nonnull String type ) + { + if( Strings.isNullOrEmpty( type ) ) throw new IllegalArgumentException( "type cannot be null or empty" ); + return new PeripheralType( type ); + } + + /** + * Get the name of this peripheral type. This may be {@literal null}. + * + * @return The type of this peripheral. + */ + @Nullable + public String getPrimaryType() + { + return type; + } +} diff --git a/src/main/java/dan200/computercraft/client/render/DebugOverlay.java b/src/main/java/dan200/computercraft/client/render/DebugOverlay.java new file mode 100644 index 000000000..770a037b9 --- /dev/null +++ b/src/main/java/dan200/computercraft/client/render/DebugOverlay.java @@ -0,0 +1,58 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.client.render; + +import dan200.computercraft.ComputerCraft; +import dan200.computercraft.api.turtle.ITurtleUpgrade; +import dan200.computercraft.api.turtle.TurtleSide; +import dan200.computercraft.shared.peripheral.monitor.TileMonitor; +import dan200.computercraft.shared.turtle.blocks.TileTurtle; +import net.minecraft.client.Minecraft; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.HitResult; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.client.event.RenderGameOverlayEvent; +import net.minecraftforge.eventbus.api.SubscribeEvent; +import net.minecraftforge.fml.common.Mod; + +import java.util.List; + +@Mod.EventBusSubscriber( modid = ComputerCraft.MOD_ID, value = Dist.CLIENT ) +public class DebugOverlay +{ + @SubscribeEvent + public static void onRenderText( RenderGameOverlayEvent.Text event ) + { + Minecraft minecraft = Minecraft.getInstance(); + if( !minecraft.options.renderDebug || minecraft.level == null ) return; + if( minecraft.hitResult == null || minecraft.hitResult.getType() != HitResult.Type.BLOCK ) return; + + BlockEntity tile = minecraft.level.getBlockEntity( ((BlockHitResult) minecraft.hitResult).getBlockPos() ); + + if( tile instanceof TileMonitor monitor ) + { + event.getRight().add( "" ); + event.getRight().add( + String.format( "Targeted monitor: (%d, %d), %d x %d", monitor.getXIndex(), monitor.getYIndex(), monitor.getWidth(), monitor.getHeight() ) + ); + } + else if( tile instanceof TileTurtle turtle ) + { + event.getRight().add( "" ); + event.getRight().add( "Targeted turtle:" ); + event.getRight().add( String.format( "Id: %d", turtle.getComputerID() ) ); + addTurtleUpgrade( event.getRight(), turtle, TurtleSide.LEFT ); + addTurtleUpgrade( event.getRight(), turtle, TurtleSide.RIGHT ); + } + } + + private static void addTurtleUpgrade( List out, TileTurtle turtle, TurtleSide side ) + { + ITurtleUpgrade upgrade = turtle.getUpgrade( side ); + if( upgrade != null ) out.add( String.format( "Upgrade[%s]: %s", side, upgrade.getUpgradeID() ) ); + } +} diff --git a/src/main/java/dan200/computercraft/core/asm/Generator.java b/src/main/java/dan200/computercraft/core/asm/Generator.java index e68732f95..4cd2d66cf 100644 --- a/src/main/java/dan200/computercraft/core/asm/Generator.java +++ b/src/main/java/dan200/computercraft/core/asm/Generator.java @@ -15,6 +15,7 @@ import dan200.computercraft.api.lua.IArguments; import dan200.computercraft.api.lua.LuaException; import dan200.computercraft.api.lua.LuaFunction; import dan200.computercraft.api.lua.MethodResult; +import dan200.computercraft.api.peripheral.PeripheralType; import org.objectweb.asm.ClassWriter; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Type; @@ -108,7 +109,7 @@ public final class Generator if( instance == null ) continue; if( methods == null ) methods = new ArrayList<>(); - addMethod( methods, method, annotation, instance ); + addMethod( methods, method, annotation, null, instance ); } for( GenericMethod method : GenericMethod.all() ) @@ -119,7 +120,7 @@ public final class Generator if( instance == null ) continue; if( methods == null ) methods = new ArrayList<>(); - addMethod( methods, method.method, method.annotation, instance ); + addMethod( methods, method.method, method.annotation, method.peripheralType, instance ); } if( methods == null ) return Collections.emptyList(); @@ -127,7 +128,7 @@ public final class Generator return Collections.unmodifiableList( methods ); } - private void addMethod( List> methods, Method method, LuaFunction annotation, T instance ) + private void addMethod( List> methods, Method method, LuaFunction annotation, PeripheralType genericType, T instance ) { if( annotation.mainThread() ) instance = wrap.apply( instance ); @@ -135,13 +136,13 @@ public final class Generator boolean isSimple = method.getReturnType() != MethodResult.class && !annotation.mainThread(); if( names.length == 0 ) { - methods.add( new NamedMethod<>( method.getName(), instance, isSimple ) ); + methods.add( new NamedMethod<>( method.getName(), instance, isSimple, genericType ) ); } else { for( String name : names ) { - methods.add( new NamedMethod<>( name, instance, isSimple ) ); + methods.add( new NamedMethod<>( name, instance, isSimple, genericType ) ); } } } diff --git a/src/main/java/dan200/computercraft/core/asm/GenericMethod.java b/src/main/java/dan200/computercraft/core/asm/GenericMethod.java index e0c916c2e..691d9b742 100644 --- a/src/main/java/dan200/computercraft/core/asm/GenericMethod.java +++ b/src/main/java/dan200/computercraft/core/asm/GenericMethod.java @@ -8,6 +8,8 @@ package dan200.computercraft.core.asm; import dan200.computercraft.ComputerCraft; import dan200.computercraft.api.lua.GenericSource; import dan200.computercraft.api.lua.LuaFunction; +import dan200.computercraft.api.peripheral.GenericPeripheral; +import dan200.computercraft.api.peripheral.PeripheralType; import javax.annotation.Nonnull; import java.lang.reflect.Method; @@ -18,6 +20,7 @@ import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.stream.Collectors; +import java.util.stream.Stream; /** * A generic method is a method belonging to a {@link GenericSource} with a known target. @@ -27,15 +30,17 @@ public class GenericMethod final Method method; final LuaFunction annotation; final Class target; + final PeripheralType peripheralType; private static final List sources = new ArrayList<>(); private static List cache; - GenericMethod( Method method, LuaFunction annotation, Class target ) + GenericMethod( Method method, LuaFunction annotation, Class target, PeripheralType peripheralType ) { this.method = method; this.annotation = annotation; this.target = target; + this.peripheralType = peripheralType; } /** @@ -46,10 +51,28 @@ public class GenericMethod static List all() { if( cache != null ) return cache; - return cache = sources.stream() - .flatMap( x -> Arrays.stream( x.getClass().getDeclaredMethods() ) ) - .map( method -> - { + return cache = sources.stream().flatMap( GenericMethod::getMethods ).collect( Collectors.toList() ); + } + + public static synchronized void register( @Nonnull GenericSource source ) + { + Objects.requireNonNull( source, "Source cannot be null" ); + + if( cache != null ) + { + ComputerCraft.log.warn( "Registering a generic source {} after cache has been built. This source will be ignored.", cache ); + } + + sources.add( source ); + } + + private static Stream getMethods( GenericSource source ) + { + Class klass = source.getClass(); + PeripheralType type = source instanceof GenericPeripheral ? ((GenericPeripheral) source).getType() : null; + + return Arrays.stream( klass.getDeclaredMethods() ) + .map( method -> { LuaFunction annotation = method.getAnnotation( LuaFunction.class ); if( annotation == null ) return null; @@ -69,22 +92,8 @@ public class GenericMethod Class target = Reflect.getRawType( method, types[0], false ); if( target == null ) return null; - return new GenericMethod( method, annotation, target ); + return new GenericMethod( method, annotation, target, type ); } ) - .filter( Objects::nonNull ) - .collect( Collectors.toList() ); - } - - - public static synchronized void register( @Nonnull GenericSource source ) - { - Objects.requireNonNull( source, "Source cannot be null" ); - - if( cache != null ) - { - ComputerCraft.log.warn( "Registering a generic source {} after cache has been built. This source will be ignored.", cache ); - } - - sources.add( source ); + .filter( Objects::nonNull ); } } diff --git a/src/main/java/dan200/computercraft/core/asm/LuaMethod.java b/src/main/java/dan200/computercraft/core/asm/LuaMethod.java index b0562835c..281ab9c56 100644 --- a/src/main/java/dan200/computercraft/core/asm/LuaMethod.java +++ b/src/main/java/dan200/computercraft/core/asm/LuaMethod.java @@ -13,7 +13,7 @@ import java.util.Collections; public interface LuaMethod { Generator GENERATOR = new Generator<>( LuaMethod.class, Collections.singletonList( ILuaContext.class ), - m -> ( target, context, args ) -> TaskCallback.make( context, () -> TaskCallback.checkUnwrap( m.apply( target, context, args ) ) ) + m -> ( target, context, args ) -> context.executeMainThreadTask( () -> ResultHelpers.checkNormalResult( m.apply( target, context, args ) ) ) ); IntCache DYNAMIC = new IntCache<>( diff --git a/src/main/java/dan200/computercraft/core/asm/NamedMethod.java b/src/main/java/dan200/computercraft/core/asm/NamedMethod.java index ea72bb7a4..35d9c0a77 100644 --- a/src/main/java/dan200/computercraft/core/asm/NamedMethod.java +++ b/src/main/java/dan200/computercraft/core/asm/NamedMethod.java @@ -5,7 +5,10 @@ */ package dan200.computercraft.core.asm; +import dan200.computercraft.api.peripheral.PeripheralType; + import javax.annotation.Nonnull; +import javax.annotation.Nullable; public final class NamedMethod { @@ -13,11 +16,14 @@ public final class NamedMethod private final T method; private final boolean nonYielding; - NamedMethod( String name, T method, boolean nonYielding ) + private final PeripheralType genericType; + + NamedMethod( String name, T method, boolean nonYielding, PeripheralType genericType ) { this.name = name; this.method = method; this.nonYielding = nonYielding; + this.genericType = genericType; } @Nonnull @@ -36,4 +42,10 @@ public final class NamedMethod { return nonYielding; } + + @Nullable + public PeripheralType getGenericType() + { + return genericType; + } } diff --git a/src/main/java/dan200/computercraft/core/asm/PeripheralMethod.java b/src/main/java/dan200/computercraft/core/asm/PeripheralMethod.java index 38618442f..146896b04 100644 --- a/src/main/java/dan200/computercraft/core/asm/PeripheralMethod.java +++ b/src/main/java/dan200/computercraft/core/asm/PeripheralMethod.java @@ -18,7 +18,7 @@ import java.util.Arrays; public interface PeripheralMethod { Generator GENERATOR = new Generator<>( PeripheralMethod.class, Arrays.asList( ILuaContext.class, IComputerAccess.class ), - m -> ( target, context, computer, args ) -> TaskCallback.make( context, () -> TaskCallback.checkUnwrap( m.apply( target, context, computer, args ) ) ) + m -> ( target, context, computer, args ) -> context.executeMainThreadTask( () -> ResultHelpers.checkNormalResult( m.apply( target, context, computer, args ) ) ) ); IntCache DYNAMIC = new IntCache<>( diff --git a/src/main/java/dan200/computercraft/core/asm/ResultHelpers.java b/src/main/java/dan200/computercraft/core/asm/ResultHelpers.java new file mode 100644 index 000000000..7b9b464b2 --- /dev/null +++ b/src/main/java/dan200/computercraft/core/asm/ResultHelpers.java @@ -0,0 +1,27 @@ +/* + * 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.asm; + +import dan200.computercraft.api.lua.MethodResult; + +final class ResultHelpers +{ + private ResultHelpers() + { + } + + static Object[] checkNormalResult( MethodResult result ) + { + if( result.getCallback() != null ) + { + // Due to how tasks are implemented, we can't currently return a MethodResult. This is an + // entirely artificial limitation - we can remove it if it ever becomes an issue. + throw new IllegalStateException( "Must return MethodResult.of from mainThread function." ); + } + + return result.getResult(); + } +} diff --git a/src/main/java/dan200/computercraft/core/computer/MainThread.java b/src/main/java/dan200/computercraft/core/computer/MainThread.java index 8f53541e2..1c77e3692 100644 --- a/src/main/java/dan200/computercraft/core/computer/MainThread.java +++ b/src/main/java/dan200/computercraft/core/computer/MainThread.java @@ -93,7 +93,7 @@ public final class MainThread executor.updateTime(); // We're not currently on the queue, so update its current execution time to - // ensure its at least as high as the minimum. + // ensure it's at least as high as the minimum. long newRuntime = minimumTime; // Slow down new computers a little bit. diff --git a/src/main/java/dan200/computercraft/core/filesystem/ResourceMount.java b/src/main/java/dan200/computercraft/core/filesystem/ResourceMount.java index 2e6283066..d31757cda 100644 --- a/src/main/java/dan200/computercraft/core/filesystem/ResourceMount.java +++ b/src/main/java/dan200/computercraft/core/filesystem/ResourceMount.java @@ -7,7 +7,6 @@ package dan200.computercraft.core.filesystem; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; -import com.google.common.collect.MapMaker; import com.google.common.io.ByteStreams; import dan200.computercraft.ComputerCraft; import dan200.computercraft.api.filesystem.IMount; @@ -15,10 +14,11 @@ import dan200.computercraft.core.apis.handles.ArrayByteChannel; import dan200.computercraft.shared.util.IoUtil; import net.minecraft.ResourceLocationException; import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.packs.resources.ReloadableResourceManager; +import net.minecraft.server.packs.resources.PreparableReloadListener; import net.minecraft.server.packs.resources.Resource; import net.minecraft.server.packs.resources.ResourceManager; -import net.minecraft.server.packs.resources.ResourceManagerReloadListener; +import net.minecraft.server.packs.resources.SimplePreparableReloadListener; +import net.minecraft.util.profiling.ProfilerFiller; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -27,7 +27,9 @@ import java.io.IOException; import java.io.InputStream; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; -import java.util.*; +import java.util.HashMap; +import java.util.List; +import java.util.Map; import java.util.concurrent.TimeUnit; public final class ResourceMount implements IMount @@ -56,50 +58,37 @@ public final class ResourceMount implements IMount .weigher( ( k, v ) -> v.length ) .build(); - private static final MapMaker CACHE_TEMPLATE = new MapMaker().weakValues().concurrencyLevel( 1 ); - /** * Maintain a cache of currently loaded resource mounts. This cache is invalidated when currentManager changes. */ - private static final Map> MOUNT_CACHE = new WeakHashMap<>( 2 ); + private static final Map MOUNT_CACHE = new HashMap<>( 2 ); private final String namespace; private final String subPath; - private final ReloadableResourceManager manager; + private ResourceManager manager; @Nullable private FileEntry root; - public static ResourceMount get( String namespace, String subPath, ReloadableResourceManager manager ) + public static ResourceMount get( String namespace, String subPath, ResourceManager manager ) { - Map cache; - + ResourceLocation path = new ResourceLocation( namespace, subPath ); synchronized( MOUNT_CACHE ) { - cache = MOUNT_CACHE.get( manager ); - if( cache == null ) MOUNT_CACHE.put( manager, cache = CACHE_TEMPLATE.makeMap() ); - } - - ResourceLocation path = new ResourceLocation( namespace, subPath ); - synchronized( cache ) - { - ResourceMount mount = cache.get( path ); - if( mount == null ) cache.put( path, mount = new ResourceMount( namespace, subPath, manager ) ); + ResourceMount mount = MOUNT_CACHE.get( path ); + if( mount == null ) MOUNT_CACHE.put( path, mount = new ResourceMount( namespace, subPath, manager ) ); return mount; } } - private ResourceMount( String namespace, String subPath, ReloadableResourceManager manager ) + private ResourceMount( String namespace, String subPath, ResourceManager manager ) { this.namespace = namespace; this.subPath = subPath; - this.manager = manager; - - Listener.INSTANCE.add( manager, this ); - if( root == null ) load(); + load( manager ); } - private void load() + private void load( ResourceManager manager ) { boolean hasAny = false; String existingNamespace = null; @@ -117,6 +106,7 @@ public final class ResourceMount implements IMount hasAny = true; } + this.manager = manager; root = hasAny ? newRoot : null; if( !hasAny ) @@ -292,28 +282,30 @@ public final class ResourceMount implements IMount } /** - * A {@link ResourceManagerReloadListener} which reloads any associated mounts. - * - * While people should really be keeping a permanent reference to this, some people construct it every - * method call, so let's make this as small as possible. + * A {@link PreparableReloadListener} which reloads any associated mounts and correctly updates the resource manager + * they point to. */ - static class Listener implements ResourceManagerReloadListener + public static final SimplePreparableReloadListener RELOAD_LISTENER = new SimplePreparableReloadListener<>() { - private static final Listener INSTANCE = new Listener(); - - private final Set mounts = Collections.newSetFromMap( new WeakHashMap<>() ); - private final Set managers = Collections.newSetFromMap( new WeakHashMap<>() ); + @Nonnull + @Override + protected Void prepare( @Nonnull ResourceManager manager, @Nonnull ProfilerFiller profiler ) + { + profiler.push( "Reloading ComputerCraft mounts" ); + try + { + for( ResourceMount mount : MOUNT_CACHE.values() ) mount.load( manager ); + } + finally + { + profiler.pop(); + } + return null; + } @Override - public void onResourceManagerReload( @Nonnull ResourceManager manager ) + protected void apply( @Nonnull Void result, @Nonnull ResourceManager manager, @Nonnull ProfilerFiller profiler ) { - for( ResourceMount mount : mounts ) mount.load(); } - - synchronized void add( ReloadableResourceManager manager, ResourceMount mount ) - { - if( managers.add( manager ) ) manager.registerReloadListener( this ); - mounts.add( mount ); - } - } + }; } diff --git a/src/main/java/dan200/computercraft/core/lua/LuaContext.java b/src/main/java/dan200/computercraft/core/lua/LuaContext.java index 0e5c792c8..626d15fad 100644 --- a/src/main/java/dan200/computercraft/core/lua/LuaContext.java +++ b/src/main/java/dan200/computercraft/core/lua/LuaContext.java @@ -9,8 +9,6 @@ import dan200.computercraft.ComputerCraft; import dan200.computercraft.api.lua.ILuaContext; import dan200.computercraft.api.lua.ILuaTask; import dan200.computercraft.api.lua.LuaException; -import dan200.computercraft.api.lua.MethodResult; -import dan200.computercraft.core.asm.TaskCallback; import dan200.computercraft.core.computer.Computer; import dan200.computercraft.core.computer.MainThread; @@ -68,11 +66,4 @@ class LuaContext implements ILuaContext throw new LuaException( "Task limit exceeded" ); } } - - @Nonnull - @Override - public MethodResult executeMainThreadTask( @Nonnull ILuaTask task ) throws LuaException - { - return TaskCallback.make( this, task ); - } } diff --git a/src/main/java/dan200/computercraft/shared/CommonHooks.java b/src/main/java/dan200/computercraft/shared/CommonHooks.java index e3339cf99..9c2f36d2a 100644 --- a/src/main/java/dan200/computercraft/shared/CommonHooks.java +++ b/src/main/java/dan200/computercraft/shared/CommonHooks.java @@ -8,6 +8,7 @@ package dan200.computercraft.shared; import dan200.computercraft.ComputerCraft; import dan200.computercraft.core.apis.http.NetworkUtils; import dan200.computercraft.core.computer.MainThread; +import dan200.computercraft.core.filesystem.ResourceMount; import dan200.computercraft.core.tracking.ComputerMBean; import dan200.computercraft.core.tracking.Tracking; import dan200.computercraft.shared.command.CommandComputerCraft; @@ -23,6 +24,7 @@ import net.minecraft.world.level.storage.loot.BuiltInLootTables; import net.minecraft.world.level.storage.loot.LootPool; import net.minecraft.world.level.storage.loot.entries.LootTableReference; import net.minecraft.world.level.storage.loot.providers.number.ConstantValue; +import net.minecraftforge.event.AddReloadListenerEvent; import net.minecraftforge.event.LootTableLoadEvent; import net.minecraftforge.event.RegisterCommandsEvent; import net.minecraftforge.event.TickEvent; @@ -138,4 +140,10 @@ public final class CommonHooks .name( "computercraft_treasure" ) .build() ); } + + @SubscribeEvent + public static void onAddReloadListeners( AddReloadListenerEvent event ) + { + event.addListener( ResourceMount.RELOAD_LISTENER ); + } } diff --git a/src/main/java/dan200/computercraft/shared/common/TileGeneric.java b/src/main/java/dan200/computercraft/shared/common/TileGeneric.java index cb74c0d25..a4bb2ca94 100644 --- a/src/main/java/dan200/computercraft/shared/common/TileGeneric.java +++ b/src/main/java/dan200/computercraft/shared/common/TileGeneric.java @@ -16,6 +16,7 @@ import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.BlockHitResult; +import net.minecraftforge.common.util.Constants; import javax.annotation.Nonnull; @@ -35,7 +36,7 @@ public abstract class TileGeneric extends BlockEntity setChanged(); BlockPos pos = getBlockPos(); BlockState state = getBlockState(); - getLevel().sendBlockUpdated( pos, state, state, 3 ); + getLevel().sendBlockUpdated( pos, state, state, Constants.BlockFlags.DEFAULT ); } @Nonnull diff --git a/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java b/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java index 4b8e2b612..64a4c9bf4 100644 --- a/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java +++ b/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java @@ -15,12 +15,16 @@ import dan200.computercraft.shared.util.NBTUtil; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands; import net.minecraft.core.BlockPos; +import net.minecraft.core.Registry; import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; +import javax.annotation.Nonnull; import java.util.*; /** @@ -187,22 +191,24 @@ public class CommandAPI implements ILuaAPI * Blocks are traversed by ascending y level, followed by z and x - the returned * table may be indexed using `x + z*width + y*depth*depth`. * - * @param minX The start x coordinate of the range to query. - * @param minY The start y coordinate of the range to query. - * @param minZ The start z coordinate of the range to query. - * @param maxX The end x coordinate of the range to query. - * @param maxY The end y coordinate of the range to query. - * @param maxZ The end z coordinate of the range to query. + * @param minX The start x coordinate of the range to query. + * @param minY The start y coordinate of the range to query. + * @param minZ The start z coordinate of the range to query. + * @param maxX The end x coordinate of the range to query. + * @param maxY The end y coordinate of the range to query. + * @param maxZ The end z coordinate of the range to query. + * @param dimension The dimension to query (e.g. "minecraft:overworld"). Defaults to the current dimension. * @return A list of information about each block. * @throws LuaException If the coordinates are not within the world. * @throws LuaException If trying to get information about more than 4096 blocks. * @cc.since 1.76 + * @cc.changed 1.99 Added {@code dimension} argument. */ @LuaFunction( mainThread = true ) - public final List> getBlockInfos( int minX, int minY, int minZ, int maxX, int maxY, int maxZ ) throws LuaException + public final List> getBlockInfos( int minX, int minY, int minZ, int maxX, int maxY, int maxZ, Optional dimension ) throws LuaException { // Get the details of the block - Level world = computer.getLevel(); + Level world = getLevel( dimension ); BlockPos min = new BlockPos( Math.min( minX, maxX ), Math.min( minY, maxY ), @@ -244,26 +250,38 @@ public class CommandAPI implements ILuaAPI * with @{turtle.inspect}). If there is a tile entity for that block, its NBT * will also be returned. * - * @param x The x position of the block to query. - * @param y The y position of the block to query. - * @param z The z position of the block to query. + * @param x The x position of the block to query. + * @param y The y position of the block to query. + * @param z The z position of the block to query. + * @param dimension The dimension to query (e.g. "minecraft:overworld"). Defaults to the current dimension. * @return The given block's information. * @throws LuaException If the coordinates are not within the world, or are not currently loaded. * @cc.changed 1.76 Added block state info to return value + * @cc.changed 1.99 Added {@code dimension} argument. */ @LuaFunction( mainThread = true ) - public final Map getBlockInfo( int x, int y, int z ) throws LuaException + public final Map getBlockInfo( int x, int y, int z, Optional dimension ) throws LuaException { - // Get the details of the block - Level world = computer.getLevel(); + Level level = getLevel( dimension ); BlockPos position = new BlockPos( x, y, z ); - if( world != null && world.isInWorldBounds( position ) ) - { - return getBlockInfo( world, position ); - } - else - { - throw new LuaException( "Co-ordinates out of range" ); - } + if( !level.isInWorldBounds( position ) ) throw new LuaException( "Co-ordinates out of range" ); + return getBlockInfo( level, position ); + } + + @Nonnull + private Level getLevel( @Nonnull Optional id ) throws LuaException + { + Level currentLevel = computer.getLevel(); + if( currentLevel == null ) throw new LuaException( "No world exists" ); + + if( !id.isPresent() ) return currentLevel; + + ResourceLocation dimensionId = ResourceLocation.tryParse( id.get() ); + if( dimensionId == null ) throw new LuaException( "Invalid dimension name" ); + + Level level = currentLevel.getServer().getLevel( ResourceKey.create( Registry.DIMENSION_REGISTRY, dimensionId ) ); + if( level == null ) throw new LuaException( "Unknown dimension" ); + + return level; } } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheral.java b/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheral.java index 75dff0c2b..d44143a90 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheral.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheral.java @@ -25,11 +25,11 @@ class GenericPeripheral implements IDynamicPeripheral private final BlockEntity tile; private final List methods; - GenericPeripheral( BlockEntity tile, List methods ) + GenericPeripheral( BlockEntity tile, String name, List methods ) { ResourceLocation type = tile.getType().getRegistryName(); this.tile = tile; - this.type = type == null ? "unknown" : type.toString(); + this.type = name != null ? name : (type != null ? type.toString() : "unknown"); this.methods = methods; } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheralProvider.java b/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheralProvider.java index 1fb3cbf62..b15f804dd 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheralProvider.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheralProvider.java @@ -6,6 +6,7 @@ package dan200.computercraft.shared.peripheral.generic; import dan200.computercraft.api.peripheral.IPeripheral; +import dan200.computercraft.api.peripheral.PeripheralType; import dan200.computercraft.core.asm.NamedMethod; import dan200.computercraft.core.asm.PeripheralMethod; import net.minecraft.core.BlockPos; @@ -38,10 +39,10 @@ public class GenericPeripheralProvider BlockEntity tile = world.getBlockEntity( pos ); if( tile == null ) return null; - ArrayList saturated = new ArrayList<>( 0 ); + GenericPeripheralBuilder saturated = new GenericPeripheralBuilder(); List> tileMethods = PeripheralMethod.GENERATOR.getMethods( tile.getClass() ); - if( !tileMethods.isEmpty() ) addSaturated( saturated, tile, tileMethods ); + if( !tileMethods.isEmpty() ) saturated.addMethods( tile, tileMethods ); for( Capability capability : capabilities ) { @@ -50,20 +51,44 @@ public class GenericPeripheralProvider List> capabilityMethods = PeripheralMethod.GENERATOR.getMethods( contents.getClass() ); if( capabilityMethods.isEmpty() ) return; - addSaturated( saturated, contents, capabilityMethods ); + saturated.addMethods( contents, capabilityMethods ); wrapper.addListener( cast( invalidate ) ); } ); } - return saturated.isEmpty() ? null : new GenericPeripheral( tile, saturated ); + return saturated.toPeripheral( tile ); } - private static void addSaturated( ArrayList saturated, Object target, List> methods ) + private static class GenericPeripheralBuilder { - saturated.ensureCapacity( saturated.size() + methods.size() ); - for( NamedMethod method : methods ) + String name; + final ArrayList methods = new ArrayList<>( 0 ); + + IPeripheral toPeripheral( BlockEntity tile ) { - saturated.add( new SaturatedMethod( target, method ) ); + if( methods.isEmpty() ) return null; + + methods.trimToSize(); + return new GenericPeripheral( tile, name, methods ); + } + + void addMethods( Object target, List> methods ) + { + ArrayList saturatedMethods = this.methods; + saturatedMethods.ensureCapacity( saturatedMethods.size() + methods.size() ); + for( NamedMethod method : methods ) + { + saturatedMethods.add( new SaturatedMethod( target, method ) ); + + // If we have a peripheral type, use it. Always pick the smallest one, so it's consistent (assuming mods + // don't change). + PeripheralType type = method.getGenericType(); + if( type != null && type.getPrimaryType() != null ) + { + String name = type.getPrimaryType(); + if( this.name == null || this.name.compareTo( name ) > 0 ) this.name = name; + } + } } } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/monitor/BlockMonitor.java b/src/main/java/dan200/computercraft/shared/peripheral/monitor/BlockMonitor.java index a546e62b3..d827ae3a1 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/monitor/BlockMonitor.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/monitor/BlockMonitor.java @@ -92,7 +92,7 @@ public class BlockMonitor extends BlockGeneric return; } - monitor.updateNeighbors(); + monitor.expand(); } } } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/monitor/Expander.java b/src/main/java/dan200/computercraft/shared/peripheral/monitor/Expander.java new file mode 100644 index 000000000..21290ccfb --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/peripheral/monitor/Expander.java @@ -0,0 +1,108 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.shared.peripheral.monitor; + +import dan200.computercraft.ComputerCraft; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; + +import java.util.Objects; + +/** + * Expands a monitor into available space. This tries to expand in each direction until a fixed point is reached. + */ +class Expander +{ + private final Level level; + private final Direction down; + private final Direction right; + + private TileMonitor origin; + private int width; + private int height; + + Expander( TileMonitor origin ) + { + this.origin = origin; + width = origin.getWidth(); + height = origin.getHeight(); + + level = Objects.requireNonNull( origin.getLevel(), "level cannot be null" ); + down = origin.getDown(); + right = origin.getRight(); + } + + void expand() + { + int changedCount = 0; + + // Impose a limit on the number of resizes we can attempt. There's a risk of getting into an infinite loop + // if we merge right/down and the next monitor has a width/height of 0. This /should/ never happen - validation + // will catch it - but I also have a complete lack of faith in the code. + // As an aside, I think the actual limit is width+height resizes, but again - complete lack of faith. + int changeLimit = ComputerCraft.monitorWidth * ComputerCraft.monitorHeight + 1; + while( expandIn( true, false ) || expandIn( true, true ) || + expandIn( false, false ) || expandIn( false, true ) + ) + { + changedCount++; + if( changedCount > changeLimit ) + { + ComputerCraft.log.error( "Monitor has grown too much. This suggests there's an empty monitor in the world." ); + break; + } + } + + if( changedCount > 0 ) origin.resize( width, height ); + } + + /** + * Attempt to expand a monitor in a particular direction as much as possible. + * + * @param useXAxis {@literal true} if we're expanding on the X Axis, {@literal false} if on the Y. + * @param isPositive {@literal true} if we're expanding in the positive direction, {@literal false} if negative. + * @return If the monitor changed. + */ + private boolean expandIn( boolean useXAxis, boolean isPositive ) + { + BlockPos pos = origin.getBlockPos(); + int height = this.height, width = this.width; + + int otherOffset = isPositive ? (useXAxis ? width : height) : -1; + BlockPos otherPos = useXAxis ? pos.relative( right, otherOffset ) : pos.relative( down, otherOffset ); + BlockEntity other = level.getBlockEntity( otherPos ); + if( !(other instanceof TileMonitor otherMonitor) || !origin.isCompatible( otherMonitor ) ) return false; + + if( useXAxis ) + { + if( otherMonitor.getYIndex() != 0 || otherMonitor.getHeight() != height ) return false; + width += otherMonitor.getWidth(); + if( width > ComputerCraft.monitorWidth ) return false; + } + else + { + if( otherMonitor.getXIndex() != 0 || otherMonitor.getWidth() != width ) return false; + height += otherMonitor.getHeight(); + if( height > ComputerCraft.monitorHeight ) return false; + } + + if( !isPositive ) + { + BlockEntity otherOrigin = level.getBlockEntity( otherMonitor.toWorldPos( 0, 0 ) ); + if( otherOrigin == null || !origin.isCompatible( (TileMonitor) otherOrigin ) ) return false; + + origin = (TileMonitor) otherOrigin; + } + + this.width = width; + this.height = height; + + return true; + } + +} diff --git a/src/main/java/dan200/computercraft/shared/peripheral/monitor/TileMonitor.java b/src/main/java/dan200/computercraft/shared/peripheral/monitor/TileMonitor.java index 7e45d65e3..65ff5ab04 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/monitor/TileMonitor.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/monitor/TileMonitor.java @@ -33,6 +33,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.HashSet; import java.util.Set; +import java.util.function.Consumer; import static dan200.computercraft.shared.Capabilities.CAPABILITY_PERIPHERAL; @@ -58,7 +59,6 @@ public class TileMonitor extends TileGeneric private boolean needsUpdate = false; private boolean needsValidating = false; private boolean destroyed = false; - private boolean visiting = false; // MonitorWatcher state. boolean enqueued; @@ -79,7 +79,7 @@ public class TileMonitor extends TileGeneric public void clearRemoved() // TODO: Switch back to onLood { super.clearRemoved(); - needsValidating = true; + needsValidating = true; // Same, tbh TickScheduler.schedule( this ); } @@ -160,30 +160,14 @@ public class TileMonitor extends TileGeneric if( needsUpdate ) { needsUpdate = false; - updateNeighbors(); + expand(); } if( xIndex != 0 || yIndex != 0 || serverMonitor == null ) return; serverMonitor.clearChanged(); - if( serverMonitor.pollResized() ) - { - for( int x = 0; x < width; x++ ) - { - for( int y = 0; y < height; y++ ) - { - TileMonitor monitor = getNeighbour( x, y ).getMonitor(); - if( monitor == null ) continue; - - for( IComputerAccess computer : monitor.computers ) - { - computer.queueEvent( "monitor_resize", computer.getAttachmentName() ); - } - } - } - } - + if( serverMonitor.pollResized() ) eachComputer( c -> c.queueEvent( "monitor_resize", c.getAttachmentName() ) ); if( serverMonitor.pollTerminalChanged() ) MonitorWatcher.enqueue( this ); } @@ -208,11 +192,13 @@ public class TileMonitor extends TileGeneric return super.getCapability( cap, side ); } + @Nullable public ServerMonitor getCachedServerMonitor() { return serverMonitor; } + @Nullable private ServerMonitor getServerMonitor() { if( serverMonitor != null ) return serverMonitor; @@ -223,6 +209,7 @@ public class TileMonitor extends TileGeneric return serverMonitor = origin.serverMonitor; } + @Nullable private ServerMonitor createServerMonitor() { if( serverMonitor != null ) return serverMonitor; @@ -238,7 +225,7 @@ public class TileMonitor extends TileGeneric { for( int y = 0; y < height; y++ ) { - TileMonitor monitor = getNeighbour( x, y ).getMonitor(); + TileMonitor monitor = getLoadedMonitor( x, y ).getMonitor(); if( monitor != null ) monitor.serverMonitor = serverMonitor; } } @@ -250,19 +237,20 @@ public class TileMonitor extends TileGeneric // Otherwise fetch the origin and attempt to get its monitor // Note this may load chunks, but we don't really have a choice here. BlockPos pos = getBlockPos(); - BlockEntity te = level.getBlockEntity( pos.relative( getRight(), -xIndex ).relative( getDown(), -yIndex ) ); + BlockEntity te = level.getBlockEntity( toWorldPos( 0, 0 ) ); if( !(te instanceof TileMonitor) ) return null; return serverMonitor = ((TileMonitor) te).createServerMonitor(); } } + @Nullable public ClientMonitor getClientMonitor() { if( clientMonitor != null ) return clientMonitor; BlockPos pos = getBlockPos(); - BlockEntity te = level.getBlockEntity( pos.relative( getRight(), -xIndex ).relative( getDown(), -yIndex ) ); + BlockEntity te = level.getBlockEntity( toWorldPos( 0, 0 ) ); if( !(te instanceof TileMonitor) ) return null; return clientMonitor = ((TileMonitor) te).clientMonitor; @@ -383,10 +371,23 @@ public class TileMonitor extends TileGeneric return yIndex; } - @Nonnull - private MonitorState getSimilarMonitorAt( BlockPos pos ) + boolean isCompatible( TileMonitor other ) { - if( pos.equals( getBlockPos() ) ) return MonitorState.present( this ); + return !other.destroyed && advanced == other.advanced && getOrientation() == other.getOrientation() && getDirection() == other.getDirection(); + } + + /** + * Get a tile within the current monitor only if it is loaded and compatible. + * + * @param x Absolute X position in monitor coordinates + * @param y Absolute Y position in monitor coordinates + * @return The located monitor + */ + @Nonnull + private MonitorState getLoadedMonitor( int x, int y ) + { + if( x == xIndex && y == yIndex ) return MonitorState.present( this ); + BlockPos pos = toWorldPos( x, y ); Level world = getLevel(); if( world == null || !world.isAreaLoaded( pos, 0 ) ) return MonitorState.UNLOADED; @@ -394,27 +395,28 @@ public class TileMonitor extends TileGeneric BlockEntity tile = world.getBlockEntity( pos ); if( !(tile instanceof TileMonitor monitor) ) return MonitorState.MISSING; - return !monitor.visiting && !monitor.destroyed && advanced == monitor.advanced - && getDirection() == monitor.getDirection() && getOrientation() == monitor.getOrientation() - ? MonitorState.present( monitor ) : MonitorState.MISSING; - } - - private MonitorState getNeighbour( int x, int y ) - { - BlockPos pos = getBlockPos(); - Direction right = getRight(); - Direction down = getDown(); - int xOffset = -xIndex + x; - int yOffset = -yIndex + y; - return getSimilarMonitorAt( pos.relative( right, xOffset ).relative( down, yOffset ) ); + return isCompatible( monitor ) ? MonitorState.present( monitor ) : MonitorState.MISSING; } private MonitorState getOrigin() { - return getNeighbour( 0, 0 ); + return getLoadedMonitor( 0, 0 ); } - private void resize( int width, int height ) + /** + * Convert monitor coordinates to world coordinates. + * + * @param x Absolute X position in monitor coordinates + * @param y Absolute Y position in monitor coordinates + * @return The monitor's position. + */ + BlockPos toWorldPos( int x, int y ) + { + if( xIndex == x && yIndex == y ) return getBlockPos(); + return getBlockPos().relative( getRight(), -xIndex + x ).relative( getDown(), -yIndex + y ); + } + + void resize( int width, int height ) { // If we're not already the origin then we'll need to generate a new terminal. if( xIndex != 0 || yIndex != 0 ) serverMonitor = null; @@ -433,7 +435,7 @@ public class TileMonitor extends TileGeneric { for( int y = 0; y < height; y++ ) { - TileMonitor monitor = getNeighbour( x, y ).getMonitor(); + TileMonitor monitor = getLoadedMonitor( x, y ).getMonitor(); if( monitor != null && monitor.peripheral != null ) { needsTerminal = true; @@ -457,190 +459,78 @@ public class TileMonitor extends TileGeneric if( serverMonitor != null ) serverMonitor.rebuild(); // Update the other monitors, setting coordinates, dimensions and the server terminal + BlockPos pos = getBlockPos(); + Direction down = getDown(), right = getRight(); for( int x = 0; x < width; x++ ) { for( int y = 0; y < height; y++ ) { - TileMonitor monitor = getNeighbour( x, y ).getMonitor(); - if( monitor == null ) continue; + BlockEntity other = getLevel().getBlockEntity( pos.relative( right, x ).relative( down, y ) ); + if( !(other instanceof TileMonitor monitor) || !isCompatible( monitor ) ) continue; monitor.xIndex = x; monitor.yIndex = y; monitor.width = width; monitor.height = height; monitor.serverMonitor = serverMonitor; + monitor.needsUpdate = monitor.needsValidating = false; monitor.updateBlockState(); monitor.updateBlock(); } } } - private boolean mergeLeft() - { - TileMonitor left = getNeighbour( -1, 0 ).getMonitor(); - if( left == null || left.yIndex != 0 || left.height != height ) return false; - - int width = left.width + this.width; - if( width > ComputerCraft.monitorWidth ) return false; - - TileMonitor origin = left.getOrigin().getMonitor(); - if( origin != null ) origin.resize( width, height ); - left.expand(); - return true; - } - - private boolean mergeRight() - { - TileMonitor right = getNeighbour( width, 0 ).getMonitor(); - if( right == null || right.yIndex != 0 || right.height != height ) return false; - - int width = this.width + right.width; - if( width > ComputerCraft.monitorWidth ) return false; - - TileMonitor origin = getOrigin().getMonitor(); - if( origin != null ) origin.resize( width, height ); - expand(); - return true; - } - - private boolean mergeUp() - { - TileMonitor above = getNeighbour( 0, height ).getMonitor(); - if( above == null || above.xIndex != 0 || above.width != width ) return false; - - int height = above.height + this.height; - if( height > ComputerCraft.monitorHeight ) return false; - - TileMonitor origin = getOrigin().getMonitor(); - if( origin != null ) origin.resize( width, height ); - expand(); - return true; - } - - private boolean mergeDown() - { - TileMonitor below = getNeighbour( 0, -1 ).getMonitor(); - if( below == null || below.xIndex != 0 || below.width != width ) return false; - - int height = this.height + below.height; - if( height > ComputerCraft.monitorHeight ) return false; - - TileMonitor origin = below.getOrigin().getMonitor(); - if( origin != null ) origin.resize( width, height ); - below.expand(); - return true; - } - void updateNeighborsDeferred() { needsUpdate = true; } - void updateNeighbors() - { - contractNeighbours(); - contract(); - expand(); - } - - @SuppressWarnings( "StatementWithEmptyBody" ) void expand() { - while( mergeLeft() || mergeRight() || mergeUp() || mergeDown() ) ; + TileMonitor monitor = getOrigin().getMonitor(); + if( monitor != null && monitor.xIndex == 0 && monitor.yIndex == 0 ) new Expander( monitor ).expand(); } - void contractNeighbours() + private void contractNeighbours() { - visiting = true; - if( xIndex > 0 ) + if( width == 1 && height == 1 ) return; + + BlockPos pos = getBlockPos(); + Direction down = getDown(), right = getRight(); + BlockPos origin = toWorldPos( 0, 0 ); + + TileMonitor toLeft = null, toAbove = null, toRight = null, toBelow = null; + if( xIndex > 0 ) toLeft = tryResizeAt( pos.relative( right, -xIndex ), xIndex, 1 ); + if( yIndex > 0 ) toAbove = tryResizeAt( origin, width, yIndex ); + if( xIndex < width - 1 ) toRight = tryResizeAt( pos.relative( right, 1 ), width - xIndex - 1, 1 ); + if( yIndex < height - 1 ) { - TileMonitor left = getNeighbour( xIndex - 1, yIndex ).getMonitor(); - if( left != null ) left.contract(); + toBelow = tryResizeAt( origin.relative( down, yIndex + 1 ), width, height - yIndex - 1 ); } - if( xIndex + 1 < width ) - { - TileMonitor right = getNeighbour( xIndex + 1, yIndex ).getMonitor(); - if( right != null ) right.contract(); - } - if( yIndex > 0 ) - { - TileMonitor below = getNeighbour( xIndex, yIndex - 1 ).getMonitor(); - if( below != null ) below.contract(); - } - if( yIndex + 1 < height ) - { - TileMonitor above = getNeighbour( xIndex, yIndex + 1 ).getMonitor(); - if( above != null ) above.contract(); - } - visiting = false; + + if( toLeft != null ) toLeft.expand(); + if( toAbove != null ) toAbove.expand(); + if( toRight != null ) toRight.expand(); + if( toBelow != null ) toBelow.expand(); } - void contract() + @Nullable + private TileMonitor tryResizeAt( BlockPos pos, int width, int height ) { - int height = this.height; - int width = this.width; - - TileMonitor origin = getOrigin().getMonitor(); - if( origin == null ) + BlockEntity tile = level.getBlockEntity( pos ); + if( tile instanceof TileMonitor monitor && isCompatible( monitor ) ) { - TileMonitor right = width > 1 ? getNeighbour( 1, 0 ).getMonitor() : null; - TileMonitor below = height > 1 ? getNeighbour( 0, 1 ).getMonitor() : null; - - if( right != null ) right.resize( width - 1, 1 ); - if( below != null ) below.resize( width, height - 1 ); - if( right != null ) right.expand(); - if( below != null ) below.expand(); - - return; + monitor.resize( width, height ); + return monitor; } - for( int y = 0; y < height; y++ ) - { - for( int x = 0; x < width; x++ ) - { - TileMonitor monitor = origin.getNeighbour( x, y ).getMonitor(); - if( monitor != null ) continue; - - // Decompose - TileMonitor above = null; - TileMonitor left = null; - TileMonitor right = null; - TileMonitor below = null; - - if( y > 0 ) - { - above = origin; - above.resize( width, y ); - } - if( x > 0 ) - { - left = origin.getNeighbour( 0, y ).getMonitor(); - left.resize( x, 1 ); - } - if( x + 1 < width ) - { - right = origin.getNeighbour( x + 1, y ).getMonitor(); - right.resize( width - (x + 1), 1 ); - } - if( y + 1 < height ) - { - below = origin.getNeighbour( 0, y + 1 ).getMonitor(); - below.resize( width, height - (y + 1) ); - } - - // Re-expand - if( above != null ) above.expand(); - if( left != null ) left.expand(); - if( right != null ) right.expand(); - if( below != null ) below.expand(); - return; - } - } + return null; } + private boolean checkMonitorAt( int xIndex, int yIndex ) { - MonitorState state = getNeighbour( xIndex, yIndex ); + MonitorState state = getLoadedMonitor( xIndex, yIndex ); if( state.isMissing() ) return false; TileMonitor monitor = state.getMonitor(); @@ -651,9 +541,11 @@ public class TileMonitor extends TileGeneric private void validate() { - if( xIndex == 0 && yIndex == 0 && width == 1 || height == 1 ) return; + if( xIndex == 0 && yIndex == 0 && width == 1 && height == 1 ) return; - if( checkMonitorAt( 0, 0 ) && checkMonitorAt( 0, height - 1 ) && + if( xIndex >= 0 && xIndex <= width && width > 0 && width <= ComputerCraft.monitorWidth && + yIndex >= 0 && yIndex <= height && height > 0 && height <= ComputerCraft.monitorHeight && + checkMonitorAt( 0, 0 ) && checkMonitorAt( 0, height - 1 ) && checkMonitorAt( width - 1, 0 ) && checkMonitorAt( width - 1, height - 1 ) ) { return; @@ -665,6 +557,7 @@ public class TileMonitor extends TileGeneric resize( 1, 1 ); needsUpdate = true; } + // endregion private void monitorTouched( float xPos, float yPos, float zPos ) { @@ -689,21 +582,22 @@ public class TileMonitor extends TileGeneric int xCharPos = (int) Math.min( originTerminal.getWidth(), Math.max( (pair.x - RENDER_BORDER - RENDER_MARGIN) / xCharWidth + 1.0, 1.0 ) ); int yCharPos = (int) Math.min( originTerminal.getHeight(), Math.max( (pair.y - RENDER_BORDER - RENDER_MARGIN) / yCharHeight + 1.0, 1.0 ) ); - for( int y = 0; y < height; y++ ) + eachComputer( c -> c.queueEvent( "monitor_touch", c.getAttachmentName(), xCharPos, yCharPos ) ); + } + + private void eachComputer( Consumer fun ) + { + for( int x = 0; x < width; x++ ) { - for( int x = 0; x < width; x++ ) + for( int y = 0; y < height; y++ ) { - TileMonitor monitor = getNeighbour( x, y ).getMonitor(); + TileMonitor monitor = getLoadedMonitor( x, y ).getMonitor(); if( monitor == null ) continue; - for( IComputerAccess computer : monitor.computers ) - { - computer.queueEvent( "monitor_touch", computer.getAttachmentName(), xCharPos, yCharPos ); - } + for( IComputerAccess computer : monitor.computers ) fun.accept( computer ); } } } - // endregion void addComputer( IComputerAccess computer ) { @@ -719,24 +613,15 @@ public class TileMonitor extends TileGeneric @Override public AABB getRenderBoundingBox() { - TileMonitor start = getNeighbour( 0, 0 ).getMonitor(); - TileMonitor end = getNeighbour( width - 1, height - 1 ).getMonitor(); - if( start != null && end != null ) - { - BlockPos startPos = start.getBlockPos(); - BlockPos endPos = end.getBlockPos(); - int minX = Math.min( startPos.getX(), endPos.getX() ); - int minY = Math.min( startPos.getY(), endPos.getY() ); - int minZ = Math.min( startPos.getZ(), endPos.getZ() ); - int maxX = Math.max( startPos.getX(), endPos.getX() ) + 1; - int maxY = Math.max( startPos.getY(), endPos.getY() ) + 1; - int maxZ = Math.max( startPos.getZ(), endPos.getZ() ) + 1; - return new AABB( minX, minY, minZ, maxX, maxY, maxZ ); - } - else - { - BlockPos pos = getBlockPos(); - return new AABB( pos.getX(), pos.getY(), pos.getZ(), pos.getX() + 1, pos.getY() + 1, pos.getZ() + 1 ); - } + BlockPos startPos = toWorldPos( 0, 0 ); + BlockPos endPos = toWorldPos( width, height ); + return new AABB( + Math.min( startPos.getX(), endPos.getX() ), + Math.min( startPos.getY(), endPos.getY() ), + Math.min( startPos.getZ(), endPos.getZ() ), + Math.max( startPos.getX(), endPos.getX() ) + 1, + Math.max( startPos.getY(), endPos.getY() ) + 1, + Math.max( startPos.getZ(), endPos.getZ() ) + 1 + ); } } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/speaker/UpgradeSpeakerPeripheral.java b/src/main/java/dan200/computercraft/shared/peripheral/speaker/UpgradeSpeakerPeripheral.java index cbe318845..d24dcdd69 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/speaker/UpgradeSpeakerPeripheral.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/speaker/UpgradeSpeakerPeripheral.java @@ -8,6 +8,9 @@ package dan200.computercraft.shared.peripheral.speaker; import dan200.computercraft.api.peripheral.IComputerAccess; import dan200.computercraft.shared.network.NetworkHandler; import dan200.computercraft.shared.network.client.SpeakerStopClientMessage; +import net.minecraft.server.MinecraftServer; +import net.minecraftforge.fml.LogicalSide; +import net.minecraftforge.fmllegacy.LogicalSidedProvider; import javax.annotation.Nonnull; import java.util.UUID; @@ -28,6 +31,10 @@ public abstract class UpgradeSpeakerPeripheral extends SpeakerPeripheral @Override public void detach( @Nonnull IComputerAccess computer ) { + // We could be in the process of shutting down the server, so we can't send packets in this case. + MinecraftServer server = LogicalSidedProvider.INSTANCE.get( LogicalSide.SERVER ); + if( server == null || server.isStopped() ) return; + NetworkHandler.sendToAllPlayers( new SpeakerStopClientMessage( source ) ); } } diff --git a/src/main/java/dan200/computercraft/shared/turtle/apis/TurtleAPI.java b/src/main/java/dan200/computercraft/shared/turtle/apis/TurtleAPI.java index 3d6997141..41cf9c655 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/apis/TurtleAPI.java +++ b/src/main/java/dan200/computercraft/shared/turtle/apis/TurtleAPI.java @@ -11,7 +11,6 @@ import dan200.computercraft.api.turtle.ITurtleCommand; import dan200.computercraft.api.turtle.TurtleCommandResult; import dan200.computercraft.api.turtle.TurtleSide; import dan200.computercraft.core.apis.IAPIEnvironment; -import dan200.computercraft.core.asm.TaskCallback; import dan200.computercraft.core.tracking.TrackingField; import dan200.computercraft.shared.peripheral.generic.data.ItemData; import dan200.computercraft.shared.peripheral.generic.methods.InventoryMethods; @@ -767,7 +766,7 @@ public class TurtleAPI implements ILuaAPI { int actualSlot = checkSlot( slot ).orElse( turtle.getSelectedSlot() ); return detailed.orElse( false ) - ? TaskCallback.make( context, () -> getItemDetail( actualSlot, true ) ) + ? context.executeMainThreadTask( () -> getItemDetail( actualSlot, true ) ) : MethodResult.of( getItemDetail( actualSlot, false ) ); } diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlaceCommand.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlaceCommand.java index 8e8529c1d..0dba03e3b 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlaceCommand.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlaceCommand.java @@ -33,6 +33,7 @@ import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.Vec3; import net.minecraftforge.common.ForgeHooks; +import net.minecraftforge.common.util.Constants; import net.minecraftforge.event.entity.player.PlayerInteractEvent; import net.minecraftforge.eventbus.api.Event.Result; import net.minecraftforge.items.IItemHandler; @@ -320,7 +321,7 @@ public class TurtlePlaceCommand implements ITurtleCommand } } signTile.setChanged(); - world.sendBlockUpdated( tile.getBlockPos(), tile.getBlockState(), tile.getBlockState(), 3 ); + world.sendBlockUpdated( tile.getBlockPos(), tile.getBlockState(), tile.getBlockState(), Constants.BlockFlags.DEFAULT ); } private static class ErrorMessage diff --git a/src/main/resources/assets/computercraft/lang/fr_fr.json b/src/main/resources/assets/computercraft/lang/fr_fr.json index fd21b0cd3..9565d80f7 100644 --- a/src/main/resources/assets/computercraft/lang/fr_fr.json +++ b/src/main/resources/assets/computercraft/lang/fr_fr.json @@ -48,6 +48,7 @@ "commands.computercraft.dump.synopsis": "Affiche le statut des ordinateurs.", "commands.computercraft.dump.desc": "Affiche les statuts de tous les ordinateurs, ou des information spécifiques sur un ordinateur. Vous pouvez spécifier l'identifiant d'instance (ex. 123), l'identifiant d'ordinateur (ex. #123) ou son nom (ex. \"@Mon Ordinateur\").", "commands.computercraft.dump.action": "Voir plus d'informations à propos de cet ordinateur", + "commands.computercraft.dump.open_path": "Voir les fichiers de cet ordinateur", "commands.computercraft.shutdown.synopsis": "Éteindre des ordinateurs à distance.", "commands.computercraft.shutdown.desc": "Éteint les ordinateurs dans la liste ou tous, si aucun n'est spécifié dans cette liste. Vous pouvez spécifier l'identifiant d'instance (ex. 123), l'identifiant d'ordinateur (ex. #123) ou son nom (ex. \"@Mon Ordinateur\").", "commands.computercraft.shutdown.done": "%s/%s ordinateurs arrêté", @@ -110,14 +111,12 @@ "gui.computercraft.tooltip.copy": "Copier dans le Presse-Papiers", "gui.computercraft.tooltip.computer_id": "ID d'ordinateur : %s", "gui.computercraft.tooltip.disk_id": "ID de disque : %s", + "gui.computercraft.tooltip.turn_on": "Allumer cet ordinateur", "gui.computercraft.tooltip.turn_on.key": "Ternir Ctrl+R", "gui.computercraft.tooltip.turn_off": "Éteindre cet ordinateur", "gui.computercraft.tooltip.turn_off.key": "Tenir Ctrl+S", "gui.computercraft.tooltip.terminate": "Arrêter le programme en cours d'éxecution", "gui.computercraft.tooltip.terminate.key": "Tenir Ctrl+T", - "gui.computercraft.upload.overwrite": "Les fichiers seraient écrasés", - "gui.computercraft.upload.overwrite.detail": "Les fichiers suivants seront écrasés lors de l'envoie. Continuer?%s", - "gui.computercraft.upload.overwrite_button": "Écraser", "gui.computercraft.upload.success": "Envoie avec succès", "gui.computercraft.upload.success.msg": "Le fichier %d est envoyé.", "gui.computercraft.upload.failed": "Echec de l'envoie", @@ -126,6 +125,7 @@ "gui.computercraft.upload.failed.too_much": "Votre fichier est trop lourd pour être envoyé.", "gui.computercraft.upload.failed.overwrite_dir": "%s ne peut pas être envoyé, il y a déjà un dossier avec le même nom.", "gui.computercraft.upload.failed.generic": "Echec de l'envoie des fichiers (%s)", - "commands.computercraft.dump.open_path": "Voir les fichiers de cet ordinateur", - "gui.computercraft.tooltip.turn_on": "Allumer cet ordinateur" + "gui.computercraft.upload.overwrite": "Les fichiers seraient écrasés", + "gui.computercraft.upload.overwrite.detail": "Les fichiers suivants seront écrasés lors de l'envoie. Continuer?%s", + "gui.computercraft.upload.overwrite_button": "Écraser" } diff --git a/src/main/resources/assets/computercraft/lang/it_it.json b/src/main/resources/assets/computercraft/lang/it_it.json index 71daaddcf..0b2595157 100644 --- a/src/main/resources/assets/computercraft/lang/it_it.json +++ b/src/main/resources/assets/computercraft/lang/it_it.json @@ -9,7 +9,7 @@ "block.computercraft.monitor_normal": "Monitor", "block.computercraft.monitor_advanced": "Monitor Avanzato", "block.computercraft.wireless_modem_normal": "Modem Wireless", - "block.computercraft.wireless_modem_advanced": "Modem di Ender", + "block.computercraft.wireless_modem_advanced": "Modem Dell'Ender", "block.computercraft.wired_modem": "Modem Cablato", "block.computercraft.cable": "Cavo Di Rete", "block.computercraft.wired_modem_full": "Modem Cablato", @@ -48,6 +48,7 @@ "commands.computercraft.dump.synopsis": "Mostra lo stato dei computer.", "commands.computercraft.dump.desc": "Mostra lo stato di tutti i computer o informazioni specifiche su un computer. Puoi specificare l'instance id di un computer (e.g. 123), l'id di un computer (e.g. #123) o l'etichetta (e.g. \"@Il mio Computer\").", "commands.computercraft.dump.action": "Mostra più informazioni su questo computer", + "commands.computercraft.dump.open_path": "Mostra i file di questo computer", "commands.computercraft.shutdown.synopsis": "Spegne i computer da remoto.", "commands.computercraft.shutdown.desc": "Spegne i computer specificati o tutti se non specificati. Puoi specificare l'instance id del computer (e.g. 123), l'id del computer (e.g. #123) o l'etichetta (e.g. \"@Il mio Computer\").", "commands.computercraft.shutdown.done": "Spenti %s/%s computer", @@ -109,5 +110,26 @@ "tracking_field.computercraft.coroutines_dead.name": "Coroutine cancellate", "gui.computercraft.tooltip.copy": "Copia negli appunti", "gui.computercraft.tooltip.computer_id": "ID Computer: %s", - "gui.computercraft.tooltip.disk_id": "ID Disco: %s" + "gui.computercraft.tooltip.disk_id": "ID Disco: %s", + "gui.computercraft.tooltip.turn_on": "Accendi questo computer", + "gui.computercraft.tooltip.turn_on.key": "Tieni premuto Ctrl+R", + "gui.computercraft.tooltip.turn_off": "Spegni questo computer", + "gui.computercraft.tooltip.turn_off.key": "Tieni premuto Ctrl+S", + "gui.computercraft.tooltip.terminate": "Ferma il codice in esecuzione", + "gui.computercraft.tooltip.terminate.key": "Tieni premuto Ctrl+T", + "gui.computercraft.upload.success": "Caricato con successo", + "gui.computercraft.upload.success.msg": "%d file caricati.", + "gui.computercraft.upload.failed": "Caricamento fallito", + "gui.computercraft.upload.failed.out_of_space": "Non c'è abbastanza spazio nel computer per questi file.", + "gui.computercraft.upload.failed.computer_off": "Devi accendere il computer prima di caricare file.", + "gui.computercraft.upload.failed.too_much": "I tuoi file sono troppo grandi per essere caricati.", + "gui.computercraft.upload.failed.name_too_long": "I nomi dei file sono troppo lunghi per essere caricati.", + "gui.computercraft.upload.failed.too_many_files": "Non puoi caricare troppi file.", + "gui.computercraft.upload.failed.overwrite_dir": "Non puoi caricare %s perché esiste una cartella con lo stesso nome.", + "gui.computercraft.upload.failed.generic": "Impossibile inviare i file (%s)", + "gui.computercraft.upload.failed.corrupted": "File corrotti durante il caricamento. Riprova.", + "gui.computercraft.upload.overwrite": "Alcuni file saranno sovrascritti", + "gui.computercraft.upload.overwrite.detail": "I seguenti file saranno sovrascritti durante il caricamento. Continuare?%s", + "gui.computercraft.upload.overwrite_button": "Sovrascrivi", + "gui.computercraft.pocket_computer_overlay": "Computer tascabile aperto. Premi ESC per chiudere." } diff --git a/src/main/resources/assets/computercraft/lang/ja_jp.json b/src/main/resources/assets/computercraft/lang/ja_jp.json index c006855ed..ac955e10f 100644 --- a/src/main/resources/assets/computercraft/lang/ja_jp.json +++ b/src/main/resources/assets/computercraft/lang/ja_jp.json @@ -8,6 +8,7 @@ "block.computercraft.speaker": "スピーカー", "block.computercraft.monitor_normal": "モニター", "block.computercraft.monitor_advanced": "高度なモニター", + "block.computercraft.wireless_modem_normal": "無線モデム", "block.computercraft.wireless_modem_advanced": "エンダーモデム", "block.computercraft.wired_modem": "有線モデム", "block.computercraft.cable": "ネットワークケーブル", @@ -17,6 +18,7 @@ "block.computercraft.turtle_normal.upgraded_twice": "%s%sタートル", "block.computercraft.turtle_advanced": "高度なタートル", "block.computercraft.turtle_advanced.upgraded": "高度な%sタートル", + "block.computercraft.turtle_advanced.upgraded_twice": "高度な%s%sタートル", "item.computercraft.disk": "フロッピーディスク", "item.computercraft.treasure_disk": "フロッピーディスク", "item.computercraft.printed_page": "印刷された紙", @@ -24,52 +26,66 @@ "item.computercraft.printed_book": "印刷された本", "item.computercraft.pocket_computer_normal": "ポケットコンピュータ", "item.computercraft.pocket_computer_normal.upgraded": "%sポケットコンピュータ", + "item.computercraft.pocket_computer_advanced": "高度なポケットコンピュータ", "item.computercraft.pocket_computer_advanced.upgraded": "高度な%sポケットコンピュータ", + "upgrade.minecraft.diamond_sword.adjective": "攻撃", "upgrade.minecraft.diamond_shovel.adjective": "掘削", "upgrade.minecraft.diamond_pickaxe.adjective": "採掘", "upgrade.minecraft.diamond_axe.adjective": "伐採", "upgrade.minecraft.diamond_hoe.adjective": "農耕", "upgrade.minecraft.crafting_table.adjective": "クラフト", + "upgrade.computercraft.wireless_modem_normal.adjective": "無線", "upgrade.computercraft.wireless_modem_advanced.adjective": "エンダー", "upgrade.computercraft.speaker.adjective": "騒音", "chat.computercraft.wired_modem.peripheral_connected": "周辺の\"%s\"のネットワークに接続されました", + "chat.computercraft.wired_modem.peripheral_disconnected": "周辺の\"%s\"のネットワークから切断されました", "commands.computercraft.synopsis": "コンピュータを制御するためのさまざまなコマンド。", + "commands.computercraft.desc": "/computercraft コマンドは、コンピュータとの制御および対話するためのさまざまなデバッグツールと管理者ツールを提供します。", "commands.computercraft.help.synopsis": "特定のコマンドのヘルプを提供します", "commands.computercraft.help.desc": "このヘルプメッセージを表示します", "commands.computercraft.help.no_children": "%s にサブコマンドはありません", "commands.computercraft.help.no_command": "%s というコマンドはありません", "commands.computercraft.dump.synopsis": "コンピュータの状態を表示します。", + "commands.computercraft.dump.desc": "すべてのコンピューターの状態、または一台のコンピューターの特定の情報を表示する。 コンピュータのインスタンスID (例えば 123), コンピュータID (例えば #123) またはラベル (例えば \\\"@My Computer\\\") を指定することができます。", "commands.computercraft.dump.action": "このコンピュータの詳細を表示します", "commands.computercraft.dump.open_path": "このコンピュータのファイルを表示します", "commands.computercraft.shutdown.synopsis": "コンピュータをリモートでシャットダウンする。", + "commands.computercraft.shutdown.desc": "指定されたコンピュータ、指定されていない場合はすべてのコンピュータをシャットダウンします。 コンピュータのインスタンスID (例えば 123), コンピュータID (例えば #123) またはラベル (例えば \\\"@My Computer\\\") を指定することができます。", "commands.computercraft.shutdown.done": "%s/%s コンピューターをシャットダウンしました", + "commands.computercraft.turn_on.synopsis": "コンピューターをリモートで起動します。", "commands.computercraft.turn_on.desc": "指定されているコンピュータを起動します。 コンピュータのインスタンスID (例えば 123), コンピュータID (例えば #123) またはラベル (例えば \\\"@My Computer\\\") を指定することができます。", "commands.computercraft.turn_on.done": "%s/%s コンピューターを起動しました", "commands.computercraft.tp.synopsis": "特定のコンピュータにテレポート。", + "commands.computercraft.tp.desc": "コンピュータの場所にテレポート.コンピュータのインスタンスID(例えば 123)またはコンピュータID(例えば #123)を指定することができます。", "commands.computercraft.tp.action": "このコンピューターへテレポートします", "commands.computercraft.tp.not_player": "非プレイヤー用のターミナルを開くことができません", "commands.computercraft.tp.not_there": "世界でコンピュータを見つけることができませんでした", "commands.computercraft.view.synopsis": "コンピュータのターミナルを表示します。", - "commands.computercraft.turn_on.synopsis": "コンピューターをリモートで起動します。", + "commands.computercraft.view.desc": "コンピュータのターミナルを開き、コンピュータのリモートコントロールを可能にします。 これはタートルのインベントリへのアクセスを提供しません。 コンピュータのインスタンスID(例えば 123)またはコンピュータID(例えば #123)を指定することができます。", "commands.computercraft.view.action": "このコンピュータを見ます", "commands.computercraft.view.not_player": "非プレイヤー用のターミナルを開くことができません", "commands.computercraft.track.synopsis": "コンピュータの実行時間を追跡します。", + "commands.computercraft.track.desc": "コンピュータの実行時間を追跡するだけでなく、イベントを確認することができます。 これは /forge と同様の方法で情報を提示し、遅れを診断するのに役立ちます。", "commands.computercraft.track.start.synopsis": "すべてのコンピュータの追跡を開始します", "commands.computercraft.track.start.desc": "すべてのコンピュータの実行時間とイベント数の追跡を開始します。 これにより、以前の実行結果が破棄されます。", + "commands.computercraft.track.start.stop": "トラッキングを停止して結果を表示するには %s を実行してください", "commands.computercraft.track.stop.synopsis": "すべてのコンピュータの追跡を停止します", + "commands.computercraft.track.stop.desc": "すべてのコンピュータのイベントと実行時間の追跡を停止します", "commands.computercraft.track.stop.action": "追跡を中止するためにクリックしてください", "commands.computercraft.track.stop.not_enabled": "現在コンピュータを追跡していません", "commands.computercraft.track.dump.synopsis": "最新の追跡結果をダンプしてください", "commands.computercraft.track.dump.desc": "コンピュータの最新の追跡結果をダンプしてください。", "commands.computercraft.track.dump.no_timings": "利用可能なタイミングはありません", + "commands.computercraft.track.dump.computer": "コンピューター", "commands.computercraft.reload.synopsis": "コンピュータークラフトのコンフィグファイルを再読み込みします", + "commands.computercraft.reload.desc": "コンピュータークラフトのコンフィグファイルを再読み込みします", "commands.computercraft.reload.done": "コンフィグを再読み込みしました", "commands.computercraft.queue.synopsis": "computer_command インベントをコマンドコンピューターに送信します", + "commands.computercraft.queue.desc": "追加の引数を通過する computer_command インベントをコマンドコンピューターに送信します。これは主にマップメーカーのために設計されており、よりコンピュータフレンドリーバージョンの /trigger として機能します。 どのプレイヤーでもコマンドを実行できます。これは、テキストコンポーネントのクリックイベントを介して行われる可能性があります。", "commands.computercraft.generic.no_position": "", "commands.computercraft.generic.position": "%s, %s, %s", "commands.computercraft.generic.yes": "Y", "commands.computercraft.generic.no": "N", - "commands.computercraft.track.start.stop": "トラッキングを停止して結果を表示するには %s を実行してください", "commands.computercraft.generic.exception": "未処理の例外 (%s)", "commands.computercraft.generic.additional_rows": "%d行を追加…", "argument.computercraft.computer.no_matching": "'%s'に一致するコンピュータはありません", @@ -82,6 +98,7 @@ "tracking_field.computercraft.max.name": "最大時間", "tracking_field.computercraft.server_count.name": "サーバータスク数", "tracking_field.computercraft.server_time.name": "サーバータスク時間", + "tracking_field.computercraft.peripheral.name": "実行呼び出し", "tracking_field.computercraft.fs.name": "ファイルシステム演算", "tracking_field.computercraft.turtle.name": "タートル演算", "tracking_field.computercraft.http.name": "HTTPリクエスト", @@ -89,43 +106,30 @@ "tracking_field.computercraft.http_download.name": "HTTPダウンロード", "tracking_field.computercraft.websocket_incoming.name": "Websocket 受信", "tracking_field.computercraft.websocket_outgoing.name": "Websocket 送信", + "tracking_field.computercraft.coroutines_created.name": "コルーチン作成", "tracking_field.computercraft.coroutines_dead.name": "コルーチン削除", "gui.computercraft.tooltip.copy": "クリップボードにコピー", "gui.computercraft.tooltip.computer_id": "コンピュータID: %s", "gui.computercraft.tooltip.disk_id": "ディスクID: %s", "gui.computercraft.tooltip.turn_on": "このコンピュータをオンにする", "gui.computercraft.tooltip.turn_on.key": "Ctrl+R 長押し", + "gui.computercraft.tooltip.turn_off": "このコンピュータをオフにする", "gui.computercraft.tooltip.turn_off.key": "Ctrl+S 長押し", "gui.computercraft.tooltip.terminate": "現在実行中のコードを停止する", "gui.computercraft.tooltip.terminate.key": "Ctrl+T 長押し", "gui.computercraft.upload.success": "アップロードは成功しました", + "gui.computercraft.upload.success.msg": "%d個のファイルがアップロードされました。", "gui.computercraft.upload.failed": "アップロードに失敗しました", "gui.computercraft.upload.failed.out_of_space": "これらのファイルに必要なスペースがコンピュータ上にありません。", + "gui.computercraft.upload.failed.computer_off": "ファイルをアップロードする前にコンピュータを起動する必要があります。", "gui.computercraft.upload.failed.too_much": "アップロードするにはファイルが大きスギます。", + "gui.computercraft.upload.failed.name_too_long": "ファイル名が長すぎてアップロードできません。", + "gui.computercraft.upload.failed.too_many_files": "多くのファイルをアップロードできません。", "gui.computercraft.upload.failed.overwrite_dir": "同じ名前のディレクトリがすでにあるため、%s をアップロードできません。", "gui.computercraft.upload.failed.generic": "ファイルのアップロードに失敗しました(%s)", + "gui.computercraft.upload.failed.corrupted": "アップロード時にファイルが破損しました。 もう一度やり直してください。", "gui.computercraft.upload.overwrite": "ファイルは上書きされます", + "gui.computercraft.upload.overwrite.detail": "アップロード時に次のファイルが上書きされます。継続しますか?%s", "gui.computercraft.upload.overwrite_button": "上書き", - "block.computercraft.wireless_modem_normal": "無線モデム", - "block.computercraft.turtle_advanced.upgraded_twice": "高度な%s%sタートル", - "item.computercraft.pocket_computer_advanced": "高度なポケットコンピュータ", - "upgrade.minecraft.diamond_sword.adjective": "攻撃", - "upgrade.computercraft.wireless_modem_normal.adjective": "無線", - "chat.computercraft.wired_modem.peripheral_disconnected": "周辺の\"%s\"のネットワークから切断されました", - "commands.computercraft.desc": "/computercraft コマンドは、コンピュータとの制御および対話するためのさまざまなデバッグツールと管理者ツールを提供します。", - "commands.computercraft.dump.desc": "すべてのコンピューターの状態、または一台のコンピューターの特定の情報を表示する。 コンピュータのインスタンスID (例えば 123), コンピュータID (例えば #123) またはラベル (例えば \\\"@My Computer\\\") を指定することができます。", - "commands.computercraft.shutdown.desc": "指定されたコンピュータ、指定されていない場合はすべてのコンピュータをシャットダウンします。 コンピュータのインスタンスID (例えば 123), コンピュータID (例えば #123) またはラベル (例えば \\\"@My Computer\\\") を指定することができます。", - "commands.computercraft.tp.desc": "コンピュータの場所にテレポート.コンピュータのインスタンスID(例えば 123)またはコンピュータID(例えば #123)を指定することができます。", - "commands.computercraft.view.desc": "コンピュータのターミナルを開き、コンピュータのリモートコントロールを可能にします。 これはタートルのインベントリへのアクセスを提供しません。 コンピュータのインスタンスID(例えば 123)またはコンピュータID(例えば #123)を指定することができます。", - "commands.computercraft.track.desc": "コンピュータの実行時間を追跡するだけでなく、イベントを確認することができます。 これは /forge と同様の方法で情報を提示し、遅れを診断するのに役立ちます。", - "commands.computercraft.track.stop.desc": "すべてのコンピュータのイベントと実行時間の追跡を停止します", - "commands.computercraft.track.dump.computer": "コンピューター", - "commands.computercraft.reload.desc": "コンピュータークラフトのコンフィグファイルを再読み込みします", - "commands.computercraft.queue.desc": "追加の引数を通過する computer_command インベントをコマンドコンピューターに送信します。これは主にマップメーカーのために設計されており、よりコンピュータフレンドリーバージョンの /trigger として機能します。 どのプレイヤーでもコマンドを実行できます。これは、テキストコンポーネントのクリックイベントを介して行われる可能性があります。", - "tracking_field.computercraft.peripheral.name": "実行呼び出し", - "tracking_field.computercraft.coroutines_created.name": "コルーチン作成", - "gui.computercraft.tooltip.turn_off": "このコンピュータをオフにする", - "gui.computercraft.upload.success.msg": "%d個のファイルがアップロードされました。", - "gui.computercraft.upload.failed.computer_off": "ファイルをアップロードする前にコンピュータを起動する必要があります。", - "gui.computercraft.upload.overwrite.detail": "アップロード時に次のファイルが上書きされます。継続しますか?%s" + "gui.computercraft.pocket_computer_overlay": "ポケットコンピュータを開いています。 ESCを押して閉じます。" } diff --git a/src/main/resources/assets/computercraft/lang/ko_kr.json b/src/main/resources/assets/computercraft/lang/ko_kr.json index 919d7e56d..2c9048e62 100644 --- a/src/main/resources/assets/computercraft/lang/ko_kr.json +++ b/src/main/resources/assets/computercraft/lang/ko_kr.json @@ -12,6 +12,7 @@ "block.computercraft.wireless_modem_advanced": "엔더 모뎀", "block.computercraft.wired_modem": "유선 모뎀", "block.computercraft.cable": "네트워크 케이블", + "block.computercraft.wired_modem_full": "유선 모뎀", "block.computercraft.turtle_normal": "터틀", "block.computercraft.turtle_normal.upgraded": "%s 터틀", "block.computercraft.turtle_normal.upgraded_twice": "%s %s 터틀", @@ -41,11 +42,13 @@ "commands.computercraft.synopsis": "컴퓨터를 제어하기 위한 다양한 명령어", "commands.computercraft.desc": "/computercraft 명령어는 컴퓨터를 제어하고 상호작용하기 위한 다양한 디버깅 및 관리자 도구를 제공합니다.", "commands.computercraft.help.synopsis": "특정 명령어에 대한 도움말을 제공하기", + "commands.computercraft.help.desc": "이 도움말 메시지를 표시합니다.", "commands.computercraft.help.no_children": "%s에는 하위 명령어가 없습니다.", "commands.computercraft.help.no_command": "'%s'라는 명령어가 없습니다.", "commands.computercraft.dump.synopsis": "컴퓨터의 상태를 보여주기", "commands.computercraft.dump.desc": "모든 시스템의 상태 또는 한 시스템에 대한 특정 정보를 표시합니다. 컴퓨터의 인스턴스 ID(예: 123)나 컴퓨터 ID(예: #123) 또는 라벨(예: \"@My Computer\")을 지정할 수 있습니다.", "commands.computercraft.dump.action": "이 컴퓨터에 대한 추가 정보를 봅니다.", + "commands.computercraft.dump.open_path": "이 컴퓨터의 파일을 봅니다.", "commands.computercraft.shutdown.synopsis": "시스템을 원격으로 종료하기", "commands.computercraft.shutdown.desc": "나열된 시스템 또는 지정된 시스템이 없는 경우 모두 종료합니다. 컴퓨터의 인스턴스 ID(예: 123)나 컴퓨터 ID(예: #123) 또는 라벨(예: \"@My Computer\")을 지정할 수 있습니다.", "commands.computercraft.shutdown.done": "%s/%s 컴퓨터 시스템 종료", @@ -55,6 +58,7 @@ "commands.computercraft.tp.synopsis": "특정 컴퓨터로 순간이동하기", "commands.computercraft.tp.desc": "컴퓨터의 위치로 순간이동합니다. 컴퓨터의 인스턴스 ID(예: 123) 또는 컴퓨터 ID(예: #123)를 지정할 수 있습니다.", "commands.computercraft.tp.action": "이 컴퓨터로 순간이동하기", + "commands.computercraft.tp.not_player": "비플레이어용 터미널을 열 수 없습니다.", "commands.computercraft.tp.not_there": "월드에서 컴퓨터를 위치시킬 수 없습니다.", "commands.computercraft.view.synopsis": "컴퓨터의 터미널을 보기", "commands.computercraft.view.desc": "컴퓨터의 원격 제어를 허용하는 컴퓨터의 터미널을 엽니다. 이것은 터틀의 인벤토리에 대한 접근을 제공하지 않습니다. 컴퓨터의 인스턴스 ID(예: 123) 또는 컴퓨터 ID(예: #123)를 지정할 수 있습니다.", @@ -86,6 +90,8 @@ "commands.computercraft.generic.additional_rows": "%d개의 추가 행…", "argument.computercraft.computer.no_matching": "'%s'와 일치하는 컴퓨터가 없습니다.", "argument.computercraft.computer.many_matching": "'%s'와 일치하는 여러 컴퓨터 (인스턴스 %s)", + "argument.computercraft.tracking_field.no_field": "알 수 없는 필드 '%s'입니다.", + "argument.computercraft.argument_expected": "인수가 필요합니다.", "tracking_field.computercraft.tasks.name": "작업", "tracking_field.computercraft.total.name": "전체 시간", "tracking_field.computercraft.average.name": "평균 시간", @@ -104,5 +110,26 @@ "tracking_field.computercraft.coroutines_dead.name": "코루틴 처리됨", "gui.computercraft.tooltip.copy": "클립보드에 복사", "gui.computercraft.tooltip.computer_id": "컴퓨터 ID: %s", - "gui.computercraft.tooltip.disk_id": "디스크 ID: %s" + "gui.computercraft.tooltip.disk_id": "디스크 ID: %s", + "gui.computercraft.tooltip.turn_on": "이 컴퓨터를 켭니다.", + "gui.computercraft.tooltip.turn_on.key": "Ctrl+R을 누르세요.", + "gui.computercraft.tooltip.turn_off": "이 컴퓨터를 끕니다.", + "gui.computercraft.tooltip.turn_off.key": "Ctrl+S를 누르세요.", + "gui.computercraft.tooltip.terminate": "현재 실행 중인 코드를 중지합니다.", + "gui.computercraft.tooltip.terminate.key": "Ctrl+T를 누르세요.", + "gui.computercraft.upload.success": "업로드에 성공했습니다.", + "gui.computercraft.upload.success.msg": "%d개의 파일이 업로드되었습니다.", + "gui.computercraft.upload.failed": "업로드하지 못했습니다.", + "gui.computercraft.upload.failed.out_of_space": "컴퓨터에 공간이 부족하여 파일을 저장할 수 없습니다.", + "gui.computercraft.upload.failed.computer_off": "파일을 업로드하기 전에 컴퓨터를 켜야 합니다.", + "gui.computercraft.upload.failed.too_much": "파일이 너무 커서 업로드할 수 없습니다.", + "gui.computercraft.upload.failed.name_too_long": "파일 이름이 너무 길어서 업로드할 수 없습니다.", + "gui.computercraft.upload.failed.too_many_files": "이렇게 많은 파일을 업로드할 수 없습니다.", + "gui.computercraft.upload.failed.overwrite_dir": "같은 이름의 디렉터리가 이미 있으므로 %s을 업로드할 수 없습니다.", + "gui.computercraft.upload.failed.generic": "파일을 업로드하지 못했습니다. (%s)", + "gui.computercraft.upload.failed.corrupted": "업로드할 때 파일이 손상되었습니다. 다시 시도하십시오.", + "gui.computercraft.upload.overwrite": "파일을 덮어씁니다.", + "gui.computercraft.upload.overwrite.detail": "업로드 시 다음 파일을 덮어씁니다. 계속할까요?%s", + "gui.computercraft.upload.overwrite_button": "덮어쓰기", + "gui.computercraft.pocket_computer_overlay": "포켓 컴퓨터가 열립니다. ESC를 눌러 닫습니다." } diff --git a/src/main/resources/assets/computercraft/lang/ru_ru.json b/src/main/resources/assets/computercraft/lang/ru_ru.json index b3987d970..2ea69dba2 100644 --- a/src/main/resources/assets/computercraft/lang/ru_ru.json +++ b/src/main/resources/assets/computercraft/lang/ru_ru.json @@ -123,12 +123,12 @@ "gui.computercraft.upload.failed.out_of_space": "Недостаточно места в компьютере для этих файлов.", "gui.computercraft.upload.failed.computer_off": "Ты должен включить компьютер перед загрузой файлов.", "gui.computercraft.upload.failed.too_much": "Твои файлы слишком большие для загрузки.", - "gui.computercraft.upload.failed.overwrite_dir": "Нельзя загрузить %s, поскольку папка с таким же названием уже существует.", - "gui.computercraft.upload.failed.generic": "Загрузка файлов не удалась (%s)", - "gui.computercraft.upload.overwrite": "Файлы будут перезаписаны", - "gui.computercraft.upload.overwrite.detail": "При загрузке следующие файлы будут перезаписаны. Продолжить?%s", - "gui.computercraft.upload.overwrite_button": "Перезаписать", "gui.computercraft.upload.failed.name_too_long": "Названия файлов слишком длинны для загрузки.", "gui.computercraft.upload.failed.too_many_files": "Нельзя загрузить столько файлов.", - "gui.computercraft.upload.failed.corrupted": "Файлы повреждены при загрузки. Попробуй снова." + "gui.computercraft.upload.failed.overwrite_dir": "Нельзя загрузить %s, поскольку папка с таким же названием уже существует.", + "gui.computercraft.upload.failed.generic": "Загрузка файлов не удалась (%s)", + "gui.computercraft.upload.failed.corrupted": "Файлы повреждены при загрузки. Попробуй снова.", + "gui.computercraft.upload.overwrite": "Файлы будут перезаписаны", + "gui.computercraft.upload.overwrite.detail": "При загрузке следующие файлы будут перезаписаны. Продолжить?%s", + "gui.computercraft.upload.overwrite_button": "Перезаписать" } diff --git a/src/main/resources/data/computercraft/lua/rom/apis/rednet.lua b/src/main/resources/data/computercraft/lua/rom/apis/rednet.lua index db40ebeac..83f4bb6ad 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/rednet.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/rednet.lua @@ -403,10 +403,11 @@ function run() if sEvent == "modem_message" then -- Got a modem message, process it and add it to the rednet event queue local sModem, nChannel, nReplyChannel, tMessage = p1, p2, p3, p4 - if isOpen(sModem) and (nChannel == id_as_channel() or nChannel == CHANNEL_BROADCAST) then + if nChannel == id_as_channel() or nChannel == CHANNEL_BROADCAST then if type(tMessage) == "table" and type(tMessage.nMessageID) == "number" and tMessage.nMessageID == tMessage.nMessageID and not tReceivedMessages[tMessage.nMessageID] and ((tMessage.nRecipient and tMessage.nRecipient == os.getComputerID()) or nChannel == CHANNEL_BROADCAST) + and isOpen(sModem) then tReceivedMessages[tMessage.nMessageID] = os.clock() + 9.5 if not nClearTimer then nClearTimer = os.startTimer(10) end diff --git a/src/test/java/dan200/computercraft/core/apis/ObjectWrapper.java b/src/test/java/dan200/computercraft/core/apis/ObjectWrapper.java index 6dcc74a34..9088ab1e2 100644 --- a/src/test/java/dan200/computercraft/core/apis/ObjectWrapper.java +++ b/src/test/java/dan200/computercraft/core/apis/ObjectWrapper.java @@ -8,7 +8,6 @@ package dan200.computercraft.core.apis; import dan200.computercraft.api.lua.*; import dan200.computercraft.core.asm.LuaMethod; import dan200.computercraft.core.asm.NamedMethod; -import dan200.computercraft.core.asm.TaskCallback; import javax.annotation.Nonnull; import java.util.HashMap; @@ -65,11 +64,4 @@ public class ObjectWrapper implements ILuaContext { throw new IllegalStateException( "Method should never queue events" ); } - - @Nonnull - @Override - public MethodResult executeMainThreadTask( @Nonnull ILuaTask task ) throws LuaException - { - return TaskCallback.make( this, task ); - } } diff --git a/src/test/java/dan200/computercraft/core/asm/GeneratorTest.java b/src/test/java/dan200/computercraft/core/asm/GeneratorTest.java index 314c92bf8..21aec5288 100644 --- a/src/test/java/dan200/computercraft/core/asm/GeneratorTest.java +++ b/src/test/java/dan200/computercraft/core/asm/GeneratorTest.java @@ -124,7 +124,7 @@ public class GeneratorTest { @LuaFunction public final void go() - { } + {} } public static class Basic2 extends Basic @@ -139,14 +139,14 @@ public class GeneratorTest { @LuaFunction public final void go() - { } + {} } public static class NonInstance { @LuaFunction public static void go() - { } + {} } public static class IllegalThrows @@ -162,42 +162,42 @@ public class GeneratorTest { @LuaFunction( { "go1", "go2" } ) public final void go() - { } + {} } public static class ArgKinds { @LuaFunction public final void objectArg( Object arg ) - { } + {} @LuaFunction public final void intArg( int arg ) - { } + {} @LuaFunction public final void optIntArg( Optional arg ) - { } + {} @LuaFunction public final void context( ILuaContext arg ) - { } + {} @LuaFunction public final void arguments( IArguments arg ) - { } + {} @LuaFunction public final void unknown( IComputerAccess arg ) - { } + {} @LuaFunction public final void illegalMap( Map arg ) - { } + {} @LuaFunction public final void optIllegalMap( Optional> arg ) - { } + {} } public static class EnumMethods @@ -219,7 +219,7 @@ public class GeneratorTest { @LuaFunction( mainThread = true ) public final void go() - { } + {} } private static T find( Collection> methods, String name ) @@ -256,12 +256,5 @@ public class GeneratorTest { return 0; } - - @Nonnull - @Override - public MethodResult executeMainThreadTask( @Nonnull ILuaTask task ) throws LuaException - { - return TaskCallback.make( this, task ); - } }; } diff --git a/src/testMod/java/dan200/computercraft/ingame/MonitorTest.kt b/src/testMod/java/dan200/computercraft/ingame/MonitorTest.kt index 82518bbe5..05dc42d4b 100644 --- a/src/testMod/java/dan200/computercraft/ingame/MonitorTest.kt +++ b/src/testMod/java/dan200/computercraft/ingame/MonitorTest.kt @@ -57,7 +57,7 @@ class Monitor_Test { val monitor = helper.getBlockEntity(BlockPos(2, 2, 3), Registry.ModBlockEntities.MONITOR_ADVANCED.get()) monitor.getCapability(Capabilities.CAPABILITY_PERIPHERAL) - val terminal = monitor.cachedServerMonitor.terminal + val terminal = monitor.cachedServerMonitor!!.terminal terminal.write("Hello, world!") terminal.setCursorPos(1, 2) terminal.textColour = 2