diff --git a/.github/workflows/main-ci.yml b/.github/workflows/main-ci.yml index 3c5793c2d..b69180785 100644 --- a/.github/workflows/main-ci.yml +++ b/.github/workflows/main-ci.yml @@ -12,10 +12,10 @@ jobs: - name: Checkout submodules run: git submodule update --init --recursive - - name: Set up Java 8 + - name: Set up Java 16 uses: actions/setup-java@v1 with: - java-version: 8 + java-version: 16 - name: Cache gradle dependencies uses: actions/cache@v2 diff --git a/.gitignore b/.gitignore index 4efc71fe5..8288d3541 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ .gradle *.DS_Store .project +*.launch diff --git a/build.gradle b/build.gradle index 6cdcf6eb8..44ca32f99 100644 --- a/build.gradle +++ b/build.gradle @@ -1,13 +1,13 @@ plugins { - id 'fabric-loom' version '0.8-SNAPSHOT' + id 'fabric-loom' version '0.9-SNAPSHOT' id 'maven-publish' id "checkstyle" - id "com.github.hierynomus.license" version "0.15.0" + id "com.github.hierynomus.license" version "0.16.1" } java { toolchain { - languageVersion = JavaLanguageVersion.of(8) + languageVersion = JavaLanguageVersion.of(16) vendor = JvmVendorSpec.ADOPTOPENJDK } } @@ -22,18 +22,23 @@ repositories { maven { url 'https://jitpack.io' } maven { url 'https://api.modrinth.com/maven'} maven { url "https://maven.shedaniel.me/" } + maven { url "https://maven.terraformersmc.com/" } maven { name "SquidDev" url "https://squiddev.cc/maven" } } +loom { + accessWidenerPath = file("src/main/resources/cc.accesswidener") +} + configurations { implementation.extendsFrom shade } dependencies { - checkstyle "com.puppycrawl.tools:checkstyle:8.25" + checkstyle 'com.puppycrawl.tools:checkstyle:8.45.1' minecraft "com.mojang:minecraft:${mc_version}" mappings "net.fabricmc:yarn:${mc_version}+build.${mappings_version}:v2" @@ -43,7 +48,7 @@ dependencies { modApi("me.shedaniel.cloth:cloth-config-fabric:${cloth_config_version}") { exclude(group: "net.fabricmc.fabric-api") } - modImplementation "io.github.prospector:modmenu:${modmenu_version}" + modImplementation "com.terraformersmc:modmenu:${modmenu_version}" modImplementation "me.shedaniel.cloth.api:cloth-utils-v1:${cloth_api_version}" implementation 'com.electronwill.night-config:toml:3.6.3' @@ -56,21 +61,16 @@ dependencies { include 'com.electronwill.night-config:toml:3.6.3' include "me.shedaniel.cloth:cloth-config-fabric:${cloth_config_version}" - modRuntime "me.shedaniel:RoughlyEnoughItems-api:5.12.248" - modRuntime "me.shedaniel:RoughlyEnoughItems:5.12.248" + modRuntime "me.shedaniel:RoughlyEnoughItems-api-fabric:6.0.254-alpha" + modRuntime "me.shedaniel:RoughlyEnoughItems-fabric:6.0.254-alpha" } processResources { inputs.property "version", project.version - from(sourceSets.main.resources.srcDirs) { - include "fabric.mod.json" - expand "version": project.version - } - - from(sourceSets.main.resources.srcDirs) { - exclude "fabric.mod.json" - } + filesMatching("fabric.mod.json") { + expand "version": project.version + } } // ensure that the encoding is set to UTF-8, no matter what the system default is diff --git a/config/checkstyle/checkstyle.xml b/config/checkstyle/checkstyle.xml index aaafdfd01..ebc62360c 100644 --- a/config/checkstyle/checkstyle.xml +++ b/config/checkstyle/checkstyle.xml @@ -58,13 +58,13 @@ - + - + diff --git a/gradle.properties b/gradle.properties index 03bde43ce..dd8208c4f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -6,12 +6,12 @@ mod_version=1.96.1-rc1 # Minecraft properties mc_version=1.17.1 -mappings_version=10 +mappings_version=61 # Dependencies cloth_config_version=5.0.34 -fabric_loader_version=0.11.6 -fabric_api_version=0.36.1+1.17 +fabric_loader_version=0.11.7 +fabric_api_version=0.40.1+1.17 jankson_version=1.2.0 -modmenu_version=1.16.9 +modmenu_version=2.0.2 cloth_api_version=2.0.54 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 05679dc3c..ffed3a254 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.1.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/java/dan200/computercraft/ComputerCraft.java b/src/main/java/dan200/computercraft/ComputerCraft.java index 27aa35429..2dc474c22 100644 --- a/src/main/java/dan200/computercraft/ComputerCraft.java +++ b/src/main/java/dan200/computercraft/ComputerCraft.java @@ -73,6 +73,8 @@ public final class ComputerCraft implements ModInitializer ) ); public static int httpMaxRequests = 16; public static int httpMaxWebsockets = 4; + public static int httpDownloadBandwidth = 32 * 1024 * 1024; + public static int httpUploadBandwidth = 32 * 1024 * 1024; public static boolean enableCommandBlock = false; public static int modemRange = 64; @@ -82,6 +84,7 @@ public final class ComputerCraft implements ModInitializer public static int maxNotesPerTick = 8; public static MonitorRenderer monitorRenderer = MonitorRenderer.BEST; public static double monitorDistanceSq = 4096; + public static int monitorDistance = 65; public static long monitorBandwidth = 1_000_000; public static boolean turtlesNeedFuel = true; diff --git a/src/main/java/dan200/computercraft/api/client/TransformedModel.java b/src/main/java/dan200/computercraft/api/client/TransformedModel.java index c44d6f04d..201d53333 100644 --- a/src/main/java/dan200/computercraft/api/client/TransformedModel.java +++ b/src/main/java/dan200/computercraft/api/client/TransformedModel.java @@ -6,16 +6,17 @@ package dan200.computercraft.api.client; -import dan200.computercraft.fabric.mixin.AffineTransformationAccess; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.MinecraftClient; import net.minecraft.client.render.model.BakedModel; import net.minecraft.client.render.model.BakedModelManager; import net.minecraft.client.util.ModelIdentifier; +import net.minecraft.util.math.AffineTransformation; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.item.ItemStack; -import net.minecraft.util.math.AffineTransformation; +import net.minecraft.util.math.Vec3f; + import javax.annotation.Nonnull; import java.util.Objects; @@ -72,22 +73,14 @@ public final class TransformedModel { matrixStack.push(); - AffineTransformationAccess access = (AffineTransformationAccess) (Object) matrix; - if( access.getTranslation() != null ) - { - matrixStack.translate( access.getTranslation().getX(), access.getTranslation().getY(), access.getTranslation().getZ() ); - } + Vec3f translation = matrix.getTranslation(); + matrixStack.translate( translation.getX(), translation.getY(), translation.getZ() ); matrixStack.multiply( matrix.getRotation2() ); - if( access.getScale() != null ) - { - matrixStack.scale( access.getScale().getX(), access.getScale().getY(), access.getScale().getZ() ); - } + Vec3f scale = matrix.getScale(); + matrixStack.scale( scale.getX(), scale.getY(), scale.getZ() ); - if( access.getRotation1() != null ) - { - matrixStack.multiply( access.getRotation1() ); - } + matrixStack.multiply( matrix.getRotation1() ); } } diff --git a/src/main/java/dan200/computercraft/api/lua/ILuaContext.java b/src/main/java/dan200/computercraft/api/lua/ILuaContext.java index d871786e4..6d1aba949 100644 --- a/src/main/java/dan200/computercraft/api/lua/ILuaContext.java +++ b/src/main/java/dan200/computercraft/api/lua/ILuaContext.java @@ -9,17 +9,18 @@ package dan200.computercraft.api.lua; import javax.annotation.Nonnull; /** - * An interface passed to peripherals and {@link IDynamicLuaObject}s by computers or turtles, providing methods that allow the peripheral call to interface - * with the computer. + * An interface passed to peripherals and {@link IDynamicLuaObject}s by computers or turtles, providing methods + * that allow the peripheral call to interface with the computer. */ public interface ILuaContext { /** - * Queue a task to be executed on the main server thread at the beginning of next tick, but do not wait for it to complete. This should be used when you - * need to interact with the world in a thread-safe manner but do not care about the result or you wish to run asynchronously. + * Queue a task to be executed on the main server thread at the beginning of next tick, but do not wait for it to + * complete. This should be used when you need to interact with the world in a thread-safe manner but do not care + * about the result or you wish to run asynchronously. * - * When the task has finished, it will enqueue a {@code task_completed} event, which takes the task id, a success value and the return values, or an - * error message if it failed. + * When the task has finished, it will enqueue a {@code task_completed} event, which takes the task id, a success + * value and the return values, or an error message if it failed. * * @param task The task to execute on the main thread. * @return The "id" of the task. This will be the first argument to the {@code task_completed} event. @@ -27,4 +28,18 @@ public interface ILuaContext * @see LuaFunction#mainThread() To run functions on the main thread and return their results synchronously. */ long issueMainThreadTask( @Nonnull ILuaTask task ) throws LuaException; + + /** + * Queue a task to be executed on the main server thread at the beginning of next tick, waiting for it to complete. + * This should be used when you need to interact with the world in a thread-safe manner. + * + * Note that the return values of your task are handled as events, meaning more complex objects such as maps or + * {@link IDynamicLuaObject} will not preserve their identities. + * + * @param task The task to execute on the main thread. + * @return The objects returned by {@code task}. + * @throws LuaException If the task could not be queued, or if the task threw an exception. + */ + @Nonnull + MethodResult executeMainThreadTask( @Nonnull ILuaTask task ) throws LuaException; } diff --git a/src/main/java/dan200/computercraft/api/lua/ILuaObject.java b/src/main/java/dan200/computercraft/api/lua/ILuaObject.java deleted file mode 100644 index eba77bc19..000000000 --- a/src/main/java/dan200/computercraft/api/lua/ILuaObject.java +++ /dev/null @@ -1,18 +0,0 @@ -/* - * This file is part of the public ComputerCraft API - http://www.computercraft.info - * Copyright Daniel Ratcliffe, 2011-2021. This API may be redistributed unmodified and in full only. - * For help using the API, and posting your mods, visit the forums at computercraft.info. - */ -package dan200.computercraft.api.lua; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -public interface ILuaObject -{ - @Nonnull - String[] getMethodNames(); - - @Nullable - Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull Object[] arguments ) throws LuaException, InterruptedException; -} diff --git a/src/main/java/dan200/computercraft/api/pocket/AbstractPocketUpgrade.java b/src/main/java/dan200/computercraft/api/pocket/AbstractPocketUpgrade.java index d17820409..6599d7e90 100644 --- a/src/main/java/dan200/computercraft/api/pocket/AbstractPocketUpgrade.java +++ b/src/main/java/dan200/computercraft/api/pocket/AbstractPocketUpgrade.java @@ -6,12 +6,15 @@ package dan200.computercraft.api.pocket; +import dan200.computercraft.shared.util.NonNullSupplier; +import net.minecraft.item.Item; import net.minecraft.item.ItemConvertible; import net.minecraft.item.ItemStack; import net.minecraft.util.Identifier; import net.minecraft.util.Util; import javax.annotation.Nonnull; +import java.util.function.Supplier; /** * A base class for {@link IPocketUpgrade}s. @@ -22,27 +25,49 @@ public abstract class AbstractPocketUpgrade implements IPocketUpgrade { private final Identifier id; private final String adjective; - private final ItemStack stack; + private final NonNullSupplier stack; - protected AbstractPocketUpgrade( Identifier id, ItemConvertible item ) - { - this( id, Util.createTranslationKey( "upgrade", id ) + ".adjective", item ); - } - - protected AbstractPocketUpgrade( Identifier id, String adjective, ItemConvertible item ) - { - this.id = id; - this.adjective = adjective; - stack = new ItemStack( item ); - } - - protected AbstractPocketUpgrade( Identifier id, String adjective, ItemStack stack ) + protected AbstractPocketUpgrade( Identifier id, String adjective, NonNullSupplier stack ) { this.id = id; this.adjective = adjective; this.stack = stack; } + protected AbstractPocketUpgrade( Identifier id, NonNullSupplier item ) + { + this( id, Util.createTranslationKey( "upgrade", id ) + ".adjective", item ); + } + + protected AbstractPocketUpgrade( Identifier id, String adjective, ItemStack stack ) + { + this( id, adjective, () -> stack ); + } + + protected AbstractPocketUpgrade( Identifier id, ItemStack stack ) + { + this( id, () -> stack ); + } + + protected AbstractPocketUpgrade( Identifier id, String adjective, ItemConvertible item ) + { + this( id, adjective, new CachedStack( () -> item ) ); + } + + protected AbstractPocketUpgrade( Identifier id, ItemConvertible item ) + { + this( id, new CachedStack( () -> item ) ); + } + + protected AbstractPocketUpgrade( Identifier id, String adjective, Supplier item ) + { + this( id, adjective, new CachedStack( item ) ); + } + + protected AbstractPocketUpgrade( Identifier id, Supplier item ) + { + this( id, new CachedStack( item ) ); + } @Nonnull @Override @@ -62,6 +87,32 @@ public abstract class AbstractPocketUpgrade implements IPocketUpgrade @Override public final ItemStack getCraftingItem() { - return stack; + return stack.get(); + } + + /** + * Caches the construction of an item stack. + * + * @see dan200.computercraft.api.turtle.AbstractTurtleUpgrade For explanation of this class. + */ + private static final class CachedStack implements NonNullSupplier + { + private final Supplier provider; + private Item item; + private ItemStack stack; + + CachedStack( Supplier provider ) + { + this.provider = provider; + } + + @Nonnull + @Override + public ItemStack get() + { + Item item = provider.get().asItem(); + if( item == this.item && stack != null ) return stack; + return stack = new ItemStack( this.item = item ); + } } } diff --git a/src/main/java/dan200/computercraft/api/turtle/AbstractTurtleUpgrade.java b/src/main/java/dan200/computercraft/api/turtle/AbstractTurtleUpgrade.java index e53215bbd..7046cc808 100644 --- a/src/main/java/dan200/computercraft/api/turtle/AbstractTurtleUpgrade.java +++ b/src/main/java/dan200/computercraft/api/turtle/AbstractTurtleUpgrade.java @@ -6,12 +6,15 @@ package dan200.computercraft.api.turtle; +import dan200.computercraft.shared.util.NonNullSupplier; +import net.minecraft.item.Item; import net.minecraft.item.ItemConvertible; import net.minecraft.item.ItemStack; import net.minecraft.util.Identifier; import net.minecraft.util.Util; import javax.annotation.Nonnull; +import java.util.function.Supplier; /** * A base class for {@link ITurtleUpgrade}s. @@ -23,14 +26,9 @@ public abstract class AbstractTurtleUpgrade implements ITurtleUpgrade private final Identifier id; private final TurtleUpgradeType type; private final String adjective; - private final ItemStack stack; + private final NonNullSupplier stack; - protected AbstractTurtleUpgrade( Identifier id, TurtleUpgradeType type, String adjective, ItemConvertible item ) - { - this( id, type, adjective, new ItemStack( item ) ); - } - - protected AbstractTurtleUpgrade( Identifier id, TurtleUpgradeType type, String adjective, ItemStack stack ) + protected AbstractTurtleUpgrade( Identifier id, TurtleUpgradeType type, String adjective, NonNullSupplier stack ) { this.id = id; this.type = type; @@ -38,16 +36,40 @@ public abstract class AbstractTurtleUpgrade implements ITurtleUpgrade this.stack = stack; } - protected AbstractTurtleUpgrade( Identifier id, TurtleUpgradeType type, ItemConvertible item ) - { - this( id, type, new ItemStack( item ) ); - } - - protected AbstractTurtleUpgrade( Identifier id, TurtleUpgradeType type, ItemStack stack ) + protected AbstractTurtleUpgrade( Identifier id, TurtleUpgradeType type, NonNullSupplier stack ) { this( id, type, Util.createTranslationKey( "upgrade", id ) + ".adjective", stack ); } + protected AbstractTurtleUpgrade( Identifier id, TurtleUpgradeType type, String adjective, ItemStack stack ) + { + this( id, type, adjective, () -> stack ); + } + + protected AbstractTurtleUpgrade( Identifier id, TurtleUpgradeType type, ItemStack stack ) + { + this( id, type, () -> stack ); + } + + protected AbstractTurtleUpgrade( Identifier id, TurtleUpgradeType type, String adjective, ItemConvertible item ) + { + this( id, type, adjective, new CachedStack( () -> item ) ); + } + + protected AbstractTurtleUpgrade( Identifier id, TurtleUpgradeType type, ItemConvertible item ) + { + this( id, type, new CachedStack( () -> item ) ); + } + + protected AbstractTurtleUpgrade( Identifier id, TurtleUpgradeType type, String adjective, Supplier item ) + { + this( id, type, adjective, new CachedStack( item ) ); + } + + protected AbstractTurtleUpgrade( Identifier id, TurtleUpgradeType type, Supplier item ) + { + this( id, type, new CachedStack( item ) ); + } @Nonnull @Override @@ -74,6 +96,32 @@ public abstract class AbstractTurtleUpgrade implements ITurtleUpgrade @Override public final ItemStack getCraftingItem() { - return stack; + return stack.get(); + } + + /** + * A supplier which converts an item into an item stack. + * + * Constructing item stacks is somewhat expensive due to attaching capabilities. We cache it if given a consistent item. + */ + private static final class CachedStack implements NonNullSupplier + { + private final Supplier provider; + private Item item; + private ItemStack stack; + + CachedStack( Supplier provider ) + { + this.provider = provider; + } + + @Nonnull + @Override + public ItemStack get() + { + Item item = provider.get().asItem(); + if( item == this.item && stack != null ) return stack; + return stack = new ItemStack( this.item = item ); + } } } diff --git a/src/main/java/dan200/computercraft/api/turtle/FakePlayer.java b/src/main/java/dan200/computercraft/api/turtle/FakePlayer.java index a73471f6b..e534e648e 100644 --- a/src/main/java/dan200/computercraft/api/turtle/FakePlayer.java +++ b/src/main/java/dan200/computercraft/api/turtle/FakePlayer.java @@ -23,20 +23,16 @@ import net.minecraft.network.packet.c2s.play.RequestCommandCompletionsC2SPacket; import net.minecraft.network.packet.c2s.play.VehicleMoveC2SPacket; import net.minecraft.recipe.Recipe; import net.minecraft.screen.NamedScreenHandlerFactory; -import net.minecraft.screen.ScreenHandler; import net.minecraft.server.network.ServerPlayNetworkHandler; import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.server.network.ServerPlayerInteractionManager; import net.minecraft.server.world.ServerWorld; import net.minecraft.sound.SoundCategory; import net.minecraft.sound.SoundEvent; import net.minecraft.text.Text; import net.minecraft.util.Hand; -import net.minecraft.util.collection.DefaultedList; import net.minecraft.util.math.ChunkPos; import net.minecraft.util.math.Vec3d; import net.minecraft.village.TradeOfferList; -import net.minecraft.world.GameMode; import javax.annotation.Nullable; import javax.crypto.Cipher; @@ -53,7 +49,7 @@ public class FakePlayer extends ServerPlayerEntity { public FakePlayer( ServerWorld world, GameProfile gameProfile ) { - super( world.getServer(), world, gameProfile, new ServerPlayerInteractionManager( world ) ); + super( world.getServer(), world, gameProfile ); networkHandler = new FakeNetHandler( this ); } @@ -137,30 +133,30 @@ public class FakePlayer extends ServerPlayerEntity { } - @Override - public void onSlotUpdate( ScreenHandler container, int slot, ItemStack stack ) - { - } - - @Override - public void onHandlerRegistered( ScreenHandler container, DefaultedList defaultedList ) - { - } - - @Override - public void onPropertyUpdate( ScreenHandler container, int key, int value ) - { - } + // @Override + // public void onSlotUpdate( ScreenHandler container, int slot, ItemStack stack ) + // { + // } + // + // @Override + // public void onHandlerRegistered( ScreenHandler container, DefaultedList defaultedList ) + // { + // } + // + // @Override + // public void onPropertyUpdate( ScreenHandler container, int key, int value ) + // { + // } @Override public void closeHandledScreen() { } - @Override - public void updateCursorStack() - { - } + // @Override + // public void updateCursorStack() + // { + // } @Override public int unlockRecipes( Collection> recipes ) @@ -196,12 +192,12 @@ public class FakePlayer extends ServerPlayerEntity } @Override - protected void onStatusEffectApplied( StatusEffectInstance statusEffectInstance ) + protected void onStatusEffectApplied( StatusEffectInstance statusEffectInstance, @Nullable Entity source ) { } @Override - protected void onStatusEffectUpgraded( StatusEffectInstance statusEffectInstance, boolean particles ) + protected void onStatusEffectUpgraded( StatusEffectInstance statusEffectInstance, boolean particles, @Nullable Entity source ) { } @@ -215,10 +211,10 @@ public class FakePlayer extends ServerPlayerEntity { } - @Override - public void changeGameMode( GameMode gameMode ) - { - } + // @Override + // public void setGameMode( GameMode gameMode ) + // { + // } @Override public void sendMessage( Text message, MessageType type, UUID senderUuid ) @@ -232,15 +228,15 @@ public class FakePlayer extends ServerPlayerEntity return "[Fake Player]"; } - @Override - public void sendResourcePackUrl( String url, String hash ) - { - } + // @Override + // public void sendResourcePackUrl( String url, String hash ) + // { + // } - @Override - public void onStoppedTracking( Entity entity ) - { - } + // @Override + // public void onStoppedTracking( Entity entity ) + // { + // } @Override public void setCameraEntity( Entity entity ) @@ -347,10 +343,5 @@ public class FakePlayer extends ServerPlayerEntity public void disableAutoRead() { } - - @Override - public void setCompressionThreshold( int size ) - { - } } } diff --git a/src/main/java/dan200/computercraft/client/ClientRegistry.java b/src/main/java/dan200/computercraft/client/ClientRegistry.java index eb66e181a..25ac05541 100644 --- a/src/main/java/dan200/computercraft/client/ClientRegistry.java +++ b/src/main/java/dan200/computercraft/client/ClientRegistry.java @@ -77,7 +77,7 @@ public final class ClientRegistry } @SuppressWarnings( "NewExpressionSideOnly" ) - public static void onModelBakeEvent( ResourceManager manager, Consumer out ) + public static void onModelBakeEvent( ResourceManager manager, Consumer out ) { for( String model : EXTRA_MODELS ) { diff --git a/src/main/java/dan200/computercraft/client/ClientTableFormatter.java b/src/main/java/dan200/computercraft/client/ClientTableFormatter.java index 8a0fdb581..194c23689 100644 --- a/src/main/java/dan200/computercraft/client/ClientTableFormatter.java +++ b/src/main/java/dan200/computercraft/client/ClientTableFormatter.java @@ -21,10 +21,6 @@ import org.apache.commons.lang3.StringUtils; import javax.annotation.Nullable; -@SuppressWarnings( { - "MethodCallSideOnly", - "LocalVariableDeclarationSideOnly" -} ) public class ClientTableFormatter implements TableFormatter { public static final ClientTableFormatter INSTANCE = new ClientTableFormatter(); diff --git a/src/main/java/dan200/computercraft/client/SoundManager.java b/src/main/java/dan200/computercraft/client/SoundManager.java new file mode 100644 index 000000000..d3c67ab5e --- /dev/null +++ b/src/main/java/dan200/computercraft/client/SoundManager.java @@ -0,0 +1,84 @@ +/* + * 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; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.sound.AbstractSoundInstance; +import net.minecraft.client.sound.SoundInstance; +import net.minecraft.client.sound.TickableSoundInstance; +import net.minecraft.sound.SoundCategory; +import net.minecraft.sound.SoundEvent; +import net.minecraft.util.math.Vec3d; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class SoundManager +{ + private static final Map sounds = new HashMap<>(); + + public static void playSound( UUID source, Vec3d position, SoundEvent event, float volume, float pitch ) + { + var soundManager = MinecraftClient.getInstance().getSoundManager(); + + MoveableSound oldSound = sounds.get( source ); + if( oldSound != null ) soundManager.stop( oldSound ); + + MoveableSound newSound = new MoveableSound( event, position, volume, pitch ); + sounds.put( source, newSound ); + soundManager.play( newSound ); + } + + public static void stopSound( UUID source ) + { + SoundInstance sound = sounds.remove( source ); + if( sound == null ) return; + + MinecraftClient.getInstance().getSoundManager().stop( sound ); + } + + public static void moveSound( UUID source, Vec3d position ) + { + MoveableSound sound = sounds.get( source ); + if( sound != null ) sound.setPosition( position ); + } + + public static void reset() + { + sounds.clear(); + } + + private static class MoveableSound extends AbstractSoundInstance implements TickableSoundInstance + { + protected MoveableSound( SoundEvent sound, Vec3d position, float volume, float pitch ) + { + super( sound, SoundCategory.RECORDS ); + setPosition( position ); + this.volume = volume; + this.pitch = pitch; + attenuationType = SoundInstance.AttenuationType.LINEAR; + } + + void setPosition( Vec3d position ) + { + x = (float) position.getX(); + y = (float) position.getY(); + z = (float) position.getZ(); + } + + @Override + public boolean isDone() + { + return false; + } + + @Override + public void tick() + { + } + } +} diff --git a/src/main/java/dan200/computercraft/client/gui/ComputerScreenBase.java b/src/main/java/dan200/computercraft/client/gui/ComputerScreenBase.java new file mode 100644 index 000000000..1373bbc61 --- /dev/null +++ b/src/main/java/dan200/computercraft/client/gui/ComputerScreenBase.java @@ -0,0 +1,230 @@ +/* + * 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.gui; + +import dan200.computercraft.ComputerCraft; +import dan200.computercraft.client.gui.widgets.ComputerSidebar; +import dan200.computercraft.client.gui.widgets.WidgetTerminal; +import dan200.computercraft.shared.computer.core.ClientComputer; +import dan200.computercraft.shared.computer.core.ComputerFamily; +import dan200.computercraft.shared.computer.inventory.ContainerComputerBase; +import dan200.computercraft.shared.computer.upload.FileUpload; +import dan200.computercraft.shared.computer.upload.UploadResult; +import dan200.computercraft.shared.network.NetworkHandler; +import dan200.computercraft.shared.network.server.ContinueUploadMessage; +import dan200.computercraft.shared.network.server.UploadFileMessage; +import net.minecraft.client.gui.screen.ingame.HandledScreen; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.text.Text; +import net.minecraft.text.TranslatableText; +import org.lwjgl.glfw.GLFW; + +import javax.annotation.Nonnull; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.channels.SeekableByteChannel; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public abstract class ComputerScreenBase extends HandledScreen +{ + + private static final Text OK = new TranslatableText( "gui.ok" ); + private static final Text CANCEL = new TranslatableText( "gui.cancel" ); + private static final Text OVERWRITE = new TranslatableText( "gui.computercraft.upload.overwrite_button" ); + + protected WidgetTerminal terminal; + protected final ClientComputer computer; + protected final ComputerFamily family; + + protected final int sidebarYOffset; + + public ComputerScreenBase( T container, PlayerInventory player, Text title, int sidebarYOffset ) + { + super( container, player, title ); + computer = (ClientComputer) container.getComputer(); + family = container.getFamily(); + this.sidebarYOffset = sidebarYOffset; + } + + protected abstract WidgetTerminal createTerminal(); + + @Override + protected final void init() + { + super.init(); + client.keyboard.setRepeatEvents( true ); + + terminal = addDrawableChild( createTerminal() ); + ComputerSidebar.addButtons( this, computer, this::addDrawableChild, x, y + sidebarYOffset ); + setFocused( terminal ); + } + + @Override + public final void removed() + { + super.removed(); + client.keyboard.setRepeatEvents( false ); + } + + @Override + public final void handledScreenTick() + { + super.handledScreenTick(); + terminal.update(); + } + + @Override + public final boolean keyPressed( int key, int scancode, int modifiers ) + { + // Forward the tab key to the terminal, rather than moving between controls. + if( key == GLFW.GLFW_KEY_TAB && getFocused() != null && getFocused() == terminal ) + { + return getFocused().keyPressed( key, scancode, modifiers ); + } + + return super.keyPressed( key, scancode, modifiers ); + } + + @Override + public final void render( @Nonnull MatrixStack stack, int mouseX, int mouseY, float partialTicks ) + { + renderBackground( stack ); + super.render( stack, mouseX, mouseY, partialTicks ); + drawMouseoverTooltip( stack, mouseX, mouseY ); + } + + @Override + public final boolean mouseDragged( double x, double y, int button, double deltaX, double deltaY ) + { + return getFocused() != null && getFocused().mouseDragged( x, y, button, deltaX, deltaY ) || super.mouseDragged( x, y, button, deltaX, deltaY ); + } + + @Override + protected void drawForeground( @Nonnull MatrixStack transform, int mouseX, int mouseY ) + { + // Skip rendering labels. + } + + @Override + public void filesDragged( @Nonnull List files ) + { + // TODO: this thing doesn't work in Tweaked at this moment + if ( true ) return; + if( files.isEmpty() ) return; + + if( computer == null || !computer.isOn() ) + { + alert( UploadResult.FAILED_TITLE, UploadResult.COMPUTER_OFF_MSG ); + return; + } + + long size = 0; + + List toUpload = new ArrayList<>(); + for( Path file : files ) + { + // TODO: Recurse directories? If so, we probably want to shunt this off-thread. + if( !Files.isRegularFile( file ) ) continue; + + try( SeekableByteChannel sbc = Files.newByteChannel( file ) ) + { + long fileSize = sbc.size(); + if( fileSize > UploadFileMessage.MAX_SIZE || (size += fileSize) >= UploadFileMessage.MAX_SIZE ) + { + alert( UploadResult.FAILED_TITLE, UploadResult.TOO_MUCH_MSG ); + return; + } + + String name = file.getFileName().toString(); + if( name.length() > UploadFileMessage.MAX_FILE_NAME ) + { + alert( UploadResult.FAILED_TITLE, new TranslatableText( "gui.computercraft.upload.failed.name_too_long" ) ); + return; + } + + ByteBuffer buffer = ByteBuffer.allocateDirect( (int) fileSize ); + sbc.read( buffer ); + buffer.flip(); + + byte[] digest = FileUpload.getDigest( buffer ); + if( digest == null ) + { + alert( UploadResult.FAILED_TITLE, new TranslatableText( "gui.computercraft.upload.failed.corrupted" ) ); + return; + } + + buffer.rewind(); + toUpload.add( new FileUpload( name, buffer, digest ) ); + } + catch( IOException e ) + { + ComputerCraft.log.error( "Failed uploading files", e ); + alert( UploadResult.FAILED_TITLE, new TranslatableText( "gui.computercraft.upload.failed.generic", "Cannot compute checksum" ) ); + } + } + + if( toUpload.size() > UploadFileMessage.MAX_FILES ) + { + alert( UploadResult.FAILED_TITLE, new TranslatableText( "gui.computercraft.upload.failed.too_many_files" ) ); + return; + } + + if( toUpload.size() > 0 ) + { + UploadFileMessage.send( computer.getInstanceID(), toUpload ); + } + } + + public void uploadResult( UploadResult result, Text message ) + { + switch( result ) + { + case SUCCESS: + alert( UploadResult.SUCCESS_TITLE, message ); + break; + case ERROR: + alert( UploadResult.FAILED_TITLE, message ); + break; + case CONFIRM_OVERWRITE: + OptionScreen.show( + client, UploadResult.UPLOAD_OVERWRITE, message, + Arrays.asList( + OptionScreen.newButton( CANCEL, b -> cancelUpload() ), + OptionScreen.newButton( OVERWRITE, b -> continueUpload() ) + ), + this::cancelUpload + ); + break; + } + } + + private void continueUpload() + { + if( client.currentScreen instanceof OptionScreen ) ((OptionScreen) client.currentScreen).disable(); + NetworkHandler.sendToServer( new ContinueUploadMessage( computer.getInstanceID(), true ) ); + } + + private void cancelUpload() + { + client.setScreen( this ); + NetworkHandler.sendToServer( new ContinueUploadMessage( computer.getInstanceID(), false ) ); + } + + private void alert( Text title, Text message ) + { + OptionScreen.show( client, title, message, + Collections.singletonList( OptionScreen.newButton( OK, b -> client.setScreen( this ) ) ), + () -> client.setScreen( this ) + ); + } + +} diff --git a/src/main/java/dan200/computercraft/client/gui/FixedWidthFontRenderer.java b/src/main/java/dan200/computercraft/client/gui/FixedWidthFontRenderer.java index 0e42a3ead..b0456bd69 100644 --- a/src/main/java/dan200/computercraft/client/gui/FixedWidthFontRenderer.java +++ b/src/main/java/dan200/computercraft/client/gui/FixedWidthFontRenderer.java @@ -6,22 +6,24 @@ package dan200.computercraft.client.gui; -import com.mojang.blaze3d.systems.RenderSystem; import dan200.computercraft.client.FrameInfo; +import dan200.computercraft.client.render.RenderTypes; import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.core.terminal.TextBuffer; import dan200.computercraft.shared.util.Colour; import dan200.computercraft.shared.util.Palette; import net.minecraft.client.MinecraftClient; -import net.minecraft.client.render.*; +import net.minecraft.client.render.VertexConsumer; +import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.util.Identifier; import net.minecraft.util.math.AffineTransformation; import net.minecraft.util.math.Matrix4f; -import org.lwjgl.opengl.GL11; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import static dan200.computercraft.client.render.RenderTypes.FULL_BRIGHT_LIGHTMAP; + public final class FixedWidthFontRenderer { public static final int FONT_HEIGHT = 9; @@ -31,47 +33,16 @@ public final class FixedWidthFontRenderer public static final float BACKGROUND_END = (WIDTH - 4.0f) / WIDTH; private static final Matrix4f IDENTITY = AffineTransformation.identity() .getMatrix(); - private static final Identifier FONT = new Identifier( "computercraft", "textures/gui/term_font.png" ); - public static final RenderLayer TYPE = Type.MAIN; + public static final Identifier FONT = new Identifier( "computercraft", "textures/gui/term_font.png" ); private FixedWidthFontRenderer() { } - public static void drawString( float x, float y, @Nonnull TextBuffer text, @Nonnull TextBuffer textColour, @Nullable TextBuffer backgroundColour, - @Nonnull Palette palette, boolean greyscale, float leftMarginSize, float rightMarginSize ) - { - bindFont(); - - VertexConsumerProvider.Immediate renderer = MinecraftClient.getInstance() - .getBufferBuilders() - .getEntityVertexConsumers(); - drawString( IDENTITY, - ((VertexConsumerProvider) renderer).getBuffer( TYPE ), - x, - y, - text, - textColour, - backgroundColour, - palette, - greyscale, - leftMarginSize, - rightMarginSize ); - renderer.draw(); - } - - private static void bindFont() - { - MinecraftClient.getInstance() - .getTextureManager() - .bindTexture( FONT ); - RenderSystem.texParameter( GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, GL11.GL_CLAMP ); - } - public static void drawString( @Nonnull Matrix4f transform, @Nonnull VertexConsumer renderer, float x, float y, @Nonnull TextBuffer text, @Nonnull TextBuffer textColour, @Nullable TextBuffer backgroundColour, @Nonnull Palette palette, boolean greyscale, - float leftMarginSize, float rightMarginSize ) + float leftMarginSize, float rightMarginSize, int light ) { if( backgroundColour != null ) { @@ -99,7 +70,7 @@ public final class FixedWidthFontRenderer { index = '?'; } - drawChar( transform, renderer, x + i * FONT_WIDTH, y, index, r, g, b ); + drawChar( transform, renderer, x + i * FONT_WIDTH, y, index, r, g, b, light ); } } @@ -170,7 +141,7 @@ public final class FixedWidthFontRenderer return (float) ((rgb[0] + rgb[1] + rgb[2]) / 3); } - private static void drawChar( Matrix4f transform, VertexConsumer buffer, float x, float y, int index, float r, float g, float b ) + private static void drawChar( Matrix4f transform, VertexConsumer buffer, float x, float y, int index, float r, float g, float b, int light ) { // Short circuit to avoid the common case - the texture should be blank here after all. if( index == '\0' || index == ' ' ) @@ -187,26 +158,32 @@ public final class FixedWidthFontRenderer buffer.vertex( transform, x, y, 0f ) .color( r, g, b, 1.0f ) .texture( xStart / WIDTH, yStart / WIDTH ) + .light( light ) .next(); buffer.vertex( transform, x, y + FONT_HEIGHT, 0f ) .color( r, g, b, 1.0f ) .texture( xStart / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ) + .light( light ) .next(); buffer.vertex( transform, x + FONT_WIDTH, y, 0f ) .color( r, g, b, 1.0f ) .texture( (xStart + FONT_WIDTH) / WIDTH, yStart / WIDTH ) + .light( light ) .next(); buffer.vertex( transform, x + FONT_WIDTH, y, 0f ) .color( r, g, b, 1.0f ) .texture( (xStart + FONT_WIDTH) / WIDTH, yStart / WIDTH ) + .light( light ) .next(); buffer.vertex( transform, x, y + FONT_HEIGHT, 0f ) .color( r, g, b, 1.0f ) .texture( xStart / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ) + .light( light ) .next(); buffer.vertex( transform, x + FONT_WIDTH, y + FONT_HEIGHT, 0f ) .color( r, g, b, 1.0f ) .texture( (xStart + FONT_WIDTH) / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ) + .light( light ) .next(); } @@ -300,7 +277,7 @@ public final class FixedWidthFontRenderer palette, greyscale, leftMarginSize, - rightMarginSize ); + rightMarginSize, FULL_BRIGHT_LIGHTMAP ); } } @@ -328,7 +305,7 @@ public final class FixedWidthFontRenderer b = (float) colour[2]; } - drawChar( transform, buffer, x + cursorX * FONT_WIDTH, y + cursorY * FONT_HEIGHT, '_', r, g, b ); + drawChar( transform, buffer, x + cursorX * FONT_WIDTH, y + cursorY * FONT_HEIGHT, '_', r, g, b, FULL_BRIGHT_LIGHTMAP ); } } @@ -342,14 +319,12 @@ public final class FixedWidthFontRenderer public static void drawTerminal( @Nonnull Matrix4f transform, float x, float y, @Nonnull Terminal terminal, boolean greyscale, float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize ) { - bindFont(); - VertexConsumerProvider.Immediate renderer = MinecraftClient.getInstance() .getBufferBuilders() .getEntityVertexConsumers(); - VertexConsumer buffer = renderer.getBuffer( TYPE ); + VertexConsumer buffer = renderer.getBuffer( RenderTypes.TERMINAL_WITH_DEPTH ); drawTerminal( transform, buffer, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize ); - renderer.draw( TYPE ); + renderer.draw(); } public static void drawTerminal( float x, float y, @Nonnull Terminal terminal, boolean greyscale, float topMarginSize, float bottomMarginSize, @@ -360,13 +335,12 @@ public final class FixedWidthFontRenderer public static void drawEmptyTerminal( float x, float y, float width, float height ) { + Colour colour = Colour.BLACK; drawEmptyTerminal( IDENTITY, x, y, width, height ); } public static void drawEmptyTerminal( @Nonnull Matrix4f transform, float x, float y, float width, float height ) { - bindFont(); - VertexConsumerProvider.Immediate renderer = MinecraftClient.getInstance() .getBufferBuilders() .getEntityVertexConsumers(); @@ -378,44 +352,12 @@ public final class FixedWidthFontRenderer float height ) { Colour colour = Colour.BLACK; - drawQuad( transform, renderer.getBuffer( TYPE ), x, y, width, height, colour.getR(), colour.getG(), colour.getB() ); + drawQuad( transform, renderer.getBuffer( RenderTypes.TERMINAL_WITH_DEPTH ), x, y, width, height, colour.getR(), colour.getG(), colour.getB() ); } public static void drawBlocker( @Nonnull Matrix4f transform, @Nonnull VertexConsumerProvider renderer, float x, float y, float width, float height ) { Colour colour = Colour.BLACK; - drawQuad( transform, renderer.getBuffer( Type.BLOCKER ), x, y, width, height, colour.getR(), colour.getG(), colour.getB() ); - } - - private static final class Type extends RenderPhase - { - private static final int GL_MODE = GL11.GL_TRIANGLES; - - private static final VertexFormat FORMAT = VertexFormats.POSITION_COLOR_TEXTURE; - - static final RenderLayer MAIN = RenderLayer.of( "terminal_font", FORMAT, GL_MODE, 1024, false, false, // useDelegate, needsSorting - RenderLayer.MultiPhaseParameters.builder() - .texture( new RenderPhase.Texture( FONT, - false, - false ) ) // blur, minimap - .alpha( ONE_TENTH_ALPHA ) - .lightmap( DISABLE_LIGHTMAP ) - .writeMaskState( COLOR_MASK ) - .build( false ) ); - - static final RenderLayer BLOCKER = RenderLayer.of( "terminal_blocker", FORMAT, GL_MODE, 256, false, false, // useDelegate, needsSorting - RenderLayer.MultiPhaseParameters.builder() - .texture( new RenderPhase.Texture( FONT, - false, - false ) ) // blur, minimap - .alpha( ONE_TENTH_ALPHA ) - .writeMaskState( ALL_MASK ) - .lightmap( DISABLE_LIGHTMAP ) - .build( false ) ); - - private Type( String name, Runnable setup, Runnable destroy ) - { - super( name, setup, destroy ); - } + drawQuad( transform, renderer.getBuffer( RenderTypes.TERMINAL_BLOCKER ), x, y, width, height, colour.getR(), colour.getG(), colour.getB() ); } } diff --git a/src/main/java/dan200/computercraft/client/gui/GuiComputer.java b/src/main/java/dan200/computercraft/client/gui/GuiComputer.java index a4f40a783..5160b806d 100644 --- a/src/main/java/dan200/computercraft/client/gui/GuiComputer.java +++ b/src/main/java/dan200/computercraft/client/gui/GuiComputer.java @@ -6,54 +6,45 @@ package dan200.computercraft.client.gui; -import com.mojang.blaze3d.systems.RenderSystem; import dan200.computercraft.ComputerCraft; +import dan200.computercraft.client.gui.widgets.ComputerSidebar; import dan200.computercraft.client.gui.widgets.WidgetTerminal; -import dan200.computercraft.client.gui.widgets.WidgetWrapper; import dan200.computercraft.client.render.ComputerBorderRenderer; -import dan200.computercraft.shared.computer.core.ClientComputer; -import dan200.computercraft.shared.computer.core.ComputerFamily; -import dan200.computercraft.shared.computer.inventory.ContainerComputer; +import dan200.computercraft.client.render.RenderTypes; +//import dan200.computercraft.shared.computer.inventory.ContainerComputer; import dan200.computercraft.shared.computer.inventory.ContainerComputerBase; import dan200.computercraft.shared.computer.inventory.ContainerViewComputer; -import dan200.computercraft.shared.pocket.inventory.ContainerPocketComputer; -import net.minecraft.client.gui.screen.ingame.HandledScreen; +//import dan200.computercraft.shared.pocket.inventory.ContainerPocketComputer; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.text.Text; -import org.lwjgl.glfw.GLFW; import javax.annotation.Nonnull; import static dan200.computercraft.client.render.ComputerBorderRenderer.BORDER; -import static dan200.computercraft.client.render.ComputerBorderRenderer.MARGIN; +import static dan200.computercraft.client.render.ComputerBorderRenderer.getTexture; -public class GuiComputer extends HandledScreen +public final class GuiComputer extends ComputerScreenBase { - protected final ComputerFamily family; - protected final ClientComputer computer; private final int termWidth; private final int termHeight; - protected WidgetTerminal terminal; - protected WidgetWrapper terminalWrapper; - - protected GuiComputer( T container, PlayerInventory player, Text title, int termWidth, int termHeight ) + private GuiComputer( T container, PlayerInventory player, Text title, int termWidth, int termHeight ) { - super( container, player, title ); - this.family = container.getFamily(); - this.computer = (ClientComputer) container.getComputer(); + super( container, player, title, BORDER ); this.termWidth = termWidth; this.termHeight = termHeight; - this.terminal = null; + + backgroundWidth = WidgetTerminal.getWidth( termWidth ) + BORDER * 2 + ComputerSidebar.WIDTH; + backgroundHeight = WidgetTerminal.getHeight( termHeight ) + BORDER * 2; } - public static GuiComputer create( ContainerComputer container, PlayerInventory inventory, Text component ) + public static GuiComputer create( ContainerComputerBase container, PlayerInventory inventory, Text component ) { return new GuiComputer<>( container, inventory, component, ComputerCraft.computerTermWidth, ComputerCraft.computerTermHeight ); } - public static GuiComputer createPocket( ContainerPocketComputer container, PlayerInventory inventory, Text component ) + public static GuiComputer createPocket( ContainerComputerBase container, PlayerInventory inventory, Text component ) { return new GuiComputer<>( container, inventory, component, ComputerCraft.pocketTermWidth, ComputerCraft.pocketTermHeight ); } @@ -63,96 +54,19 @@ public class GuiComputer extends HandledScreen< return new GuiComputer<>( container, inventory, component, container.getWidth(), container.getHeight() ); } - protected void initTerminal( int border, int widthExtra, int heightExtra ) - { - client.keyboard.setRepeatEvents( true ); - - int termPxWidth = termWidth * FixedWidthFontRenderer.FONT_WIDTH; - int termPxHeight = termHeight * FixedWidthFontRenderer.FONT_HEIGHT; - - backgroundWidth = termPxWidth + MARGIN * 2 + border * 2 + widthExtra; - backgroundHeight = termPxHeight + MARGIN * 2 + border * 2 + heightExtra; - - super.init(); - - terminal = new WidgetTerminal( client, () -> computer, termWidth, termHeight, MARGIN, MARGIN, MARGIN, MARGIN ); - terminalWrapper = new WidgetWrapper( terminal, MARGIN + border + x, MARGIN + border + y, termPxWidth, termPxHeight ); - - children.add( terminalWrapper ); - setFocused( terminalWrapper ); - } - @Override - protected void init() + protected WidgetTerminal createTerminal() { - initTerminal( BORDER, 0, 0 ); + return new WidgetTerminal( computer, + x + ComputerSidebar.WIDTH + BORDER, y + BORDER, termWidth, termHeight + ); } - - @Override - public void render( @Nonnull MatrixStack stack, int mouseX, int mouseY, float partialTicks ) - { - this.renderBackground( stack ); - super.render( stack, mouseX, mouseY, partialTicks ); - drawMouseoverTooltip( stack, mouseX, mouseY ); - } - - @Override - protected void drawForeground( @Nonnull MatrixStack transform, int mouseX, int mouseY ) - { - // Skip rendering labels. - } - @Override public void drawBackground( @Nonnull MatrixStack stack, float partialTicks, int mouseX, int mouseY ) { - // Draw terminal - terminal.draw( terminalWrapper.getX(), terminalWrapper.getY() ); - - // Draw a border around the terminal - RenderSystem.color4f( 1, 1, 1, 1 ); - client.getTextureManager() - .bindTexture( ComputerBorderRenderer.getTexture( family ) ); - ComputerBorderRenderer.render( terminalWrapper.getX() - MARGIN, terminalWrapper.getY() - MARGIN, - getZOffset(), terminalWrapper.getWidth() + MARGIN * 2, terminalWrapper.getHeight() + MARGIN * 2 ); - } - - @Override - public boolean mouseDragged( double x, double y, int button, double deltaX, double deltaY ) - { - return (getFocused() != null && getFocused().mouseDragged( x, y, button, deltaX, deltaY )) || super.mouseDragged( x, y, button, deltaX, deltaY ); - } - - @Override - public boolean mouseReleased( double mouseX, double mouseY, int button ) - { - return (getFocused() != null && getFocused().mouseReleased( mouseX, mouseY, button )) || super.mouseReleased( x, y, button ); - } - - @Override - public boolean keyPressed( int key, int scancode, int modifiers ) - { - // Forward the tab key to the terminal, rather than moving between controls. - if( key == GLFW.GLFW_KEY_TAB && getFocused() != null && getFocused() == terminalWrapper ) - { - return getFocused().keyPressed( key, scancode, modifiers ); - } - - return super.keyPressed( key, scancode, modifiers ); - } - - @Override - public void removed() - { - super.removed(); - children.remove( terminal ); - terminal = null; - client.keyboard.setRepeatEvents( false ); - } - - @Override - public void tick() - { - super.tick(); - terminal.update(); + ComputerBorderRenderer.render( + getTexture( family ), terminal.x, terminal.y, getZOffset(), + RenderTypes.FULL_BRIGHT_LIGHTMAP, terminal.getWidth(), terminal.getHeight() ); + ComputerSidebar.renderBackground( stack, x, y + sidebarYOffset ); } } diff --git a/src/main/java/dan200/computercraft/client/gui/GuiDiskDrive.java b/src/main/java/dan200/computercraft/client/gui/GuiDiskDrive.java index 773ac2c00..0cfbc3daa 100644 --- a/src/main/java/dan200/computercraft/client/gui/GuiDiskDrive.java +++ b/src/main/java/dan200/computercraft/client/gui/GuiDiskDrive.java @@ -9,6 +9,7 @@ package dan200.computercraft.client.gui; import com.mojang.blaze3d.systems.RenderSystem; import dan200.computercraft.shared.peripheral.diskdrive.ContainerDiskDrive; import net.minecraft.client.gui.screen.ingame.HandledScreen; +import net.minecraft.client.render.GameRenderer; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.text.Text; @@ -36,9 +37,9 @@ public class GuiDiskDrive extends HandledScreen @Override protected void drawBackground( @Nonnull MatrixStack transform, float partialTicks, int mouseX, int mouseY ) { - RenderSystem.color4f( 1.0F, 1.0F, 1.0F, 1.0F ); - client.getTextureManager() - .bindTexture( BACKGROUND ); + RenderSystem.setShader( GameRenderer::getPositionTexShader ); + RenderSystem.setShaderColor( 1.0F, 1.0F, 1.0F, 1.0F ); + RenderSystem.setShaderTexture( 0, BACKGROUND ); drawTexture( transform, x, y, 0, 0, backgroundWidth, backgroundHeight ); } } diff --git a/src/main/java/dan200/computercraft/client/gui/GuiPrinter.java b/src/main/java/dan200/computercraft/client/gui/GuiPrinter.java index f0e29a49b..917a6861a 100644 --- a/src/main/java/dan200/computercraft/client/gui/GuiPrinter.java +++ b/src/main/java/dan200/computercraft/client/gui/GuiPrinter.java @@ -25,14 +25,6 @@ public class GuiPrinter extends HandledScreen super( container, player, title ); } - /*@Override - protected void drawGuiContainerForegroundLayer( int mouseX, int mouseY ) - { - String title = getTitle().getFormattedText(); - font.drawString( title, (xSize - font.getStringWidth( title )) / 2.0f, 6, 0x404040 ); - font.drawString( I18n.format( "container.inventory" ), 8, ySize - 96 + 2, 0x404040 ); - }*/ - @Override public void render( @Nonnull MatrixStack stack, int mouseX, int mouseY, float partialTicks ) { @@ -44,9 +36,8 @@ public class GuiPrinter extends HandledScreen @Override protected void drawBackground( @Nonnull MatrixStack transform, float partialTicks, int mouseX, int mouseY ) { - RenderSystem.color4f( 1.0F, 1.0F, 1.0F, 1.0F ); - client.getTextureManager() - .bindTexture( BACKGROUND ); + RenderSystem.setShaderColor( 1.0F, 1.0F, 1.0F, 1.0F ); + RenderSystem.setShaderTexture( 0, BACKGROUND ); drawTexture( transform, x, y, 0, 0, backgroundWidth, backgroundHeight ); if( getScreenHandler().isPrinting() ) diff --git a/src/main/java/dan200/computercraft/client/gui/GuiPrintout.java b/src/main/java/dan200/computercraft/client/gui/GuiPrintout.java index 261948e19..2163ef355 100644 --- a/src/main/java/dan200/computercraft/client/gui/GuiPrintout.java +++ b/src/main/java/dan200/computercraft/client/gui/GuiPrintout.java @@ -22,6 +22,7 @@ import org.lwjgl.glfw.GLFW; import javax.annotation.Nonnull; import static dan200.computercraft.client.render.PrintoutRenderer.*; +import static dan200.computercraft.client.render.RenderTypes.FULL_BRIGHT_LIGHTMAP; public class GuiPrintout extends HandledScreen { @@ -108,7 +109,7 @@ public class GuiPrintout extends HandledScreen protected void drawBackground( @Nonnull MatrixStack transform, float partialTicks, int mouseX, int mouseY ) { // Draw the printout - RenderSystem.color4f( 1.0f, 1.0f, 1.0f, 1.0f ); + RenderSystem.setShaderColor( 1.0f, 1.0f, 1.0f, 1.0f ); RenderSystem.enableDepthTest(); VertexConsumerProvider.Immediate renderer = MinecraftClient.getInstance() @@ -116,8 +117,8 @@ public class GuiPrintout extends HandledScreen .getEntityVertexConsumers(); Matrix4f matrix = transform.peek() .getModel(); - drawBorder( matrix, renderer, x, y, getZOffset(), page, pages, book ); - drawText( matrix, renderer, x + X_TEXT_MARGIN, y + Y_TEXT_MARGIN, ItemPrintout.LINES_PER_PAGE * page, text, colours ); + drawBorder( matrix, renderer, x, y, getZOffset(), page, pages, book, FULL_BRIGHT_LIGHTMAP ); + drawText( matrix, renderer, x + X_TEXT_MARGIN, y + Y_TEXT_MARGIN, ItemPrintout.LINES_PER_PAGE * page, FULL_BRIGHT_LIGHTMAP, text, colours ); renderer.draw(); } diff --git a/src/main/java/dan200/computercraft/client/gui/GuiTurtle.java b/src/main/java/dan200/computercraft/client/gui/GuiTurtle.java index af78829ad..56dd0b1be 100644 --- a/src/main/java/dan200/computercraft/client/gui/GuiTurtle.java +++ b/src/main/java/dan200/computercraft/client/gui/GuiTurtle.java @@ -8,6 +8,9 @@ package dan200.computercraft.client.gui; import com.mojang.blaze3d.systems.RenderSystem; import dan200.computercraft.ComputerCraft; +import dan200.computercraft.client.gui.widgets.ComputerSidebar; +import dan200.computercraft.client.gui.widgets.WidgetTerminal; +import dan200.computercraft.client.render.ComputerBorderRenderer; import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.turtle.inventory.ContainerTurtle; import net.minecraft.client.util.math.MatrixStack; @@ -17,40 +20,46 @@ import net.minecraft.util.Identifier; import javax.annotation.Nonnull; -public class GuiTurtle extends GuiComputer +import static dan200.computercraft.shared.turtle.inventory.ContainerTurtle.BORDER; + +public class GuiTurtle extends ComputerScreenBase { private static final Identifier BACKGROUND_NORMAL = new Identifier( "computercraft", "textures/gui/turtle_normal.png" ); private static final Identifier BACKGROUND_ADVANCED = new Identifier( "computercraft", "textures/gui/turtle_advanced.png" ); - private final ContainerTurtle container; + + private static final int TEX_WIDTH = 254; + private static final int TEX_HEIGHT = 217; + + private final ComputerFamily family; public GuiTurtle( ContainerTurtle container, PlayerInventory player, Text title ) { - super( container, player, title, ComputerCraft.turtleTermWidth, ComputerCraft.turtleTermHeight ); + super( container, player, title, BORDER ); + + family = container.getFamily(); + backgroundWidth = TEX_WIDTH + ComputerSidebar.WIDTH; + backgroundHeight = TEX_HEIGHT; - this.container = container; } @Override - protected void init() + protected WidgetTerminal createTerminal() { - initTerminal( 8, 0, 80 ); + return new WidgetTerminal( + computer, x + BORDER + ComputerSidebar.WIDTH, y + BORDER, + ComputerCraft.turtleTermWidth, ComputerCraft.turtleTermHeight + ); } @Override public void drawBackground( @Nonnull MatrixStack transform, float partialTicks, int mouseX, int mouseY ) { - // Draw term - Identifier texture = family == ComputerFamily.ADVANCED ? BACKGROUND_ADVANCED : BACKGROUND_NORMAL; - terminal.draw( terminalWrapper.getX(), terminalWrapper.getY() ); - - // Draw border/inventory - RenderSystem.color4f( 1.0F, 1.0F, 1.0F, 1.0F ); - client.getTextureManager() - .bindTexture( texture ); - drawTexture( transform, x, y, 0, 0, backgroundWidth, backgroundHeight ); + boolean advanced = family == ComputerFamily.ADVANCED; + RenderSystem.setShaderTexture( 0, advanced ? BACKGROUND_ADVANCED : BACKGROUND_NORMAL ); + drawTexture( transform, x + ComputerSidebar.WIDTH, y, 0, 0, TEX_WIDTH, TEX_HEIGHT ); // Draw selection slot - int slot = container.getSelectedSlot(); + int slot = getScreenHandler().getSelectedSlot(); if( slot >= 0 ) { int slotX = slot % 4; @@ -61,5 +70,8 @@ public class GuiTurtle extends GuiComputer 24, 24 ); } + + RenderSystem.setShaderTexture( 0, advanced ? ComputerBorderRenderer.BACKGROUND_ADVANCED : ComputerBorderRenderer.BACKGROUND_NORMAL ); + ComputerSidebar.renderBackground( transform, x, y + sidebarYOffset ); } } diff --git a/src/main/java/dan200/computercraft/client/gui/NoTermComputerScreen.java b/src/main/java/dan200/computercraft/client/gui/NoTermComputerScreen.java new file mode 100644 index 000000000..9c03f5f6b --- /dev/null +++ b/src/main/java/dan200/computercraft/client/gui/NoTermComputerScreen.java @@ -0,0 +1,118 @@ +/* + * 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.gui; + +import dan200.computercraft.ComputerCraft; +import dan200.computercraft.client.gui.widgets.WidgetTerminal; +import dan200.computercraft.shared.computer.core.ClientComputer; +import dan200.computercraft.shared.computer.inventory.ContainerComputerBase; +import net.minecraft.client.font.TextRenderer; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.screen.ingame.ScreenHandlerProvider; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.text.OrderedText; +import net.minecraft.text.Text; +import net.minecraft.text.TranslatableText; +import org.lwjgl.glfw.GLFW; + +import javax.annotation.Nonnull; +import java.util.List; + +public class NoTermComputerScreen extends Screen implements ScreenHandlerProvider +{ + private final T menu; + private WidgetTerminal terminal; + + public NoTermComputerScreen( T menu, PlayerInventory player, Text title ) + { + super( title ); + this.menu = menu; + } + + @Nonnull + @Override + public T getScreenHandler() + { + return menu; + } + + @Override + protected void init() + { + this.passEvents = true; + client.mouse.lockCursor(); + client.currentScreen = this; + super.init(); + client.keyboard.setRepeatEvents( true ); + + terminal = addDrawableChild( new WidgetTerminal( (ClientComputer) menu.getComputer(), 0, 0, ComputerCraft.pocketTermWidth, ComputerCraft.pocketTermHeight ) ); + terminal.visible = false; + terminal.active = false; + setFocused( terminal ); + } + + @Override + public final void removed() + { + super.removed(); + client.keyboard.setRepeatEvents( false ); + } + + @Override + public final void tick() + { + super.tick(); + terminal.update(); + } + + @Override + public boolean mouseScrolled( double pMouseX, double pMouseY, double pDelta ) + { + client.player.getInventory().scrollInHotbar( pDelta ); + return super.mouseScrolled( pMouseX, pMouseY, pDelta ); + } + + @Override + public void onClose() + { + client.player.closeHandledScreen(); + super.onClose(); + } + + @Override + public boolean isPauseScreen() + { + return false; + } + + @Override + public final boolean keyPressed( int key, int scancode, int modifiers ) + { + // Forward the tab key to the terminal, rather than moving between controls. + if( key == GLFW.GLFW_KEY_TAB && getFocused() != null && getFocused() == terminal ) + { + return getFocused().keyPressed( key, scancode, modifiers ); + } + + return super.keyPressed( key, scancode, modifiers ); + } + + @Override + public void render( MatrixStack transform, int mouseX, int mouseY, float partialTicks ) + { + super.render( transform, mouseX, mouseY, partialTicks ); + + TextRenderer font = client.textRenderer; + List lines = font.wrapLines( new TranslatableText( "gui.computercraft.pocket_computer_overlay" ), (int) (width * 0.8) ); + float y = 10.0f; + for( OrderedText line : lines ) + { + font.drawWithShadow( transform, line, (float) ((width / 2) - (client.textRenderer.getWidth( line ) / 2)), y, 0xFFFFFF ); + y += 9.0f; + } + } +} diff --git a/src/main/java/dan200/computercraft/client/gui/OptionScreen.java b/src/main/java/dan200/computercraft/client/gui/OptionScreen.java new file mode 100644 index 000000000..23de17f2a --- /dev/null +++ b/src/main/java/dan200/computercraft/client/gui/OptionScreen.java @@ -0,0 +1,128 @@ +/* + * 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.gui; + +import com.mojang.blaze3d.systems.RenderSystem; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.font.MultilineText; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.gui.widget.ClickableWidget; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; + +import javax.annotation.Nonnull; +import java.util.List; + +public final class OptionScreen extends Screen +{ + private static final Identifier BACKGROUND = new Identifier( "computercraft", "textures/gui/blank_screen.png" ); + + public static final int BUTTON_WIDTH = 100; + public static final int BUTTON_HEIGHT = 20; + + private static final int PADDING = 16; + private static final int FONT_HEIGHT = 9; + + private int x; + private int y; + private int innerWidth; + private int innerHeight; + + private MultilineText messageRenderer; + private final Text message; + private final List buttons; + private final Runnable exit; + + private final Screen originalScreen; + + private OptionScreen( Text title, Text message, List buttons, Runnable exit, Screen originalScreen ) + { + super( title ); + this.message = message; + this.buttons = buttons; + this.exit = exit; + this.originalScreen = originalScreen; + } + + public static void show( MinecraftClient client, Text title, Text message, List buttons, Runnable exit ) + { + client.setScreen( new OptionScreen( title, message, buttons, exit, unwrap( client.currentScreen ) ) ); + } + + public static Screen unwrap( Screen screen ) + { + return screen instanceof OptionScreen ? ((OptionScreen) screen).getOriginalScreen() : screen; + } + + @Override + public void init() + { + super.init(); + + int buttonWidth = BUTTON_WIDTH * buttons.size() + PADDING * (buttons.size() - 1); + int innerWidth = this.innerWidth = Math.max( 256, buttonWidth + PADDING * 2 ); + + messageRenderer = MultilineText.create( textRenderer, message, innerWidth - PADDING * 2 ); + + int textHeight = messageRenderer.count() * FONT_HEIGHT + PADDING * 2; + innerHeight = textHeight + (buttons.isEmpty() ? 0 : buttons.get( 0 ).getHeight()) + PADDING; + + x = (width - innerWidth) / 2; + y = (height - innerHeight) / 2; + + int x = (width - buttonWidth) / 2; + for( ClickableWidget button : buttons ) + { + button.x = x; + button.y = y + textHeight; + addDrawableChild( button ); + + x += BUTTON_WIDTH + PADDING; + } + } + + @Override + public void render( @Nonnull MatrixStack transform, int mouseX, int mouseY, float partialTicks ) + { + renderBackground( transform ); + + // Render the actual texture. + RenderSystem.setShaderTexture( 0, BACKGROUND ); + drawTexture( transform, x, y, 0, 0, innerWidth, PADDING ); + drawTexture( transform, + x, y + PADDING, 0, PADDING, innerWidth, innerHeight - PADDING * 2, + innerWidth, PADDING + ); + drawTexture( transform, x, y + innerHeight - PADDING, 0, 256 - PADDING, innerWidth, PADDING ); + + messageRenderer.draw( transform, x + PADDING, y + PADDING, FONT_HEIGHT, 0x404040 ); + super.render( transform, mouseX, mouseY, partialTicks ); + } + + @Override + public void onClose() + { + exit.run(); + } + + public static ClickableWidget newButton( Text component, ButtonWidget.PressAction clicked ) + { + return new ButtonWidget( 0, 0, BUTTON_WIDTH, BUTTON_HEIGHT, component, clicked ); + } + + public void disable() + { + for( ClickableWidget widget : buttons ) widget.active = false; + } + + @Nonnull + public Screen getOriginalScreen() + { + return originalScreen; + } +} diff --git a/src/main/java/dan200/computercraft/client/gui/widgets/ComputerSidebar.java b/src/main/java/dan200/computercraft/client/gui/widgets/ComputerSidebar.java new file mode 100644 index 000000000..25d87c8dc --- /dev/null +++ b/src/main/java/dan200/computercraft/client/gui/widgets/ComputerSidebar.java @@ -0,0 +1,107 @@ +/* + * 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.gui.widgets; + +import dan200.computercraft.ComputerCraft; +import dan200.computercraft.client.render.ComputerBorderRenderer; +import dan200.computercraft.shared.command.text.ChatHelpers; +import dan200.computercraft.shared.computer.core.ClientComputer; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.widget.ClickableWidget; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.text.TranslatableText; +import net.minecraft.util.Formatting; +import net.minecraft.util.Identifier; + +import java.util.Arrays; +import java.util.function.Consumer; + +/** + * Registers buttons to interact with a computer. + */ +public final class ComputerSidebar +{ + private static final Identifier TEXTURE = new Identifier( ComputerCraft.MOD_ID, "textures/gui/buttons.png" ); + + private static final int TEX_SIZE = 64; + + private static final int ICON_WIDTH = 12; + private static final int ICON_HEIGHT = 12; + private static final int ICON_MARGIN = 2; + + private static final int ICON_TEX_Y_DIFF = 14; + + private static final int CORNERS_BORDER = 3; + private static final int FULL_BORDER = CORNERS_BORDER + ICON_MARGIN; + + private static final int BUTTONS = 2; + private static final int HEIGHT = (ICON_HEIGHT + ICON_MARGIN * 2) * BUTTONS + CORNERS_BORDER * 2; + public static final int WIDTH = 17; + + private ComputerSidebar() + { + } + + public static void addButtons( Screen screen, ClientComputer computer, Consumer add, int x, int y ) + { + x += CORNERS_BORDER + 1; + y += CORNERS_BORDER + ICON_MARGIN; + + add.accept( new DynamicImageButton( + screen, x, y, ICON_WIDTH, ICON_HEIGHT, () -> computer.isOn() ? 15 : 1, 1, ICON_TEX_Y_DIFF, + TEXTURE, TEX_SIZE, TEX_SIZE, b -> toggleComputer( computer ), + () -> computer.isOn() ? Arrays.asList( + new TranslatableText( "gui.computercraft.tooltip.turn_off" ), + ChatHelpers.coloured( new TranslatableText( "gui.computercraft.tooltip.turn_off.key" ), Formatting.GRAY ) + ) : Arrays.asList( + new TranslatableText( "gui.computercraft.tooltip.turn_on" ), + ChatHelpers.coloured( new TranslatableText( "gui.computercraft.tooltip.turn_off.key" ), Formatting.GRAY ) + ) + ) ); + + y += ICON_HEIGHT + ICON_MARGIN * 2; + + add.accept( new DynamicImageButton( + screen, x, y, ICON_WIDTH, ICON_HEIGHT, 29, 1, ICON_TEX_Y_DIFF, + TEXTURE, TEX_SIZE, TEX_SIZE, b -> computer.queueEvent( "terminate" ), + Arrays.asList( + new TranslatableText( "gui.computercraft.tooltip.terminate" ), + ChatHelpers.coloured( new TranslatableText( "gui.computercraft.tooltip.terminate.key" ), Formatting.GRAY ) + ) + ) ); + } + + public static void renderBackground( MatrixStack transform, int x, int y ) + { + Screen.drawTexture( transform, + x, y, 0, 102, WIDTH, FULL_BORDER, + ComputerBorderRenderer.TEX_SIZE, ComputerBorderRenderer.TEX_SIZE + ); + + Screen.drawTexture( transform, + x, y + FULL_BORDER, WIDTH, HEIGHT - FULL_BORDER * 2, + 0, 107, WIDTH, 4, + ComputerBorderRenderer.TEX_SIZE, ComputerBorderRenderer.TEX_SIZE + ); + + Screen.drawTexture( transform, + x, y + HEIGHT - FULL_BORDER, 0, 111, WIDTH, FULL_BORDER, + ComputerBorderRenderer.TEX_SIZE, ComputerBorderRenderer.TEX_SIZE + ); + } + + private static void toggleComputer( ClientComputer computer ) + { + if( computer.isOn() ) + { + computer.shutdown(); + } + else + { + computer.turnOn(); + } + } +} diff --git a/src/main/java/dan200/computercraft/client/gui/widgets/DynamicImageButton.java b/src/main/java/dan200/computercraft/client/gui/widgets/DynamicImageButton.java new file mode 100644 index 000000000..4e8751883 --- /dev/null +++ b/src/main/java/dan200/computercraft/client/gui/widgets/DynamicImageButton.java @@ -0,0 +1,100 @@ +/* + * 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.gui.widgets; + +import com.mojang.blaze3d.systems.RenderSystem; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.client.gui.widget.ButtonWidget; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.text.LiteralText; +import net.minecraft.text.Text; +import net.minecraft.util.Identifier; + +import javax.annotation.Nonnull; +import java.util.List; +import java.util.function.IntSupplier; +import java.util.function.Supplier; + +/** + * Version of {@link net.minecraft.client.gui.widget.TexturedButtonWidget} which allows changing some properties + * dynamically. + */ +public class DynamicImageButton extends ButtonWidget +{ + private final Screen screen; + private final Identifier texture; + private final IntSupplier xTexStart; + private final int yTexStart; + private final int yDiffTex; + private final int textureWidth; + private final int textureHeight; + private final Supplier> tooltip; + + public DynamicImageButton( + Screen screen, int x, int y, int width, int height, int xTexStart, int yTexStart, int yDiffTex, + Identifier texture, int textureWidth, int textureHeight, + PressAction onPress, List tooltip + ) + { + this( + screen, x, y, width, height, () -> xTexStart, yTexStart, yDiffTex, + texture, textureWidth, textureHeight, + onPress, () -> tooltip + ); + } + + + public DynamicImageButton( + Screen screen, int x, int y, int width, int height, IntSupplier xTexStart, int yTexStart, int yDiffTex, + Identifier texture, int textureWidth, int textureHeight, + PressAction onPress, Supplier> tooltip + ) + { + super( x, y, width, height, LiteralText.EMPTY, onPress ); + this.screen = screen; + this.textureWidth = textureWidth; + this.textureHeight = textureHeight; + this.xTexStart = xTexStart; + this.yTexStart = yTexStart; + this.yDiffTex = yDiffTex; + this.texture = texture; + this.tooltip = tooltip; + } + + @Override + public void renderButton( @Nonnull MatrixStack stack, int mouseX, int mouseY, float partialTicks ) + { + RenderSystem.setShaderTexture( 0, texture ); + RenderSystem.disableDepthTest(); + + int yTex = yTexStart; + if( isHovered() ) yTex += yDiffTex; + + drawTexture( stack, x, y, xTexStart.getAsInt(), yTex, width, height, textureWidth, textureHeight ); + RenderSystem.enableDepthTest(); + + if( isHovered() ) renderToolTip( stack, mouseX, mouseY ); + } + + @Nonnull + @Override + public Text getMessage() + { + List tooltip = this.tooltip.get(); + return tooltip.isEmpty() ? LiteralText.EMPTY : tooltip.get( 0 ); + } + + // @Override + public void renderToolTip( @Nonnull MatrixStack stack, int mouseX, int mouseY ) + { + List tooltip = this.tooltip.get(); + + if( !tooltip.isEmpty() ) + { + screen.renderTooltip( stack, tooltip, mouseX, mouseY ); + } + } +} diff --git a/src/main/java/dan200/computercraft/client/gui/widgets/WidgetTerminal.java b/src/main/java/dan200/computercraft/client/gui/widgets/WidgetTerminal.java index 495a98a96..62c3d8404 100644 --- a/src/main/java/dan200/computercraft/client/gui/widgets/WidgetTerminal.java +++ b/src/main/java/dan200/computercraft/client/gui/widgets/WidgetTerminal.java @@ -9,66 +9,84 @@ package dan200.computercraft.client.gui.widgets; import dan200.computercraft.client.gui.FixedWidthFontRenderer; import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.shared.computer.core.ClientComputer; -import dan200.computercraft.shared.computer.core.IComputer; import net.minecraft.SharedConstants; import net.minecraft.client.MinecraftClient; -import net.minecraft.client.gui.Element; +import net.minecraft.client.gui.screen.narration.NarrationMessageBuilder; +import net.minecraft.client.gui.widget.ClickableWidget; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.text.LiteralText; +import net.minecraft.util.math.Matrix4f; import org.lwjgl.glfw.GLFW; +import javax.annotation.Nonnull; import java.util.BitSet; -import java.util.function.Supplier; import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT; import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_WIDTH; +import static dan200.computercraft.client.render.ComputerBorderRenderer.MARGIN; -public class WidgetTerminal implements Element +public class WidgetTerminal extends ClickableWidget { private static final float TERMINATE_TIME = 0.5f; - private final MinecraftClient client; - private final Supplier computer; - private final int termWidth; - private final int termHeight; - private final int leftMargin; - private final int rightMargin; - private final int topMargin; - private final int bottomMargin; - private final BitSet keysDown = new BitSet( 256 ); - private boolean focused; + private final ClientComputer computer; + + // The positions of the actual terminal + private final int innerX; + private final int innerY; + private final int innerWidth; + private final int innerHeight; + private float terminateTimer = -1; private float rebootTimer = -1; private float shutdownTimer = -1; + private int lastMouseButton = -1; private int lastMouseX = -1; private int lastMouseY = -1; - public WidgetTerminal( MinecraftClient client, Supplier computer, int termWidth, int termHeight, int leftMargin, int rightMargin, - int topMargin, int bottomMargin ) + private final BitSet keysDown = new BitSet( 256 ); + + public WidgetTerminal( @Nonnull ClientComputer computer, int x, int y, int termWidth, int termHeight ) { - this.client = client; + super( x, y, termWidth * FONT_WIDTH + MARGIN * 2, termHeight * FONT_HEIGHT + MARGIN * 2, LiteralText.EMPTY ); + this.computer = computer; - this.termWidth = termWidth; - this.termHeight = termHeight; - this.leftMargin = leftMargin; - this.rightMargin = rightMargin; - this.topMargin = topMargin; - this.bottomMargin = bottomMargin; + + innerX = x + MARGIN; + innerY = y + MARGIN; + innerWidth = termWidth * FONT_WIDTH; + innerHeight = termHeight * FONT_HEIGHT; + } + + @Override + public boolean charTyped( char ch, int modifiers ) + { + if( ch >= 32 && ch <= 126 || ch >= 160 && ch <= 255 ) // printable chars in byte range + { + // Queue the "char" event + queueEvent( "char", Character.toString( ch ) ); + } + + return true; + } + + private boolean inTermRegion( double mouseX, double mouseY ) + { + return active && visible && mouseX >= innerX && mouseY >= innerY && mouseX < innerX + innerWidth && mouseY < innerY + innerHeight; } @Override public boolean mouseClicked( double mouseX, double mouseY, int button ) { - ClientComputer computer = this.computer.get(); - if( computer == null || !computer.isColour() || button < 0 || button > 2 ) - { - return false; - } + if( !inTermRegion( mouseX, mouseY ) ) return false; + if( !computer.isColour() || button < 0 || button > 2 ) return false; Terminal term = computer.getTerminal(); if( term != null ) { - int charX = (int) (mouseX / FONT_WIDTH); - int charY = (int) (mouseY / FONT_HEIGHT); + int charX = (int) ((mouseX - innerX) / FONT_WIDTH); + int charY = (int) ((mouseY - innerY) / FONT_HEIGHT); charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 ); charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 ); @@ -85,17 +103,14 @@ public class WidgetTerminal implements Element @Override public boolean mouseReleased( double mouseX, double mouseY, int button ) { - ClientComputer computer = this.computer.get(); - if( computer == null || !computer.isColour() || button < 0 || button > 2 ) - { - return false; - } + if( !inTermRegion( mouseX, mouseY ) ) return false; + if( !computer.isColour() || button < 0 || button > 2 ) return false; Terminal term = computer.getTerminal(); if( term != null ) { - int charX = (int) (mouseX / FONT_WIDTH); - int charY = (int) (mouseY / FONT_HEIGHT); + int charX = (int) ((mouseX - innerX) / FONT_WIDTH); + int charY = (int) ((mouseY - innerY) / FONT_HEIGHT); charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 ); charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 ); @@ -115,17 +130,14 @@ public class WidgetTerminal implements Element @Override public boolean mouseDragged( double mouseX, double mouseY, int button, double v2, double v3 ) { - ClientComputer computer = this.computer.get(); - if( computer == null || !computer.isColour() || button < 0 || button > 2 ) - { - return false; - } + if( !inTermRegion( mouseX, mouseY ) ) return false; + if( !computer.isColour() || button < 0 || button > 2 ) return false; Terminal term = computer.getTerminal(); if( term != null ) { - int charX = (int) (mouseX / FONT_WIDTH); - int charY = (int) (mouseY / FONT_HEIGHT); + int charX = (int) ((mouseX - innerX) / FONT_WIDTH); + int charY = (int) ((mouseY - innerY) / FONT_HEIGHT); charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 ); charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 ); @@ -143,17 +155,14 @@ public class WidgetTerminal implements Element @Override public boolean mouseScrolled( double mouseX, double mouseY, double delta ) { - ClientComputer computer = this.computer.get(); - if( computer == null || !computer.isColour() || delta == 0 ) - { - return false; - } + if( !inTermRegion( mouseX, mouseY ) ) return false; + if( !computer.isColour() || delta == 0 ) return false; Terminal term = computer.getTerminal(); if( term != null ) { - int charX = (int) (mouseX / FONT_WIDTH); - int charY = (int) (mouseY / FONT_HEIGHT); + int charX = (int) ((mouseX - innerX) / FONT_WIDTH); + int charY = (int) ((mouseY - innerY) / FONT_HEIGHT); charX = Math.min( Math.max( charX, 0 ), term.getWidth() - 1 ); charY = Math.min( Math.max( charY, 0 ), term.getHeight() - 1 ); @@ -198,7 +207,7 @@ public class WidgetTerminal implements Element case GLFW.GLFW_KEY_V: // Ctrl+V for paste - String clipboard = client.keyboard.getClipboard(); + String clipboard = MinecraftClient.getInstance().keyboard.getClipboard(); if( clipboard != null ) { // Clip to the first occurrence of \r or \n @@ -239,11 +248,7 @@ public class WidgetTerminal implements Element // Queue the "key" event and add to the down set boolean repeat = keysDown.get( key ); keysDown.set( key ); - IComputer computer = this.computer.get(); - if( computer != null ) - { - computer.keyDown( key, repeat ); - } + computer.keyDown( key, repeat ); } return true; @@ -256,11 +261,7 @@ public class WidgetTerminal implements Element if( key >= 0 && keysDown.get( key ) ) { keysDown.set( key, false ); - IComputer computer = this.computer.get(); - if( computer != null ) - { - computer.keyUp( key ); - } + computer.keyUp( key ); } switch( key ) @@ -284,47 +285,26 @@ public class WidgetTerminal implements Element } @Override - public boolean charTyped( char ch, int modifiers ) + public void onFocusedChanged( boolean focused ) { - if( ch >= 32 && ch <= 126 || ch >= 160 && ch <= 255 ) // printable chars in byte range - { - // Queue the "char" event - queueEvent( "char", Character.toString( ch ) ); - } - - return true; - } - - @Override - public boolean changeFocus( boolean reversed ) - { - if( focused ) + if( !focused ) { // When blurring, we should make all keys go up for( int key = 0; key < keysDown.size(); key++ ) { - if( keysDown.get( key ) ) - { - queueEvent( "key_up", key ); - } + if( keysDown.get( key ) ) computer.keyUp( key ); } keysDown.clear(); // When blurring, we should make the last mouse button go up if( lastMouseButton > 0 ) { - IComputer computer = this.computer.get(); - if( computer != null ) - { - computer.mouseUp( lastMouseButton + 1, lastMouseX + 1, lastMouseY + 1 ); - } + computer.mouseUp( lastMouseButton + 1, lastMouseX + 1, lastMouseY + 1 ); lastMouseButton = -1; } shutdownTimer = terminateTimer = rebootTimer = -1; } - focused = !focused; - return true; } @Override @@ -335,11 +315,7 @@ public class WidgetTerminal implements Element private void queueEvent( String event, Object... args ) { - ClientComputer computer = this.computer.get(); - if( computer != null ) - { - computer.queueEvent( event, args ); - } + computer.queueEvent( event, args ); } public void update() @@ -351,50 +327,53 @@ public class WidgetTerminal implements Element if( shutdownTimer >= 0 && shutdownTimer < TERMINATE_TIME && (shutdownTimer += 0.05f) > TERMINATE_TIME ) { - ClientComputer computer = this.computer.get(); - if( computer != null ) - { - computer.shutdown(); - } + computer.shutdown(); } if( rebootTimer >= 0 && rebootTimer < TERMINATE_TIME && (rebootTimer += 0.05f) > TERMINATE_TIME ) { - ClientComputer computer = this.computer.get(); - if( computer != null ) - { - computer.reboot(); - } + computer.reboot(); } } private void queueEvent( String event ) { - ClientComputer computer = this.computer.get(); + ClientComputer computer = this.computer; if( computer != null ) { computer.queueEvent( event ); } } - public void draw( int originX, int originY ) + @Override + public void render( @Nonnull MatrixStack transform, int mouseX, int mouseY, float partialTicks ) { - synchronized( computer ) + if( !visible ) return; + Matrix4f matrix = transform.peek().getModel(); + Terminal terminal = computer.getTerminal(); + if( terminal != null ) { - // Draw the screen contents - ClientComputer computer = this.computer.get(); - Terminal terminal = computer != null ? computer.getTerminal() : null; - if( terminal != null ) - { - FixedWidthFontRenderer.drawTerminal( originX, originY, terminal, !computer.isColour(), topMargin, bottomMargin, leftMargin, - rightMargin ); - } - else - { - FixedWidthFontRenderer.drawEmptyTerminal( originX - leftMargin, - originY - rightMargin, termWidth * FONT_WIDTH + leftMargin + rightMargin, - termHeight * FONT_HEIGHT + topMargin + bottomMargin ); - } + FixedWidthFontRenderer.drawTerminal( matrix, innerX, innerY, terminal, !computer.isColour(), MARGIN, MARGIN, MARGIN, MARGIN ); + } + else + { + FixedWidthFontRenderer.drawEmptyTerminal( matrix, x, y, width, height ); } } + + @Override + public void appendNarrations( NarrationMessageBuilder builder ) + { + + } + + public static int getWidth( int termWidth ) + { + return termWidth * FONT_WIDTH + MARGIN * 2; + } + + public static int getHeight( int termHeight ) + { + return termHeight * FONT_HEIGHT + MARGIN * 2; + } } diff --git a/src/main/java/dan200/computercraft/client/gui/widgets/WidgetWrapper.java b/src/main/java/dan200/computercraft/client/gui/widgets/WidgetWrapper.java deleted file mode 100644 index c805a16ce..000000000 --- a/src/main/java/dan200/computercraft/client/gui/widgets/WidgetWrapper.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * 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.gui.widgets; - -import net.minecraft.client.gui.Element; - -public class WidgetWrapper implements Element -{ - private final Element listener; - private final int x; - private final int y; - private final int width; - private final int height; - - public WidgetWrapper( Element listener, int x, int y, int width, int height ) - { - this.listener = listener; - this.x = x; - this.y = y; - this.width = width; - this.height = height; - } - - @Override - public boolean mouseClicked( double x, double y, int button ) - { - double dx = x - this.x, dy = y - this.y; - return dx >= 0 && dx < width && dy >= 0 && dy < height && listener.mouseClicked( dx, dy, button ); - } - - @Override - public boolean mouseReleased( double x, double y, int button ) - { - double dx = x - this.x, dy = y - this.y; - return dx >= 0 && dx < width && dy >= 0 && dy < height && listener.mouseReleased( dx, dy, button ); - } - - @Override - public boolean mouseDragged( double x, double y, int button, double deltaX, double deltaY ) - { - double dx = x - this.x, dy = y - this.y; - return dx >= 0 && dx < width && dy >= 0 && dy < height && listener.mouseDragged( dx, dy, button, deltaX, deltaY ); - } - - @Override - public boolean mouseScrolled( double x, double y, double delta ) - { - double dx = x - this.x, dy = y - this.y; - return dx >= 0 && dx < width && dy >= 0 && dy < height && listener.mouseScrolled( dx, dy, delta ); - } - - @Override - public boolean keyPressed( int key, int scancode, int modifiers ) - { - return listener.keyPressed( key, scancode, modifiers ); - } - - @Override - public boolean keyReleased( int key, int scancode, int modifiers ) - { - return listener.keyReleased( key, scancode, modifiers ); - } - - @Override - public boolean charTyped( char character, int modifiers ) - { - return listener.charTyped( character, modifiers ); - } - - @Override - public boolean changeFocus( boolean b ) - { - return listener.changeFocus( b ); - } - - @Override - public boolean isMouseOver( double x, double y ) - { - double dx = x - this.x, dy = y - this.y; - return dx >= 0 && dx < width && dy >= 0 && dy < height; - } - - public int getX() - { - return x; - } - - public int getY() - { - return y; - } - - public int getWidth() - { - return width; - } - - public int getHeight() - { - return height; - } -} diff --git a/src/main/java/dan200/computercraft/client/proxy/ComputerCraftProxyClient.java b/src/main/java/dan200/computercraft/client/proxy/ComputerCraftProxyClient.java index fa7c0a8f1..3d22b88ef 100644 --- a/src/main/java/dan200/computercraft/client/proxy/ComputerCraftProxyClient.java +++ b/src/main/java/dan200/computercraft/client/proxy/ComputerCraftProxyClient.java @@ -19,12 +19,11 @@ import dan200.computercraft.shared.ComputerCraftRegistry; import dan200.computercraft.shared.common.ContainerHeldItem; import dan200.computercraft.shared.common.IColouredItem; import dan200.computercraft.shared.common.TileGeneric; -import dan200.computercraft.shared.computer.inventory.ContainerComputer; +import dan200.computercraft.shared.computer.inventory.ContainerComputerBase; import dan200.computercraft.shared.computer.inventory.ContainerViewComputer; import dan200.computercraft.shared.peripheral.diskdrive.ContainerDiskDrive; import dan200.computercraft.shared.peripheral.monitor.ClientMonitor; import dan200.computercraft.shared.peripheral.printer.ContainerPrinter; -import dan200.computercraft.shared.pocket.inventory.ContainerPocketComputer; import dan200.computercraft.shared.pocket.items.ItemPocketComputer; import dan200.computercraft.shared.turtle.inventory.ContainerTurtle; import dan200.computercraft.shared.util.Config; @@ -35,12 +34,12 @@ import net.fabricmc.fabric.api.blockrenderlayer.v1.BlockRenderLayerMap; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientBlockEntityEvents; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientLifecycleEvents; import net.fabricmc.fabric.api.client.model.ModelLoadingRegistry; -import net.fabricmc.fabric.api.client.rendereregistry.v1.BlockEntityRendererRegistry; -import net.fabricmc.fabric.api.client.rendereregistry.v1.EntityRendererRegistry; +import net.fabricmc.fabric.api.client.rendering.v1.BlockEntityRendererRegistry; +import net.fabricmc.fabric.api.client.rendering.v1.EntityRendererRegistry; import net.fabricmc.fabric.api.client.screenhandler.v1.ScreenRegistry; import net.fabricmc.fabric.api.event.client.ClientSpriteRegistryCallback; -import net.fabricmc.fabric.mixin.object.builder.ModelPredicateProviderRegistrySpecificAccessor; -import net.minecraft.client.item.ModelPredicateProvider; +import net.fabricmc.fabric.api.object.builder.v1.client.model.FabricModelPredicateProviderRegistry; +import net.minecraft.client.item.UnclampedModelPredicateProvider; import net.minecraft.client.render.RenderLayer; import net.minecraft.item.Item; import net.minecraft.screen.PlayerScreenHandler; @@ -76,33 +75,32 @@ public final class ComputerCraftProxyClient implements ClientModInitializer // While turtles themselves are not transparent, their upgrades may be. BlockRenderLayerMap.INSTANCE.putBlock( ComputerCraftRegistry.ModBlocks.TURTLE_NORMAL, RenderLayer.getTranslucent() ); BlockRenderLayerMap.INSTANCE.putBlock( ComputerCraftRegistry.ModBlocks.TURTLE_ADVANCED, RenderLayer.getTranslucent() ); - // Monitors' textures have transparent fronts and so count as cutouts. BlockRenderLayerMap.INSTANCE.putBlock( ComputerCraftRegistry.ModBlocks.MONITOR_NORMAL, RenderLayer.getCutout() ); BlockRenderLayerMap.INSTANCE.putBlock( ComputerCraftRegistry.ModBlocks.MONITOR_ADVANCED, RenderLayer.getCutout() ); // Setup TESRs - BlockEntityRendererRegistry.INSTANCE.register( ComputerCraftRegistry.ModTiles.MONITOR_NORMAL, TileEntityMonitorRenderer::new ); - BlockEntityRendererRegistry.INSTANCE.register( ComputerCraftRegistry.ModTiles.MONITOR_ADVANCED, TileEntityMonitorRenderer::new ); - BlockEntityRendererRegistry.INSTANCE.register( ComputerCraftRegistry.ModTiles.TURTLE_NORMAL, TileEntityTurtleRenderer::new ); - BlockEntityRendererRegistry.INSTANCE.register( ComputerCraftRegistry.ModTiles.TURTLE_ADVANCED, TileEntityTurtleRenderer::new ); + BlockEntityRendererRegistry.register( ComputerCraftRegistry.ModTiles.MONITOR_NORMAL, TileEntityMonitorRenderer::new ); + BlockEntityRendererRegistry.register( ComputerCraftRegistry.ModTiles.MONITOR_ADVANCED, TileEntityMonitorRenderer::new ); + BlockEntityRendererRegistry.register( ComputerCraftRegistry.ModTiles.TURTLE_NORMAL, TileEntityTurtleRenderer::new ); + BlockEntityRendererRegistry.register( ComputerCraftRegistry.ModTiles.TURTLE_ADVANCED, TileEntityTurtleRenderer::new ); ClientSpriteRegistryCallback.event( PlayerScreenHandler.BLOCK_ATLAS_TEXTURE ) .register( ClientRegistry::onTextureStitchEvent ); - ModelLoadingRegistry.INSTANCE.registerAppender( ClientRegistry::onModelBakeEvent ); + ModelLoadingRegistry.INSTANCE.registerModelProvider( ClientRegistry::onModelBakeEvent ); ModelLoadingRegistry.INSTANCE.registerResourceProvider( loader -> ( name, context ) -> TurtleModelLoader.INSTANCE.accepts( name ) ? TurtleModelLoader.INSTANCE.loadModel( name ) : null ); - EntityRendererRegistry.INSTANCE.register( ComputerCraftRegistry.ModEntities.TURTLE_PLAYER, TurtlePlayerRenderer::new ); + EntityRendererRegistry.register( ComputerCraftRegistry.ModEntities.TURTLE_PLAYER, TurtlePlayerRenderer::new ); registerItemProperty( "state", - ( stack, world, player ) -> ItemPocketComputer.getState( stack ) + ( stack, world, player, integer ) -> ItemPocketComputer.getState( stack ) .ordinal(), () -> ComputerCraftRegistry.ModItems.POCKET_COMPUTER_NORMAL, () -> ComputerCraftRegistry.ModItems.POCKET_COMPUTER_ADVANCED ); registerItemProperty( "state", - ( stack, world, player ) -> IColouredItem.getColourBasic( stack ) != -1 ? 1 : 0, + ( stack, world, player, integer ) -> IColouredItem.getColourBasic( stack ) != -1 ? 1 : 0, () -> ComputerCraftRegistry.ModItems.POCKET_COMPUTER_NORMAL, () -> ComputerCraftRegistry.ModItems.POCKET_COMPUTER_ADVANCED ); ClientRegistry.onItemColours(); @@ -113,9 +111,11 @@ public final class ComputerCraftProxyClient implements ClientModInitializer // My IDE doesn't think so, but we do actually need these generics. private static void registerContainers() { - ScreenRegistry.>register( ComputerCraftRegistry.ModContainers.COMPUTER, GuiComputer::create ); - ScreenRegistry.>register( ComputerCraftRegistry.ModContainers.POCKET_COMPUTER, + ScreenRegistry.>register( ComputerCraftRegistry.ModContainers.COMPUTER, GuiComputer::create ); + ScreenRegistry.>register( ComputerCraftRegistry.ModContainers.POCKET_COMPUTER, GuiComputer::createPocket ); + ScreenRegistry.>register( ComputerCraftRegistry.ModContainers.POCKET_COMPUTER_NO_TERM, + NoTermComputerScreen::new ); ScreenRegistry.register( ComputerCraftRegistry.ModContainers.TURTLE, GuiTurtle::new ); ScreenRegistry.register( ComputerCraftRegistry.ModContainers.PRINTER, GuiPrinter::new ); @@ -127,12 +127,12 @@ public final class ComputerCraftProxyClient implements ClientModInitializer } @SafeVarargs - private static void registerItemProperty( String name, ModelPredicateProvider getter, Supplier... items ) + private static void registerItemProperty( String name, UnclampedModelPredicateProvider getter, Supplier... items ) { Identifier id = new Identifier( ComputerCraft.MOD_ID, name ); for( Supplier item : items ) { - ModelPredicateProviderRegistrySpecificAccessor.callRegister( item.get(), id, getter ); + FabricModelPredicateProviderRegistry.register( item.get(), id, getter ); } } } diff --git a/src/main/java/dan200/computercraft/client/render/CableHighlightRenderer.java b/src/main/java/dan200/computercraft/client/render/CableHighlightRenderer.java index 2532e044f..32965f478 100644 --- a/src/main/java/dan200/computercraft/client/render/CableHighlightRenderer.java +++ b/src/main/java/dan200/computercraft/client/render/CableHighlightRenderer.java @@ -18,9 +18,7 @@ import net.minecraft.client.render.Camera; import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.entity.Entity; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Matrix4f; -import net.minecraft.util.math.Vec3d; +import net.minecraft.util.math.*; import net.minecraft.util.shape.VoxelShape; @Environment( EnvType.CLIENT ) @@ -49,17 +47,29 @@ public final class CableHighlightRenderer state ); Vec3d cameraPos = info.getPos(); + double xOffset = pos.getX() - cameraPos.getX(); double yOffset = pos.getY() - cameraPos.getY(); double zOffset = pos.getZ() - cameraPos.getZ(); Matrix4f matrix4f = stack.peek() .getModel(); + Matrix3f normal = stack.peek().getNormal(); shape.forEachEdge( ( x1, y1, z1, x2, y2, z2 ) -> { + float xDelta = (float) (x2 - x1); + float yDelta = (float) (y2 - y1); + float zDelta = (float) (z2 - z1); + float len = MathHelper.sqrt( xDelta * xDelta + yDelta * yDelta + zDelta * zDelta ); + xDelta = xDelta / len; + yDelta = yDelta / len; + zDelta = zDelta / len; + consumer.vertex( matrix4f, (float) (x1 + xOffset), (float) (y1 + yOffset), (float) (z1 + zOffset) ) .color( 0, 0, 0, 0.4f ) + .normal( normal, xDelta, yDelta, zDelta ) .next(); consumer.vertex( matrix4f, (float) (x2 + xOffset), (float) (y2 + yOffset), (float) (z2 + zOffset) ) .color( 0, 0, 0, 0.4f ) + .normal( normal, xDelta, yDelta, zDelta ) .next(); } ); diff --git a/src/main/java/dan200/computercraft/client/render/ComputerBorderRenderer.java b/src/main/java/dan200/computercraft/client/render/ComputerBorderRenderer.java index 65166b05d..c36acd10e 100644 --- a/src/main/java/dan200/computercraft/client/render/ComputerBorderRenderer.java +++ b/src/main/java/dan200/computercraft/client/render/ComputerBorderRenderer.java @@ -5,16 +5,14 @@ */ package dan200.computercraft.client.render; -import com.mojang.blaze3d.systems.RenderSystem; import dan200.computercraft.ComputerCraft; import dan200.computercraft.shared.computer.core.ComputerFamily; -import net.minecraft.client.render.BufferBuilder; +import net.minecraft.client.render.RenderLayer; import net.minecraft.client.render.Tessellator; import net.minecraft.client.render.VertexConsumer; -import net.minecraft.client.render.VertexFormats; +import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.util.Identifier; import net.minecraft.util.math.Matrix4f; -import org.lwjgl.opengl.GL11; import javax.annotation.Nonnull; @@ -42,7 +40,8 @@ public class ComputerBorderRenderer private static final int LIGHT_CORNER_Y = 80; public static final int LIGHT_HEIGHT = 8; - private static final float TEX_SCALE = 1 / 256.0f; + public static final int TEX_SIZE = 256; + private static final float TEX_SCALE = 1 / (float) TEX_SIZE; static { @@ -54,11 +53,14 @@ public class ComputerBorderRenderer private final int z; private final float r, g, b; - public ComputerBorderRenderer( Matrix4f transform, VertexConsumer builder, int z, float r, float g, float b ) + private final int light; + + public ComputerBorderRenderer( Matrix4f transform, VertexConsumer builder, int z, int light, float r, float g, float b ) { this.transform = transform; this.builder = builder; this.z = z; + this.light = light; this.r = r; this.g = g; this.b = b; @@ -80,31 +82,17 @@ public class ComputerBorderRenderer } } - public static void render( int x, int y, int z, int width, int height ) + public static void render( Identifier location, int x, int y, int z, int light, int width, int height ) { - Tessellator tessellator = Tessellator.getInstance(); - BufferBuilder buffer = tessellator.getBuffer(); - buffer.begin( GL11.GL_QUADS, VertexFormats.POSITION_COLOR_TEXTURE ); - - render( IDENTITY, buffer, x, y, z, width, height ); - - RenderSystem.enableAlphaTest(); - tessellator.draw(); + VertexConsumerProvider.Immediate source = VertexConsumerProvider.immediate( Tessellator.getInstance().getBuffer() ); + render( IDENTITY, source.getBuffer( RenderLayer.getText( location ) ), x, y, z, light, width, height, false, 1, 1, 1 ); + source.draw(); } - public static void render( Matrix4f transform, VertexConsumer buffer, int x, int y, int z, int width, int height ) - { - render( transform, buffer, x, y, z, width, height, 1, 1, 1 ); - } - public static void render( Matrix4f transform, VertexConsumer buffer, int x, int y, int z, int width, int height, float r, float g, float b ) + public static void render( Matrix4f transform, VertexConsumer buffer, int x, int y, int z, int light, int width, int height, boolean withLight, float r, float g, float b ) { - render( transform, buffer, x, y, z, width, height, false, r, g, b ); - } - - public static void render( Matrix4f transform, VertexConsumer buffer, int x, int y, int z, int width, int height, boolean withLight, float r, float g, float b ) - { - new ComputerBorderRenderer( transform, buffer, z, r, g, b ).doRender( x, y, width, height, withLight ); + new ComputerBorderRenderer( transform, buffer, z, light, r, g, b ).doRender( x, y, width, height, withLight ); } public void doRender( int x, int y, int width, int height, boolean withLight ) @@ -157,18 +145,22 @@ public class ComputerBorderRenderer builder.vertex( transform, x, y + height, z ) .color( r, g, b, 1.0f ) .texture( u * TEX_SCALE, (v + textureHeight) * TEX_SCALE ) + .light( light ) .next(); builder.vertex( transform, x + width, y + height, z ) .color( r, g, b, 1.0f ) .texture( (u + textureWidth) * TEX_SCALE, (v + textureHeight) * TEX_SCALE ) + .light( light ) .next(); builder.vertex( transform, x + width, y, z ) .color( r, g, b, 1.0f ) .texture( (u + textureWidth) * TEX_SCALE, v * TEX_SCALE ) + .light( light ) .next(); builder.vertex( transform, x, y, z ) .color( r, g, b, 1.0f ) .texture( u * TEX_SCALE, v * TEX_SCALE ) + .light( light ) .next(); } } diff --git a/src/main/java/dan200/computercraft/client/render/ItemMapLikeRenderer.java b/src/main/java/dan200/computercraft/client/render/ItemMapLikeRenderer.java index 6ecd99254..f1ef584c5 100644 --- a/src/main/java/dan200/computercraft/client/render/ItemMapLikeRenderer.java +++ b/src/main/java/dan200/computercraft/client/render/ItemMapLikeRenderer.java @@ -89,7 +89,7 @@ public abstract class ItemMapLikeRenderer transform.multiply( Vec3f.POSITIVE_X.getDegreesQuaternion( rX * 20.0F ) ); transform.scale( 2.0F, 2.0F, 2.0F ); - renderItem( transform, render, stack ); + renderItem( transform, render, stack, combinedLight ); } /** @@ -133,7 +133,7 @@ public abstract class ItemMapLikeRenderer transform.multiply( Vec3f.POSITIVE_X.getDegreesQuaternion( f2 * -45f ) ); transform.multiply( Vec3f.POSITIVE_Y.getDegreesQuaternion( offset * f2 * -30f ) ); - renderItem( transform, render, stack ); + renderItem( transform, render, stack, combinedLight ); transform.pop(); } @@ -144,6 +144,7 @@ public abstract class ItemMapLikeRenderer * @param transform The matrix transformation stack * @param render The buffer to render to * @param stack The stack to render + * @param light TODO rebase */ - protected abstract void renderItem( MatrixStack transform, VertexConsumerProvider render, ItemStack stack ); + protected abstract void renderItem( MatrixStack transform, VertexConsumerProvider render, ItemStack stack, int light ); } diff --git a/src/main/java/dan200/computercraft/client/render/ItemPocketRenderer.java b/src/main/java/dan200/computercraft/client/render/ItemPocketRenderer.java index ec96c0d81..23982f3a9 100644 --- a/src/main/java/dan200/computercraft/client/render/ItemPocketRenderer.java +++ b/src/main/java/dan200/computercraft/client/render/ItemPocketRenderer.java @@ -15,15 +15,12 @@ import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.pocket.items.ItemPocketComputer; import dan200.computercraft.shared.util.Colour; import net.minecraft.client.MinecraftClient; -import net.minecraft.client.render.BufferBuilder; -import net.minecraft.client.render.Tessellator; -import net.minecraft.client.render.VertexConsumerProvider; -import net.minecraft.client.render.VertexFormats; +import net.minecraft.client.render.*; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.item.ItemStack; +import net.minecraft.util.Identifier; import net.minecraft.util.math.Matrix4f; import net.minecraft.util.math.Vec3f; -import org.lwjgl.opengl.GL11; import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT; import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_WIDTH; @@ -41,7 +38,7 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer } @Override - protected void renderItem( MatrixStack transform, VertexConsumerProvider render, ItemStack stack ) + protected void renderItem( MatrixStack transform, VertexConsumerProvider render, ItemStack stack, int light ) { ClientComputer computer = ItemPocketComputer.createClientComputer( stack ); Terminal terminal = computer == null ? null : computer.getTerminal(); @@ -79,7 +76,7 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer Matrix4f matrix = transform.peek() .getModel(); - renderFrame( matrix, family, frameColour, width, height ); + renderFrame( matrix, render, family, frameColour, light, width, height ); // Render the light int lightColour = ItemPocketComputer.getLightState( stack ); @@ -91,7 +88,12 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer if( computer != null && terminal != null ) { - FixedWidthFontRenderer.drawTerminal( matrix, MARGIN, MARGIN, terminal, !computer.isColour(), MARGIN, MARGIN, MARGIN, MARGIN ); + FixedWidthFontRenderer.drawTerminal( + matrix, render.getBuffer( RenderTypes.TERMINAL_WITHOUT_DEPTH ), + MARGIN, MARGIN, terminal, !computer.isColour(), MARGIN, MARGIN, MARGIN, MARGIN + ); + FixedWidthFontRenderer.drawBlocker( transform.peek().getModel(), render, 0, 0, width, height ); + } else { @@ -101,24 +103,20 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer transform.pop(); } - private static void renderFrame( Matrix4f transform, ComputerFamily family, int colour, int width, int height ) + private static void renderFrame( Matrix4f transform, VertexConsumerProvider render, ComputerFamily family, int colour, int light, int width, int height ) { RenderSystem.enableBlend(); MinecraftClient.getInstance() .getTextureManager() .bindTexture( colour != -1 ? ComputerBorderRenderer.BACKGROUND_COLOUR : ComputerBorderRenderer.getTexture( family ) ); + Identifier texture = colour != -1 ? ComputerBorderRenderer.BACKGROUND_COLOUR : ComputerBorderRenderer.getTexture( family ); + float r = ((colour >>> 16) & 0xFF) / 255.0f; float g = ((colour >>> 8) & 0xFF) / 255.0f; float b = (colour & 0xFF) / 255.0f; - Tessellator tessellator = Tessellator.getInstance(); - BufferBuilder buffer = tessellator.getBuffer(); - buffer.begin( GL11.GL_QUADS, VertexFormats.POSITION_COLOR_TEXTURE ); - - ComputerBorderRenderer.render( transform, buffer, 0, 0, 0, width, height, true, r, g, b ); - - tessellator.draw(); + ComputerBorderRenderer.render( transform, render.getBuffer( RenderLayer.getText( texture ) ), 0, 0, 0, light, width, height, true, r, g, b ); } private static void renderLight( Matrix4f transform, int colour, int width, int height ) @@ -131,7 +129,7 @@ public final class ItemPocketRenderer extends ItemMapLikeRenderer Tessellator tessellator = Tessellator.getInstance(); BufferBuilder buffer = tessellator.getBuffer(); - buffer.begin( GL11.GL_QUADS, VertexFormats.POSITION_COLOR ); + buffer.begin( VertexFormat.DrawMode.QUADS, VertexFormats.POSITION_COLOR ); buffer.vertex( transform, width - LIGHT_HEIGHT * 2, height + LIGHT_HEIGHT + BORDER / 2.0f, 0 ) .color( r, g, b, 1.0f ) .next(); diff --git a/src/main/java/dan200/computercraft/client/render/ItemPrintoutRenderer.java b/src/main/java/dan200/computercraft/client/render/ItemPrintoutRenderer.java index e45ec3ef4..3760a6f80 100644 --- a/src/main/java/dan200/computercraft/client/render/ItemPrintoutRenderer.java +++ b/src/main/java/dan200/computercraft/client/render/ItemPrintoutRenderer.java @@ -9,6 +9,7 @@ package dan200.computercraft.client.render; import dan200.computercraft.shared.media.items.ItemPrintout; import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.math.Vec3f; import net.minecraft.item.ItemStack; import net.minecraft.util.math.Matrix4f; import net.minecraft.util.math.Vec3f; @@ -31,16 +32,16 @@ public final class ItemPrintoutRenderer extends ItemMapLikeRenderer } @Override - protected void renderItem( MatrixStack transform, VertexConsumerProvider render, ItemStack stack ) + protected void renderItem( MatrixStack transform, VertexConsumerProvider render, ItemStack stack, int light ) { transform.multiply( Vec3f.POSITIVE_X.getDegreesQuaternion( 180f ) ); transform.scale( 0.42f, 0.42f, -0.42f ); transform.translate( -0.5f, -0.48f, 0.0f ); - drawPrintout( transform, render, stack ); + drawPrintout( transform, render, stack, light ); } - private static void drawPrintout( MatrixStack transform, VertexConsumerProvider render, ItemStack stack ) + private static void drawPrintout( MatrixStack transform, VertexConsumerProvider render, ItemStack stack, int light ) { int pages = ItemPrintout.getPageCount( stack ); boolean book = ((ItemPrintout) stack.getItem()).getType() == ItemPrintout.Type.BOOK; @@ -72,11 +73,11 @@ public final class ItemPrintoutRenderer extends ItemMapLikeRenderer Matrix4f matrix = transform.peek() .getModel(); - drawBorder( matrix, render, 0, 0, -0.01f, 0, pages, book ); - drawText( matrix, render, X_TEXT_MARGIN, Y_TEXT_MARGIN, 0, ItemPrintout.getText( stack ), ItemPrintout.getColours( stack ) ); + drawBorder( matrix, render, 0, 0, -0.01f, 0, pages, book, light ); + drawText( matrix, render, X_TEXT_MARGIN, Y_TEXT_MARGIN, 0, light, ItemPrintout.getText( stack ), ItemPrintout.getColours( stack ) ); } - public boolean renderInFrame( MatrixStack matrixStack, VertexConsumerProvider consumerProvider, ItemStack stack ) + public boolean renderInFrame( MatrixStack matrixStack, VertexConsumerProvider consumerProvider, ItemStack stack, int light ) { if( !(stack.getItem() instanceof ItemPrintout) ) { @@ -89,7 +90,7 @@ public final class ItemPrintoutRenderer extends ItemMapLikeRenderer matrixStack.scale( 0.95f, 0.95f, -0.95f ); matrixStack.translate( -0.5f, -0.5f, 0.0f ); - drawPrintout( matrixStack, consumerProvider, stack ); + drawPrintout( matrixStack, consumerProvider, stack, light ); return true; } diff --git a/src/main/java/dan200/computercraft/client/render/ModelTransformer.java b/src/main/java/dan200/computercraft/client/render/ModelTransformer.java index ce01b25b6..1556246a1 100644 --- a/src/main/java/dan200/computercraft/client/render/ModelTransformer.java +++ b/src/main/java/dan200/computercraft/client/render/ModelTransformer.java @@ -5,7 +5,6 @@ */ package dan200.computercraft.client.render; -import dan200.computercraft.fabric.mixin.BakedQuadAccess; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.render.VertexFormat; @@ -14,6 +13,7 @@ import net.minecraft.client.render.VertexFormats; import net.minecraft.client.render.model.BakedQuad; import net.minecraft.util.math.Matrix4f; import net.minecraft.util.math.Vector4f; + import java.util.List; /** @@ -57,30 +57,43 @@ public final class ModelTransformer private static BakedQuad doTransformQuad( VertexFormat format, BakedQuad quad, Matrix4f transform ) { int[] vertexData = quad.getVertexData().clone(); - BakedQuad copy = new BakedQuad( vertexData, -1, quad.getFace(), ((BakedQuadAccess) quad).getSprite(), true ); + BakedQuad copy = new BakedQuad( vertexData, -1, quad.getFace(), quad.getSprite(), true ); int offsetBytes = 0; for( int v = 0; v < 4; ++v ) { for( VertexFormatElement element : format.getElements() ) // For each vertex element { - int start = offsetBytes / Integer.BYTES; - if( element.getType() == VertexFormatElement.Type.POSITION && element.getDataType() == VertexFormatElement.DataType.FLOAT ) // When we find a position element + if( element.isPosition() && + element.getDataType() == VertexFormatElement.DataType.FLOAT && + element.getLength() == 3 ) // When we find a position element { - Vector4f pos = new Vector4f( Float.intBitsToFloat( vertexData[start] ), - Float.intBitsToFloat( vertexData[start + 1] ), - Float.intBitsToFloat( vertexData[start + 2] ), - 1 ); + for ( int j = 0; j < 4; ++j ) // For each corner of the quad + { + int start = offsetBytes + j * format.getVertexSize(); + if ( (start % 4) == 0 ) + { + start = start / 4; - // Transform the position - pos.transform( transform ); + // Extract the position + Vector4f pos = new Vector4f( + Float.intBitsToFloat( vertexData[start] ), + Float.intBitsToFloat( vertexData[start + 1] ), + Float.intBitsToFloat( vertexData[start + 2] ), + 1 + ); - // Insert the position - vertexData[start] = Float.floatToRawIntBits( pos.getX() ); - vertexData[start + 1] = Float.floatToRawIntBits( pos.getY() ); - vertexData[start + 2] = Float.floatToRawIntBits( pos.getZ() ); + // Transform the position + pos.transform( transform ); + + // Insert the position + vertexData[start] = Float.floatToRawIntBits( pos.getX() ); + vertexData[start + 1] = Float.floatToRawIntBits( pos.getY() ); + vertexData[start + 2] = Float.floatToRawIntBits( pos.getZ() ); + } + } } - offsetBytes += element.getByteLength(); + offsetBytes += element.getLength(); } } return copy; diff --git a/src/main/java/dan200/computercraft/client/render/MonitorHighlightRenderer.java b/src/main/java/dan200/computercraft/client/render/MonitorHighlightRenderer.java index d0117b2ac..d4083c67f 100644 --- a/src/main/java/dan200/computercraft/client/render/MonitorHighlightRenderer.java +++ b/src/main/java/dan200/computercraft/client/render/MonitorHighlightRenderer.java @@ -15,14 +15,10 @@ import net.minecraft.client.MinecraftClient; import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.entity.Entity; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Direction; -import net.minecraft.util.math.Matrix4f; -import net.minecraft.util.math.Vec3d; +import net.minecraft.util.math.*; import net.minecraft.world.World; import java.util.EnumSet; - import static net.minecraft.util.math.Direction.*; /** @@ -36,9 +32,7 @@ public final class MonitorHighlightRenderer { } - public static boolean drawHighlight( - MatrixStack matrixStack, VertexConsumer vertexConsumer, Entity entity, double d, double e, double f, BlockPos pos, BlockState blockState - ) + public static boolean drawHighlight( MatrixStack matrixStack, VertexConsumer vertexConsumer, Entity entity, double d, double e, double f, BlockPos pos, BlockState blockState ) { // Preserve normal behaviour when crouching. if( entity.isInSneakingPose() ) @@ -49,13 +43,11 @@ public final class MonitorHighlightRenderer World world = entity.getEntityWorld(); BlockEntity tile = world.getBlockEntity( pos ); - if( !(tile instanceof TileMonitor) ) + if( !(tile instanceof TileMonitor monitor) ) { return false; } - TileMonitor monitor = (TileMonitor) tile; - // Determine which sides are part of the external faces of the monitor, and so which need to be rendered. EnumSet faces = EnumSet.allOf( Direction.class ); Direction front = monitor.getFront(); @@ -87,53 +79,54 @@ public final class MonitorHighlightRenderer // I wish I could think of a better way to do this Matrix4f transform = matrixStack.peek() .getModel(); + Matrix3f normal = matrixStack.peek().getNormal(); if( faces.contains( NORTH ) || faces.contains( WEST ) ) { - line( vertexConsumer, transform, 0, 0, 0, UP ); + line( vertexConsumer, transform, normal, 0, 0, 0, UP ); } if( faces.contains( SOUTH ) || faces.contains( WEST ) ) { - line( vertexConsumer, transform, 0, 0, 1, UP ); + line( vertexConsumer, transform, normal, 0, 0, 1, UP ); } if( faces.contains( NORTH ) || faces.contains( EAST ) ) { - line( vertexConsumer, transform, 1, 0, 0, UP ); + line( vertexConsumer, transform, normal, 1, 0, 0, UP ); } if( faces.contains( SOUTH ) || faces.contains( EAST ) ) { - line( vertexConsumer, transform, 1, 0, 1, UP ); + line( vertexConsumer, transform, normal, 1, 0, 1, UP ); } if( faces.contains( NORTH ) || faces.contains( DOWN ) ) { - line( vertexConsumer, transform, 0, 0, 0, EAST ); + line( vertexConsumer, transform, normal, 0, 0, 0, EAST ); } if( faces.contains( SOUTH ) || faces.contains( DOWN ) ) { - line( vertexConsumer, transform, 0, 0, 1, EAST ); + line( vertexConsumer, transform, normal, 0, 0, 1, EAST ); } if( faces.contains( NORTH ) || faces.contains( UP ) ) { - line( vertexConsumer, transform, 0, 1, 0, EAST ); + line( vertexConsumer, transform, normal, 0, 1, 0, EAST ); } if( faces.contains( SOUTH ) || faces.contains( UP ) ) { - line( vertexConsumer, transform, 0, 1, 1, EAST ); + line( vertexConsumer, transform, normal, 0, 1, 1, EAST ); } if( faces.contains( WEST ) || faces.contains( DOWN ) ) { - line( vertexConsumer, transform, 0, 0, 0, SOUTH ); + line( vertexConsumer, transform, normal, 0, 0, 0, SOUTH ); } if( faces.contains( EAST ) || faces.contains( DOWN ) ) { - line( vertexConsumer, transform, 1, 0, 0, SOUTH ); + line( vertexConsumer, transform, normal, 1, 0, 0, SOUTH ); } if( faces.contains( WEST ) || faces.contains( UP ) ) { - line( vertexConsumer, transform, 0, 1, 0, SOUTH ); + line( vertexConsumer, transform, normal, 0, 1, 0, SOUTH ); } if( faces.contains( EAST ) || faces.contains( UP ) ) { - line( vertexConsumer, transform, 1, 1, 0, SOUTH ); + line( vertexConsumer, transform, normal, 1, 1, 0, SOUTH ); } matrixStack.pop(); @@ -141,13 +134,15 @@ public final class MonitorHighlightRenderer return true; } - private static void line( VertexConsumer buffer, Matrix4f transform, float x, float y, float z, Direction direction ) + private static void line( VertexConsumer buffer, Matrix4f transform, Matrix3f normal, float x, float y, float z, Direction direction ) { buffer.vertex( transform, x, y, z ) .color( 0, 0, 0, 0.4f ) + .normal( normal, direction.getOffsetX(), direction.getOffsetY(), direction.getOffsetZ() ) .next(); buffer.vertex( transform, x + direction.getOffsetX(), y + direction.getOffsetY(), z + direction.getOffsetZ() ) .color( 0, 0, 0, 0.4f ) + .normal( normal, direction.getOffsetX(), direction.getOffsetY(), direction.getOffsetZ() ) .next(); } } diff --git a/src/main/java/dan200/computercraft/client/render/MonitorTextureBufferShader.java b/src/main/java/dan200/computercraft/client/render/MonitorTextureBufferShader.java index 405f056b8..bd14234a0 100644 --- a/src/main/java/dan200/computercraft/client/render/MonitorTextureBufferShader.java +++ b/src/main/java/dan200/computercraft/client/render/MonitorTextureBufferShader.java @@ -5,180 +5,107 @@ */ package dan200.computercraft.client.render; -import com.google.common.base.Strings; -import com.mojang.blaze3d.platform.GlStateManager; -import com.mojang.blaze3d.systems.RenderSystem; -import dan200.computercraft.ComputerCraft; import dan200.computercraft.client.gui.FixedWidthFontRenderer; import dan200.computercraft.shared.util.Palette; -import net.minecraft.client.texture.TextureUtil; -import net.minecraft.util.math.Matrix4f; -import org.lwjgl.BufferUtils; +import net.minecraft.client.gl.GlUniform; +import net.minecraft.client.render.Shader; +import net.minecraft.client.render.VertexFormat; +import net.minecraft.resource.ResourceFactory; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.lwjgl.opengl.GL13; -import org.lwjgl.opengl.GL20; -import java.io.InputStream; +import javax.annotation.Nullable; +import java.io.IOException; import java.nio.FloatBuffer; -class MonitorTextureBufferShader +public class MonitorTextureBufferShader extends Shader { static final int TEXTURE_INDEX = GL13.GL_TEXTURE3; - private static final FloatBuffer MATRIX_BUFFER = BufferUtils.createFloatBuffer( 16 ); - private static final FloatBuffer PALETTE_BUFFER = BufferUtils.createFloatBuffer( 16 * 3 ); + private static final Logger LOGGER = LogManager.getLogger(); - private static int uniformMv; + private final GlUniform palette; + private final GlUniform width; + private final GlUniform height; - private static int uniformFont; - private static int uniformWidth; - private static int uniformHeight; - private static int uniformTbo; - private static int uniformPalette; - - private static boolean initialised; - private static boolean ok; - private static int program; - - static void setupUniform( Matrix4f transform, int width, int height, Palette palette, boolean greyscale ) + public MonitorTextureBufferShader( ResourceFactory factory, String name, VertexFormat format ) throws IOException { - MATRIX_BUFFER.rewind(); - transform.writeColumnMajor( MATRIX_BUFFER ); - MATRIX_BUFFER.rewind(); - RenderSystem.glUniformMatrix4( uniformMv, false, MATRIX_BUFFER ); + super( factory, name, format ); - RenderSystem.glUniform1i( uniformWidth, width ); - RenderSystem.glUniform1i( uniformHeight, height ); + width = getUniformChecked( "Width" ); + height = getUniformChecked( "Height" ); + palette = new GlUniform( "Palette", GlUniform.field_32044 /* UT_FLOAT3 */, 16 * 3, this ); + updateUniformLocation( palette ); - PALETTE_BUFFER.rewind(); + GlUniform tbo = getUniformChecked( "Tbo" ); + if( tbo != null ) tbo.set( TEXTURE_INDEX - GL13.GL_TEXTURE0 ); + } + + void setupUniform( int width, int height, Palette palette, boolean greyscale ) + { + if( this.width != null ) this.width.set( width ); + if( this.height != null ) this.height.set( height ); + setupPalette( palette, greyscale ); + } + + private void setupPalette( Palette palette, boolean greyscale ) + { + if( this.palette == null ) return; + + FloatBuffer paletteBuffer = this.palette.getFloatData(); + paletteBuffer.rewind(); for( int i = 0; i < 16; i++ ) { double[] colour = palette.getColour( i ); if( greyscale ) { float f = FixedWidthFontRenderer.toGreyscale( colour ); - PALETTE_BUFFER.put( f ) - .put( f ) - .put( f ); + paletteBuffer.put( f ).put( f ).put( f ); } else { - PALETTE_BUFFER.put( (float) colour[0] ) - .put( (float) colour[1] ) - .put( (float) colour[2] ); + paletteBuffer.put( (float) colour[0] ).put( (float) colour[1] ).put( (float) colour[2] ); } } - PALETTE_BUFFER.flip(); - RenderSystem.glUniform3( uniformPalette, PALETTE_BUFFER ); - } - - static boolean use() - { - if( initialised ) - { - if( ok ) - { - GlStateManager.useProgram( program ); - } - return ok; - } - - if( ok = load() ) - { - GL20.glUseProgram( program ); - RenderSystem.glUniform1i( uniformFont, 0 ); - RenderSystem.glUniform1i( uniformTbo, TEXTURE_INDEX - GL13.GL_TEXTURE0 ); - } - - return ok; - } - - private static boolean load() - { - initialised = true; - - try - { - int vertexShader = loadShader( GL20.GL_VERTEX_SHADER, "assets/computercraft/shaders/monitor.vert" ); - int fragmentShader = loadShader( GL20.GL_FRAGMENT_SHADER, "assets/computercraft/shaders/monitor.frag" ); - - program = GlStateManager.createProgram(); - GlStateManager.attachShader( program, vertexShader ); - GlStateManager.attachShader( program, fragmentShader ); - GL20.glBindAttribLocation( program, 0, "v_pos" ); - - GlStateManager.linkProgram( program ); - boolean ok = GlStateManager.getProgram( program, GL20.GL_LINK_STATUS ) != 0; - String log = GlStateManager.getProgramInfoLog( program, Short.MAX_VALUE ) - .trim(); - if( !Strings.isNullOrEmpty( log ) ) - { - ComputerCraft.log.warn( "Problems when linking monitor shader: {}", log ); - } - - GL20.glDetachShader( program, vertexShader ); - GL20.glDetachShader( program, fragmentShader ); - GlStateManager.deleteShader( vertexShader ); - GlStateManager.deleteShader( fragmentShader ); - - if( !ok ) - { - return false; - } - - uniformMv = getUniformLocation( program, "u_mv" ); - uniformFont = getUniformLocation( program, "u_font" ); - uniformWidth = getUniformLocation( program, "u_width" ); - uniformHeight = getUniformLocation( program, "u_height" ); - uniformTbo = getUniformLocation( program, "u_tbo" ); - uniformPalette = getUniformLocation( program, "u_palette" ); - - ComputerCraft.log.info( "Loaded monitor shader." ); - return true; - } - catch( Exception e ) - { - ComputerCraft.log.error( "Cannot load monitor shaders", e ); - return false; - } } - private static int loadShader( int kind, String path ) + @Override + public void bind() { - InputStream stream = TileEntityMonitorRenderer.class.getClassLoader() - .getResourceAsStream( path ); - if( stream == null ) - { - throw new IllegalArgumentException( "Cannot find " + path ); - } - String contents = TextureUtil.readAllToString( stream ); - - int shader = GlStateManager.createShader( kind ); - - GlStateManager.shaderSource( shader, contents ); - GlStateManager.compileShader( shader ); - - boolean ok = GlStateManager.getShader( shader, GL20.GL_COMPILE_STATUS ) != 0; - String log = GlStateManager.getShaderInfoLog( shader, Short.MAX_VALUE ) - .trim(); - if( !Strings.isNullOrEmpty( log ) ) - { - ComputerCraft.log.warn( "Problems when loading monitor shader {}: {}", path, log ); - } - - if( !ok ) - { - throw new IllegalStateException( "Cannot compile shader " + path ); - } - return shader; + super.bind(); + palette.upload(); } - private static int getUniformLocation( int program, String name ) + @Override + public void close() { - int uniform = GlStateManager.getUniformLocation( program, name ); - if( uniform == -1 ) + palette.close(); + super.close(); + } + + private void updateUniformLocation( GlUniform uniform ) + { + int id = GlUniform.getUniformLocation( getProgramRef(), uniform.getName() ); + if( id == -1 ) { - throw new IllegalStateException( "Cannot find uniform " + name ); + LOGGER.warn( "Shader {} could not find uniform named {} in the specified shader program.", getName(), uniform.getName() ); } + else + { + uniform.setLoc( id ); + } + } + + @Nullable + private GlUniform getUniformChecked( String name ) + { + GlUniform uniform = getUniform( name ); + if( uniform == null ) + { + LOGGER.warn( "Monitor shader {} should have uniform {}, but it was not present.", getName(), name ); + } + return uniform; } } diff --git a/src/main/java/dan200/computercraft/client/render/PrintoutRenderer.java b/src/main/java/dan200/computercraft/client/render/PrintoutRenderer.java index 489506aae..7b8c809a5 100644 --- a/src/main/java/dan200/computercraft/client/render/PrintoutRenderer.java +++ b/src/main/java/dan200/computercraft/client/render/PrintoutRenderer.java @@ -9,10 +9,9 @@ package dan200.computercraft.client.render; import dan200.computercraft.client.gui.FixedWidthFontRenderer; import dan200.computercraft.core.terminal.TextBuffer; import dan200.computercraft.shared.util.Palette; -import net.minecraft.client.render.*; -import net.minecraft.util.Identifier; +import net.minecraft.client.render.VertexConsumer; +import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.util.math.Matrix4f; -import org.lwjgl.opengl.GL11; import static dan200.computercraft.client.gui.FixedWidthFontRenderer.FONT_HEIGHT; import static dan200.computercraft.shared.media.items.ItemPrintout.LINES_PER_PAGE; @@ -39,7 +38,6 @@ public final class PrintoutRenderer * Size of the leather cover. */ public static final int COVER_SIZE = 12; - private static final Identifier BG = new Identifier( "computercraft", "textures/gui/printout.png" ); private static final float BG_SIZE = 256.0f; /** * Width of the extra page texture. @@ -50,9 +48,9 @@ public final class PrintoutRenderer private PrintoutRenderer() {} - public static void drawText( Matrix4f transform, VertexConsumerProvider renderer, int x, int y, int start, TextBuffer[] text, TextBuffer[] colours ) + public static void drawText( Matrix4f transform, VertexConsumerProvider renderer, int x, int y, int start, int light, TextBuffer[] text, TextBuffer[] colours ) { - VertexConsumer buffer = renderer.getBuffer( FixedWidthFontRenderer.TYPE ); + VertexConsumer buffer = renderer.getBuffer( RenderTypes.PRINTOUT_TEXT ); for( int line = 0; line < LINES_PER_PAGE && line < text.length; line++ ) { FixedWidthFontRenderer.drawString( transform, @@ -65,13 +63,14 @@ public final class PrintoutRenderer Palette.DEFAULT, false, 0, - 0 ); + 0, + light ); } } - public static void drawText( Matrix4f transform, VertexConsumerProvider renderer, int x, int y, int start, String[] text, String[] colours ) + public static void drawText( Matrix4f transform, VertexConsumerProvider renderer, int x, int y, int start, int light, String[] text, String[] colours ) { - VertexConsumer buffer = renderer.getBuffer( FixedWidthFontRenderer.TYPE ); + VertexConsumer buffer = renderer.getBuffer( RenderTypes.PRINTOUT_TEXT ); for( int line = 0; line < LINES_PER_PAGE && line < text.length; line++ ) { FixedWidthFontRenderer.drawString( transform, @@ -84,16 +83,17 @@ public final class PrintoutRenderer Palette.DEFAULT, false, 0, - 0 ); + 0, + light ); } } - public static void drawBorder( Matrix4f transform, VertexConsumerProvider renderer, float x, float y, float z, int page, int pages, boolean isBook ) + public static void drawBorder( Matrix4f transform, VertexConsumerProvider renderer, float x, float y, float z, int page, int pages, boolean isBook, int light ) { int leftPages = page; int rightPages = pages - page - 1; - VertexConsumer buffer = renderer.getBuffer( Type.TYPE ); + VertexConsumer buffer = renderer.getBuffer( RenderTypes.PRINTOUT_BACKGROUND ); if( isBook ) { @@ -103,8 +103,8 @@ public final class PrintoutRenderer float right = x + X_SIZE + offset - 4; // Left and right border - drawTexture( transform, buffer, left - 4, y - 8, z - 0.02f, COVER_X, 0, COVER_SIZE, Y_SIZE + COVER_SIZE * 2 ); - drawTexture( transform, buffer, right, y - 8, z - 0.02f, COVER_X + COVER_SIZE, 0, COVER_SIZE, Y_SIZE + COVER_SIZE * 2 ); + drawTexture( transform, buffer, left - 4, y - 8, z - 0.02f, COVER_X, 0, COVER_SIZE, Y_SIZE + COVER_SIZE * 2, light ); + drawTexture( transform, buffer, right, y - 8, z - 0.02f, COVER_X + COVER_SIZE, 0, COVER_SIZE, Y_SIZE + COVER_SIZE * 2, light ); // Draw centre panel (just stretched texture, sorry). drawTexture( transform, @@ -117,34 +117,35 @@ public final class PrintoutRenderer COVER_X + COVER_SIZE / 2.0f, COVER_SIZE, COVER_SIZE, - Y_SIZE ); + Y_SIZE, + light ); float borderX = left; while( borderX < right ) { double thisWidth = Math.min( right - borderX, X_SIZE ); - drawTexture( transform, buffer, borderX, y - 8, z - 0.02f, 0, COVER_Y, (float) thisWidth, COVER_SIZE ); - drawTexture( transform, buffer, borderX, y + Y_SIZE - 4, z - 0.02f, 0, COVER_Y + COVER_SIZE, (float) thisWidth, COVER_SIZE ); + drawTexture( transform, buffer, borderX, y - 8, z - 0.02f, 0, COVER_Y, (float) thisWidth, COVER_SIZE, light ); + drawTexture( transform, buffer, borderX, y + Y_SIZE - 4, z - 0.02f, 0, COVER_Y + COVER_SIZE, (float) thisWidth, COVER_SIZE, light ); borderX += thisWidth; } } // Left half - drawTexture( transform, buffer, x, y, z, X_FOLD_SIZE * 2, 0, X_SIZE / 2.0f, Y_SIZE ); + drawTexture( transform, buffer, x, y, z, X_FOLD_SIZE * 2, 0, X_SIZE / 2.0f, Y_SIZE, light ); for( int n = 0; n <= leftPages; n++ ) { drawTexture( transform, buffer, x - offsetAt( n ), y, z - 1e-3f * n, // Use the left "bold" fold for the outermost page - n == leftPages ? 0 : X_FOLD_SIZE, 0, X_FOLD_SIZE, Y_SIZE ); + n == leftPages ? 0 : X_FOLD_SIZE, 0, X_FOLD_SIZE, Y_SIZE, light ); } // Right half - drawTexture( transform, buffer, x + X_SIZE / 2.0f, y, z, X_FOLD_SIZE * 2 + X_SIZE / 2.0f, 0, X_SIZE / 2.0f, Y_SIZE ); + drawTexture( transform, buffer, x + X_SIZE / 2.0f, y, z, X_FOLD_SIZE * 2 + X_SIZE / 2.0f, 0, X_SIZE / 2.0f, Y_SIZE, light ); for( int n = 0; n <= rightPages; n++ ) { drawTexture( transform, buffer, x + (X_SIZE - X_FOLD_SIZE) + offsetAt( n ), y, z - 1e-3f * n, // Two folds, then the main page. Use the right "bold" fold for the outermost page. - X_FOLD_SIZE * 2 + X_SIZE + (n == rightPages ? X_FOLD_SIZE : 0), 0, X_FOLD_SIZE, Y_SIZE ); + X_FOLD_SIZE * 2 + X_SIZE + (n == rightPages ? X_FOLD_SIZE : 0), 0, X_FOLD_SIZE, Y_SIZE, light ); } } @@ -153,57 +154,25 @@ public final class PrintoutRenderer return (float) (32 * (1 - Math.pow( 1.2, -page ))); } - private static void drawTexture( Matrix4f matrix, VertexConsumer buffer, float x, float y, float z, float u, float v, float width, float height ) + private static void drawTexture( Matrix4f matrix, VertexConsumer buffer, float x, float y, float z, float u, float v, float width, float height, int light ) { - buffer.vertex( matrix, x, y + height, z ) - .texture( u / BG_SIZE, (v + height) / BG_SIZE ) - .next(); - buffer.vertex( matrix, x + width, y + height, z ) - .texture( (u + width) / BG_SIZE, (v + height) / BG_SIZE ) - .next(); - buffer.vertex( matrix, x + width, y, z ) - .texture( (u + width) / BG_SIZE, v / BG_SIZE ) - .next(); - buffer.vertex( matrix, x, y, z ) - .texture( u / BG_SIZE, v / BG_SIZE ) - .next(); + vertex( buffer, matrix, x, y + height, z, u / BG_SIZE, (v + height) / BG_SIZE, light ); + vertex( buffer, matrix, x + width, y + height, z, (u + width) / BG_SIZE, (v + height) / BG_SIZE, light ); + vertex( buffer, matrix, x + width, y, z, (u + width) / BG_SIZE, v / BG_SIZE, light ); + vertex( buffer, matrix, x, y, z, u / BG_SIZE, v / BG_SIZE, light ); } private static void drawTexture( Matrix4f matrix, VertexConsumer buffer, float x, float y, float z, float width, float height, float u, float v, - float tWidth, float tHeight ) + float tWidth, float tHeight, int light ) { - buffer.vertex( matrix, x, y + height, z ) - .texture( u / BG_SIZE, (v + tHeight) / BG_SIZE ) - .next(); - buffer.vertex( matrix, x + width, y + height, z ) - .texture( (u + tWidth) / BG_SIZE, (v + tHeight) / BG_SIZE ) - .next(); - buffer.vertex( matrix, x + width, y, z ) - .texture( (u + tWidth) / BG_SIZE, v / BG_SIZE ) - .next(); - buffer.vertex( matrix, x, y, z ) - .texture( u / BG_SIZE, v / BG_SIZE ) - .next(); + vertex( buffer, matrix, x, y + height, z, u / BG_SIZE, (v + tHeight) / BG_SIZE, light ); + vertex( buffer, matrix, x + width, y + height, z, (u + tWidth) / BG_SIZE, (v + tHeight) / BG_SIZE, light ); + vertex( buffer, matrix, x + width, y, z, (u + tWidth) / BG_SIZE, v / BG_SIZE, light ); + vertex( buffer, matrix, x, y, z, u / BG_SIZE, v / BG_SIZE, light ); } - private static final class Type extends RenderPhase + private static void vertex( VertexConsumer buffer, Matrix4f matrix, float x, float y, float z, float u, float v, int light ) { - static final RenderLayer TYPE = RenderLayer.of( "printout_background", - VertexFormats.POSITION_TEXTURE, - GL11.GL_QUADS, - 1024, - false, - false, - // useDelegate, needsSorting - RenderLayer.MultiPhaseParameters.builder() - .texture( new RenderPhase.Texture( BG, false, false ) ) // blur, minimap - .alpha( ONE_TENTH_ALPHA ) - .lightmap( DISABLE_LIGHTMAP ) - .build( false ) ); - - private Type( String name, Runnable setup, Runnable destroy ) - { - super( name, setup, destroy ); - } + buffer.vertex( matrix, x, y, z ).color( 255, 255, 255, 255 ).texture( u, v ).light( light ).next(); } } diff --git a/src/main/java/dan200/computercraft/client/render/RenderTypes.java b/src/main/java/dan200/computercraft/client/render/RenderTypes.java new file mode 100644 index 000000000..cabc1c870 --- /dev/null +++ b/src/main/java/dan200/computercraft/client/render/RenderTypes.java @@ -0,0 +1,117 @@ +/* + * 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.client.gui.FixedWidthFontRenderer; +import net.minecraft.client.render.*; +import net.minecraft.util.Identifier; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class RenderTypes +{ + + public static final int FULL_BRIGHT_LIGHTMAP = (0xF << 4) | (0xF << 20); + + @Nullable + public static MonitorTextureBufferShader monitorTboShader; + + @Nullable + public static Shader terminalShader; + + public static final RenderLayer TERMINAL_WITHOUT_DEPTH = Types.TERMINAL_WITHOUT_DEPTH; + public static final RenderLayer MONITOR_TBO = Types.MONITOR_TBO; + public static final RenderLayer TERMINAL_BLOCKER = Types.BLOCKER; + public static final RenderLayer TERMINAL_WITH_DEPTH = Types.TERMINAL_WITH_DEPTH; + public static final RenderLayer PRINTOUT_TEXT = Types.PRINTOUT_TEXT; + + public static final RenderLayer PRINTOUT_BACKGROUND = RenderLayer.getText( new Identifier( "computercraft", "textures/gui/printout.png" ) ); + + public static final RenderLayer POSITION_COLOR = Types.POSITION_COLOR; + + @Nonnull + static MonitorTextureBufferShader getMonitorTextureBufferShader() + { + if( monitorTboShader == null ) throw new NullPointerException( "MonitorTboShader has not been registered" ); + return monitorTboShader; + } + + @Nonnull + static Shader getTerminalShader() + { + if( terminalShader == null ) throw new NullPointerException( "MonitorTboShader has not been registered" ); + return terminalShader; + } + + private static final class Types extends RenderPhase + { + private static final VertexFormat.DrawMode GL_MODE = VertexFormat.DrawMode.TRIANGLES; + private static final VertexFormat FORMAT = VertexFormats.POSITION_COLOR_TEXTURE; + private static final Shader TERM_SHADER = new Shader( RenderTypes::getTerminalShader ); + + private static final RenderPhase.Texture TERM_FONT_TEXTURE = new RenderPhase.Texture( + FixedWidthFontRenderer.FONT, + false, false // blur, minimap + ); + + public static final RenderLayer MONITOR_TBO = RenderLayer.of( "monitor_tbo", VertexFormats.POSITION_TEXTURE, VertexFormat.DrawMode.TRIANGLE_STRIP, 128, false, false, // useDelegate, needsSorting + RenderLayer.MultiPhaseParameters.builder() + .texture( TERM_FONT_TEXTURE ) // blur, minimap + .shader( new RenderPhase.Shader( RenderTypes::getMonitorTextureBufferShader ) ) + .writeMaskState( RenderLayer.ALL_MASK ) + .build( false ) ); + + static final RenderLayer TERMINAL_WITHOUT_DEPTH = RenderLayer.of( + "terminal_without_depth", FORMAT, GL_MODE, 1024, + false, false, // useDelegate, needsSorting + RenderLayer.MultiPhaseParameters.builder() + .texture( TERM_FONT_TEXTURE ) + .shader( TERM_SHADER ) + .writeMaskState( COLOR_MASK ) + .build( false ) + ); + + static final RenderLayer BLOCKER = RenderLayer.of( "terminal_blocker", FORMAT, GL_MODE, 256, false, false, // useDelegate, needsSorting + RenderLayer.MultiPhaseParameters.builder() + .texture( TERM_FONT_TEXTURE ) + .shader( TERM_SHADER ) + .writeMaskState( DEPTH_MASK ) + .build( false ) ); + + static final RenderLayer TERMINAL_WITH_DEPTH = RenderLayer.of( + "terminal_with_depth", FORMAT, GL_MODE, 1024, + false, false, // useDelegate, needsSorting + RenderLayer.MultiPhaseParameters.builder() + .texture( TERM_FONT_TEXTURE ) + .shader( TERM_SHADER ) + .build( false ) + ); + + static final RenderLayer PRINTOUT_TEXT = RenderLayer.of( + "printout_text", VertexFormats.POSITION_COLOR_TEXTURE_LIGHT, GL_MODE, 1024, + false, false, // useDelegate, needsSorting + RenderLayer.MultiPhaseParameters.builder() + .texture( TERM_FONT_TEXTURE ) + .shader( RenderPhase.TEXT_SHADER ) + .lightmap( RenderPhase.ENABLE_LIGHTMAP ) + .build( false ) + ); + + static final RenderLayer POSITION_COLOR = RenderLayer.of( + "position_color", VertexFormats.POSITION_COLOR, VertexFormat.DrawMode.QUADS, 128, + false, false, // useDelegate, needsSorting + RenderLayer.MultiPhaseParameters.builder() + .shader( COLOR_SHADER ) + .build( false ) + ); + + private Types( String name, Runnable setup, Runnable destroy ) + { + super( name, setup, destroy ); + } + } +} diff --git a/src/main/java/dan200/computercraft/client/render/TileEntityMonitorRenderer.java b/src/main/java/dan200/computercraft/client/render/TileEntityMonitorRenderer.java index bc7ce88d0..6c8c0ab74 100644 --- a/src/main/java/dan200/computercraft/client/render/TileEntityMonitorRenderer.java +++ b/src/main/java/dan200/computercraft/client/render/TileEntityMonitorRenderer.java @@ -7,6 +7,8 @@ package dan200.computercraft.client.render; import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.systems.RenderSystem; +import dan200.computercraft.ComputerCraft; import dan200.computercraft.client.FrameInfo; import dan200.computercraft.client.gui.FixedWidthFontRenderer; import dan200.computercraft.core.terminal.Terminal; @@ -18,17 +20,11 @@ import dan200.computercraft.shared.util.Colour; import dan200.computercraft.shared.util.DirectionUtil; import net.minecraft.client.gl.VertexBuffer; import net.minecraft.client.render.*; -import net.minecraft.client.render.block.entity.BlockEntityRenderDispatcher; import net.minecraft.client.render.block.entity.BlockEntityRenderer; -import net.minecraft.client.util.GlAllocationUtils; +import net.minecraft.client.render.block.entity.BlockEntityRendererFactory; import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.util.math.AffineTransformation; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Direction; -import net.minecraft.util.math.Matrix4f; -import net.minecraft.util.math.Vec3f; +import net.minecraft.util.math.*; import org.lwjgl.opengl.GL11; -import org.lwjgl.opengl.GL13; import org.lwjgl.opengl.GL20; import org.lwjgl.opengl.GL31; @@ -36,8 +32,9 @@ import javax.annotation.Nonnull; import java.nio.ByteBuffer; import static dan200.computercraft.client.gui.FixedWidthFontRenderer.*; +import static net.minecraft.client.util.GlAllocationUtils.allocateByteBuffer; -public class TileEntityMonitorRenderer extends BlockEntityRenderer +public class TileEntityMonitorRenderer implements BlockEntityRenderer { /** * {@link TileMonitor#RENDER_MARGIN}, but a tiny bit of additional padding to ensure that there is no space between the monitor frame and contents. @@ -47,11 +44,10 @@ public class TileEntityMonitorRenderer extends BlockEntityRenderer .getMatrix(); private static ByteBuffer tboContents; - public TileEntityMonitorRenderer( BlockEntityRenderDispatcher rendererDispatcher ) + public TileEntityMonitorRenderer( BlockEntityRendererFactory.Context context ) { - super( rendererDispatcher ); + // super( context ); } - @Override public void render( @Nonnull TileMonitor monitor, float partialTicks, @Nonnull MatrixStack transform, @Nonnull VertexConsumerProvider renderer, int lightmapCoord, int overlayLight ) @@ -97,17 +93,6 @@ public class TileEntityMonitorRenderer extends BlockEntityRenderer double xSize = origin.getWidth() - 2.0 * (TileMonitor.RENDER_MARGIN + TileMonitor.RENDER_BORDER); double ySize = origin.getHeight() - 2.0 * (TileMonitor.RENDER_MARGIN + TileMonitor.RENDER_BORDER); - // Draw the background blocker - FixedWidthFontRenderer.drawBlocker( transform.peek().getModel(), - renderer, - (float) -TileMonitor.RENDER_MARGIN, - (float) TileMonitor.RENDER_MARGIN, - (float) (xSize + 2 * TileMonitor.RENDER_MARGIN), - (float) -(ySize + TileMonitor.RENDER_MARGIN * 2) ); - - // Set the contents slightly off the surface to prevent z-fighting - transform.translate( 0.0, 0.0, 0.001 ); - // Draw the contents Terminal terminal = originTerminal.getTerminal(); if( terminal != null ) @@ -122,23 +107,22 @@ public class TileEntityMonitorRenderer extends BlockEntityRenderer Matrix4f matrix = transform.peek().getModel(); - // Sneaky hack here: we get a buffer now in order to flush existing ones and set up the appropriate - // render state. I've no clue how well this'll work in future versions of Minecraft, but it does the trick - // for now. - VertexConsumer buffer = renderer.getBuffer( FixedWidthFontRenderer.TYPE ); - FixedWidthFontRenderer.TYPE.startDrawing(); - - renderTerminal( matrix, originTerminal, (float) (MARGIN / xScale), (float) (MARGIN / yScale) ); + renderTerminal( renderer, matrix, originTerminal, (float) (MARGIN / xScale), (float) (MARGIN / yScale) ); // We don't draw the cursor with the VBO, as it's dynamic and so we'll end up refreshing far more than is // reasonable. - FixedWidthFontRenderer.drawCursor( matrix, buffer, 0, 0, terminal, !originTerminal.isColour() ); - - // To go along with sneaky hack above: make sure state changes are undone. I would have thought this would - // happen automatically after these buffers are drawn, but chests will render weird around monitors without this. - FixedWidthFontRenderer.TYPE.endDrawing(); + FixedWidthFontRenderer.drawCursor( matrix, renderer.getBuffer( RenderTypes.TERMINAL_WITHOUT_DEPTH ), 0, 0, terminal, !originTerminal.isColour() ); transform.pop(); + + // Draw the background blocker + FixedWidthFontRenderer.drawBlocker( + transform.peek().getModel(), renderer, + -MARGIN, MARGIN, + (float) (xSize + 2 * MARGIN), (float) -(ySize + MARGIN * 2) + ); + + renderer.getBuffer( RenderLayer.getSolid() ); } else { @@ -154,7 +138,7 @@ public class TileEntityMonitorRenderer extends BlockEntityRenderer transform.pop(); } - private static void renderTerminal( Matrix4f matrix, ClientMonitor monitor, float xMargin, float yMargin ) + private static void renderTerminal( VertexConsumerProvider renderer, Matrix4f matrix, ClientMonitor monitor, float xMargin, float yMargin ) { Terminal terminal = monitor.getTerminal(); @@ -169,10 +153,6 @@ public class TileEntityMonitorRenderer extends BlockEntityRenderer { case TBO: { - if( !MonitorTextureBufferShader.use() ) - { - return; - } int width = terminal.getWidth(), height = terminal.getHeight(); int pixelWidth = width * FONT_WIDTH, pixelHeight = height * FONT_HEIGHT; @@ -182,7 +162,7 @@ public class TileEntityMonitorRenderer extends BlockEntityRenderer int size = width * height * 3; if( tboContents == null || tboContents.capacity() < size ) { - tboContents = GlAllocationUtils.allocateByteBuffer( size ); + tboContents = allocateByteBuffer( size ); } ByteBuffer monitorBuffer = tboContents; @@ -199,32 +179,27 @@ public class TileEntityMonitorRenderer extends BlockEntityRenderer } monitorBuffer.flip(); - GlStateManager.bindBuffers( GL31.GL_TEXTURE_BUFFER, monitor.tboBuffer ); - GlStateManager.bufferData( GL31.GL_TEXTURE_BUFFER, monitorBuffer, GL20.GL_STATIC_DRAW ); - GlStateManager.bindBuffers( GL31.GL_TEXTURE_BUFFER, 0 ); + GlStateManager._glBindBuffer( GL31.GL_TEXTURE_BUFFER, monitor.tboBuffer ); + GlStateManager._glBufferData( GL31.GL_TEXTURE_BUFFER, monitorBuffer, GL20.GL_STATIC_DRAW ); + GlStateManager._glBindBuffer( GL31.GL_TEXTURE_BUFFER, 0 ); } // Nobody knows what they're doing! - GlStateManager.activeTexture( MonitorTextureBufferShader.TEXTURE_INDEX ); + int active = GlStateManager._getActiveTexture(); + RenderSystem.activeTexture( MonitorTextureBufferShader.TEXTURE_INDEX ); GL11.glBindTexture( GL31.GL_TEXTURE_BUFFER, monitor.tboTexture ); - GlStateManager.activeTexture( GL13.GL_TEXTURE0 ); + RenderSystem.activeTexture( active ); - MonitorTextureBufferShader.setupUniform( matrix, width, height, terminal.getPalette(), !monitor.isColour() ); + MonitorTextureBufferShader shader = RenderTypes.getMonitorTextureBufferShader(); + shader.setupUniform( width, height, terminal.getPalette(), !monitor.isColour() ); - Tessellator tessellator = Tessellator.getInstance(); - BufferBuilder buffer = tessellator.getBuffer(); - buffer.begin( GL11.GL_TRIANGLE_STRIP, VertexFormats.POSITION ); - buffer.vertex( -xMargin, -yMargin, 0 ) - .next(); - buffer.vertex( -xMargin, pixelHeight + yMargin, 0 ) - .next(); - buffer.vertex( pixelWidth + xMargin, -yMargin, 0 ) - .next(); - buffer.vertex( pixelWidth + xMargin, pixelHeight + yMargin, 0 ) - .next(); - tessellator.draw(); + VertexConsumer buffer = renderer.getBuffer( RenderTypes.MONITOR_TBO ); + tboVertex( buffer, matrix, -xMargin, -yMargin ); + tboVertex( buffer, matrix, -xMargin, pixelHeight + yMargin ); + tboVertex( buffer, matrix, pixelWidth + xMargin, -yMargin ); + tboVertex( buffer, matrix, pixelWidth + xMargin, pixelHeight + yMargin ); - GlStateManager.useProgram( 0 ); + renderer.getBuffer( RenderTypes.TERMINAL_WITHOUT_DEPTH ); break; } @@ -234,7 +209,7 @@ public class TileEntityMonitorRenderer extends BlockEntityRenderer { Tessellator tessellator = Tessellator.getInstance(); BufferBuilder builder = tessellator.getBuffer(); - builder.begin( FixedWidthFontRenderer.TYPE.getDrawMode(), FixedWidthFontRenderer.TYPE.getVertexFormat() ); + builder.begin( RenderTypes.TERMINAL_WITHOUT_DEPTH.getDrawMode(), RenderTypes.TERMINAL_WITHOUT_DEPTH.getVertexFormat() ); FixedWidthFontRenderer.drawTerminalWithoutCursor( IDENTITY, builder, 0, @@ -250,14 +225,23 @@ public class TileEntityMonitorRenderer extends BlockEntityRenderer vbo.upload( builder ); } - vbo.bind(); - FixedWidthFontRenderer.TYPE.getVertexFormat() - .startDrawing( 0L ); - vbo.draw( matrix, FixedWidthFontRenderer.TYPE.getDrawMode() ); - VertexBuffer.unbind(); - FixedWidthFontRenderer.TYPE.getVertexFormat() - .endDrawing(); + renderer.getBuffer( RenderTypes.TERMINAL_WITHOUT_DEPTH ); + + RenderTypes.TERMINAL_WITHOUT_DEPTH.startDrawing(); + vbo.setShader( matrix, RenderSystem.getProjectionMatrix(), RenderTypes.getTerminalShader() ); break; } } + + private static void tboVertex( VertexConsumer builder, Matrix4f matrix, float x, float y ) + { + // We encode position in the UV, as that's not transformed by the matrix. + builder.vertex( matrix, x, y, 0 ).texture( x, y ).next(); + } + + @Override + public int getRenderDistance() + { + return ComputerCraft.monitorDistance; + } } diff --git a/src/main/java/dan200/computercraft/client/render/TileEntityTurtleRenderer.java b/src/main/java/dan200/computercraft/client/render/TileEntityTurtleRenderer.java index 67ad4c76a..70284b245 100644 --- a/src/main/java/dan200/computercraft/client/render/TileEntityTurtleRenderer.java +++ b/src/main/java/dan200/computercraft/client/render/TileEntityTurtleRenderer.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.client.render; import dan200.computercraft.api.client.TransformedModel; @@ -21,11 +20,13 @@ import net.minecraft.client.render.VertexConsumer; import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.block.entity.BlockEntityRenderDispatcher; import net.minecraft.client.render.block.entity.BlockEntityRenderer; +import net.minecraft.client.render.block.entity.BlockEntityRendererFactory; import net.minecraft.client.render.model.BakedModel; import net.minecraft.client.render.model.BakedModelManager; import net.minecraft.client.render.model.BakedQuad; import net.minecraft.client.util.ModelIdentifier; import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.math.Vec3f; import net.minecraft.util.Identifier; import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.hit.HitResult; @@ -37,7 +38,7 @@ import javax.annotation.Nonnull; import java.util.List; import java.util.Random; -public class TileEntityTurtleRenderer extends BlockEntityRenderer +public class TileEntityTurtleRenderer implements BlockEntityRenderer { private static final ModelIdentifier NORMAL_TURTLE_MODEL = new ModelIdentifier( "computercraft:turtle_normal", "inventory" ); private static final ModelIdentifier ADVANCED_TURTLE_MODEL = new ModelIdentifier( "computercraft:turtle_advanced", "inventory" ); @@ -46,9 +47,11 @@ public class TileEntityTurtleRenderer extends BlockEntityRenderer private final Random random = new Random( 0 ); - public TileEntityTurtleRenderer( BlockEntityRenderDispatcher renderDispatcher ) + BlockEntityRenderDispatcher renderer; + + public TileEntityTurtleRenderer( BlockEntityRendererFactory.Context context ) { - super( renderDispatcher ); + renderer = context.getRenderDispatcher(); } public static ModelIdentifier getTurtleModel( ComputerFamily family, boolean coloured ) @@ -76,6 +79,118 @@ public class TileEntityTurtleRenderer extends BlockEntityRenderer return null; } + @Override + public void render( @Nonnull TileTurtle turtle, float partialTicks, @Nonnull MatrixStack transform, @Nonnull VertexConsumerProvider buffers, + int lightmapCoord, int overlayLight ) + { + // Render the label + String label = turtle.createProxy() + .getLabel(); + HitResult hit = renderer.crosshairTarget; + if( label != null && hit.getType() == HitResult.Type.BLOCK && turtle.getPos() + .equals( ((BlockHitResult) hit).getBlockPos() ) ) + { + MinecraftClient mc = MinecraftClient.getInstance(); + TextRenderer font = mc.textRenderer; + + transform.push(); + transform.translate( 0.5, 1.2, 0.5 ); + transform.multiply( mc.getEntityRenderDispatcher() + .getRotation() ); + transform.scale( -0.025f, -0.025f, 0.025f ); + + Matrix4f matrix = transform.peek() + .getModel(); + int opacity = (int) (mc.options.getTextBackgroundOpacity( 0.25f ) * 255) << 24; + float width = -font.getWidth( label ) / 2.0f; + font.draw( label, width, (float) 0, 0x20ffffff, false, matrix, buffers, true, opacity, lightmapCoord ); + font.draw( label, width, (float) 0, 0xffffffff, false, matrix, buffers, false, 0, lightmapCoord ); + + transform.pop(); + } + + transform.push(); + + // Setup the transform. + Vec3d offset = turtle.getRenderOffset( partialTicks ); + float yaw = turtle.getRenderYaw( partialTicks ); + transform.translate( offset.x, offset.y, offset.z ); + + transform.translate( 0.5f, 0.5f, 0.5f ); + transform.multiply( Vec3f.POSITIVE_Y.getDegreesQuaternion( 180.0f - yaw ) ); + if( label != null && (label.equals( "Dinnerbone" ) || label.equals( "Grumm" )) ) + { + // Flip the model + transform.scale( 1.0f, -1.0f, 1.0f ); + } + transform.translate( -0.5f, -0.5f, -0.5f ); + + // Render the turtle + int colour = turtle.getColour(); + ComputerFamily family = turtle.getFamily(); + Identifier overlay = turtle.getOverlay(); + + VertexConsumer buffer = buffers.getBuffer( TexturedRenderLayers.getEntityTranslucentCull() ); + renderModel( transform, buffer, lightmapCoord, overlayLight, getTurtleModel( family, colour != -1 ), colour == -1 ? null : new int[] { colour } ); + + // Render the overlay + ModelIdentifier overlayModel = getTurtleOverlayModel( overlay, HolidayUtil.getCurrentHoliday() == Holiday.CHRISTMAS ); + if( overlayModel != null ) + { + renderModel( transform, buffer, lightmapCoord, overlayLight, overlayModel, null ); + } + + // Render the upgrades + renderUpgrade( transform, buffer, lightmapCoord, overlayLight, turtle, TurtleSide.LEFT, partialTicks ); + renderUpgrade( transform, buffer, lightmapCoord, overlayLight, turtle, TurtleSide.RIGHT, partialTicks ); + + transform.pop(); + } + + private void renderUpgrade( @Nonnull MatrixStack transform, @Nonnull VertexConsumer renderer, int lightmapCoord, int overlayLight, TileTurtle turtle, + TurtleSide side, float f ) + { + ITurtleUpgrade upgrade = turtle.getUpgrade( side ); + if( upgrade == null ) + { + return; + } + transform.push(); + + float toolAngle = turtle.getToolRenderAngle( side, f ); + transform.translate( 0.0f, 0.5f, 0.5f ); + transform.multiply( Vec3f.NEGATIVE_X.getDegreesQuaternion( toolAngle ) ); + transform.translate( 0.0f, -0.5f, -0.5f ); + + TransformedModel model = upgrade.getModel( turtle.getAccess(), side ); + model.push( transform ); + renderModel( transform, renderer, lightmapCoord, overlayLight, model.getModel(), null ); + transform.pop(); + + transform.pop(); + } + + private void renderModel( @Nonnull MatrixStack transform, @Nonnull VertexConsumer renderer, int lightmapCoord, int overlayLight, + ModelIdentifier modelLocation, int[] tints ) + { + BakedModelManager modelManager = MinecraftClient.getInstance() + .getItemRenderer() + .getModels() + .getModelManager(); + renderModel( transform, renderer, lightmapCoord, overlayLight, modelManager.getModel( modelLocation ), tints ); + } + + private void renderModel( @Nonnull MatrixStack transform, @Nonnull VertexConsumer renderer, int lightmapCoord, int overlayLight, BakedModel model, + int[] tints ) + { + random.setSeed( 0 ); + renderQuads( transform, renderer, lightmapCoord, overlayLight, model.getQuads( null, null, random ), tints ); + for( Direction facing : DirectionUtil.FACINGS ) + { + renderQuads( transform, renderer, lightmapCoord, overlayLight, model.getQuads( null, facing, random ), tints ); + } + } + private static void renderQuads( @Nonnull MatrixStack transform, @Nonnull VertexConsumer buffer, int lightmapCoord, int overlayLight, List quads, int[] tints ) { @@ -107,117 +222,4 @@ public class TileEntityTurtleRenderer extends BlockEntityRenderer true ); } } - - @Override - public void render( @Nonnull TileTurtle turtle, float partialTicks, @Nonnull MatrixStack transform, @Nonnull VertexConsumerProvider renderer, - int lightmapCoord, int overlayLight ) - { - // Render the label - String label = turtle.createProxy() - .getLabel(); - HitResult hit = dispatcher.crosshairTarget; - if( label != null && hit.getType() == HitResult.Type.BLOCK && turtle.getPos() - .equals( ((BlockHitResult) hit).getBlockPos() ) ) - { - MinecraftClient mc = MinecraftClient.getInstance(); - TextRenderer font = mc.textRenderer; - - transform.push(); - transform.translate( 0.5, 1.2, 0.5 ); - transform.multiply( mc.getEntityRenderDispatcher() - .getRotation() ); - transform.scale( -0.025f, -0.025f, 0.025f ); - - Matrix4f matrix = transform.peek() - .getModel(); - int opacity = (int) (mc.options.getTextBackgroundOpacity( 0.25f ) * 255) << 24; - float width = -font.getWidth( label ) / 2.0f; - font.draw( label, width, (float) 0, 0x20ffffff, false, matrix, renderer, true, opacity, lightmapCoord ); - font.draw( label, width, (float) 0, 0xffffffff, false, matrix, renderer, false, 0, lightmapCoord ); - - transform.pop(); - } - - transform.push(); - - // Setup the transform. - Vec3d offset = turtle.getRenderOffset( partialTicks ); - float yaw = turtle.getRenderYaw( partialTicks ); - transform.translate( offset.x, offset.y, offset.z ); - - transform.translate( 0.5f, 0.5f, 0.5f ); - transform.multiply( Vec3f.POSITIVE_Y.getDegreesQuaternion( 180.0f - yaw ) ); - if( label != null && (label.equals( "Dinnerbone" ) || label.equals( "Grumm" )) ) - { - // Flip the model - transform.scale( 1.0f, -1.0f, 1.0f ); - } - transform.translate( -0.5f, -0.5f, -0.5f ); - - // Render the turtle - int colour = turtle.getColour(); - ComputerFamily family = turtle.getFamily(); - Identifier overlay = turtle.getOverlay(); - - VertexConsumer buffer = renderer.getBuffer( TexturedRenderLayers.getEntityTranslucentCull() ); - renderModel( transform, buffer, lightmapCoord, overlayLight, getTurtleModel( family, colour != -1 ), colour == -1 ? null : new int[] { colour } ); - - // Render the overlay - ModelIdentifier overlayModel = getTurtleOverlayModel( overlay, HolidayUtil.getCurrentHoliday() == Holiday.CHRISTMAS ); - if( overlayModel != null ) - { - renderModel( transform, buffer, lightmapCoord, overlayLight, overlayModel, null ); - } - - // Render the upgrades - renderUpgrade( transform, buffer, lightmapCoord, overlayLight, turtle, TurtleSide.LEFT, partialTicks ); - renderUpgrade( transform, buffer, lightmapCoord, overlayLight, turtle, TurtleSide.RIGHT, partialTicks ); - - transform.pop(); - } - - public static void renderUpgrade( @Nonnull MatrixStack transform, @Nonnull VertexConsumer renderer, int lightmapCoord, int overlayLight, TileTurtle turtle, - TurtleSide side, float f ) - { - ITurtleUpgrade upgrade = turtle.getUpgrade( side ); - if( upgrade == null ) - { - return; - } - transform.push(); - - float toolAngle = turtle.getToolRenderAngle( side, f ); - transform.translate( 0.0f, 0.5f, 0.5f ); - transform.multiply( Vec3f.NEGATIVE_X.getDegreesQuaternion( toolAngle ) ); - transform.translate( 0.0f, -0.5f, -0.5f ); - - TransformedModel model = upgrade.getModel( turtle.getAccess(), side ); - model.push( transform ); - TileEntityTurtleRenderer.renderModel( transform, renderer, lightmapCoord, overlayLight, model.getModel(), null ); - transform.pop(); - - transform.pop(); - } - - public static void renderModel( @Nonnull MatrixStack transform, @Nonnull VertexConsumer renderer, int lightmapCoord, int overlayLight, - ModelIdentifier modelLocation, int[] tints ) - { - BakedModelManager modelManager = MinecraftClient.getInstance() - .getItemRenderer() - .getModels() - .getModelManager(); - renderModel( transform, renderer, lightmapCoord, overlayLight, modelManager.getModel( modelLocation ), tints ); - } - - public static void renderModel( @Nonnull MatrixStack transform, @Nonnull VertexConsumer renderer, int lightmapCoord, int overlayLight, BakedModel model, - int[] tints ) - { - Random random = new Random(); - random.setSeed( 0 ); - renderQuads( transform, renderer, lightmapCoord, overlayLight, model.getQuads( null, null, random ), tints ); - for( Direction facing : DirectionUtil.FACINGS ) - { - renderQuads( transform, renderer, lightmapCoord, overlayLight, model.getQuads( null, facing, random ), tints ); - } - } } diff --git a/src/main/java/dan200/computercraft/client/render/TurtleMultiModel.java b/src/main/java/dan200/computercraft/client/render/TurtleMultiModel.java index f6803af3e..bc7a6e46b 100644 --- a/src/main/java/dan200/computercraft/client/render/TurtleMultiModel.java +++ b/src/main/java/dan200/computercraft/client/render/TurtleMultiModel.java @@ -30,7 +30,7 @@ public class TurtleMultiModel implements BakedModel private final TransformedModel leftUpgradeModel; private final TransformedModel rightUpgradeModel; private List generalQuads = null; - private Map> faceQuads = new EnumMap<>( Direction.class ); + private final Map> faceQuads = new EnumMap<>( Direction.class ); public TurtleMultiModel( BakedModel baseModel, BakedModel overlayModel, AffineTransformation generalTransform, TransformedModel leftUpgradeModel, TransformedModel rightUpgradeModel ) @@ -120,9 +120,9 @@ public class TurtleMultiModel implements BakedModel @Nonnull @Override @Deprecated - public Sprite getSprite() + public Sprite getParticleSprite() { - return baseModel.getSprite(); + return baseModel.getParticleSprite(); } @Nonnull diff --git a/src/main/java/dan200/computercraft/client/render/TurtlePlayerRenderer.java b/src/main/java/dan200/computercraft/client/render/TurtlePlayerRenderer.java index 636cc4b4d..5b113f97a 100644 --- a/src/main/java/dan200/computercraft/client/render/TurtlePlayerRenderer.java +++ b/src/main/java/dan200/computercraft/client/render/TurtlePlayerRenderer.java @@ -6,25 +6,19 @@ package dan200.computercraft.client.render; import dan200.computercraft.shared.turtle.core.TurtlePlayer; -import net.fabricmc.fabric.api.client.rendereregistry.v1.EntityRendererRegistry; import net.minecraft.client.render.VertexConsumerProvider; -import net.minecraft.client.render.entity.EntityRenderDispatcher; import net.minecraft.client.render.entity.EntityRenderer; +import net.minecraft.client.render.entity.EntityRendererFactory; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.util.Identifier; import javax.annotation.Nonnull; public class TurtlePlayerRenderer extends EntityRenderer -{ - public TurtlePlayerRenderer( EntityRenderDispatcher renderManager ) +{ //FIXME Make sure this isn't an issue. Context was EntityRenderDispatcher. + public TurtlePlayerRenderer( EntityRendererFactory.Context context ) { - super( renderManager ); - } - - public TurtlePlayerRenderer( EntityRenderDispatcher entityRenderDispatcher, EntityRendererRegistry.Context context ) - { - super( entityRenderDispatcher ); + super( context ); } @Override diff --git a/src/main/java/dan200/computercraft/client/render/TurtleSmartItemModel.java b/src/main/java/dan200/computercraft/client/render/TurtleSmartItemModel.java index e632e4840..dc5ebc934 100644 --- a/src/main/java/dan200/computercraft/client/render/TurtleSmartItemModel.java +++ b/src/main/java/dan200/computercraft/client/render/TurtleSmartItemModel.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.client.render; import com.google.common.base.Objects; @@ -24,6 +23,7 @@ import net.minecraft.client.render.model.json.ModelOverrideList; import net.minecraft.client.render.model.json.ModelTransformation; import net.minecraft.client.texture.Sprite; import net.minecraft.client.util.ModelIdentifier; +import net.minecraft.util.math.AffineTransformation; import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.world.ClientWorld; import net.minecraft.entity.LivingEntity; @@ -55,8 +55,60 @@ public class TurtleSmartItemModel implements BakedModel .getModel() ); } + private static class TurtleModelCombination + { + final boolean colour; + final ITurtleUpgrade leftUpgrade; + final ITurtleUpgrade rightUpgrade; + final Identifier overlay; + final boolean christmas; + final boolean flip; + + TurtleModelCombination( boolean colour, ITurtleUpgrade leftUpgrade, ITurtleUpgrade rightUpgrade, Identifier overlay, boolean christmas, + boolean flip ) + { + this.colour = colour; + this.leftUpgrade = leftUpgrade; + this.rightUpgrade = rightUpgrade; + this.overlay = overlay; + this.christmas = christmas; + this.flip = flip; + } + + @Override + public boolean equals( Object other ) + { + if( other == this ) + { + return true; + } + if( !(other instanceof TurtleModelCombination otherCombo) ) + { + return false; + } + + return otherCombo.colour == colour && otherCombo.leftUpgrade == leftUpgrade && otherCombo.rightUpgrade == rightUpgrade && Objects.equal( + otherCombo.overlay, overlay ) && otherCombo.christmas == christmas && otherCombo.flip == flip; + } + + @Override + public int hashCode() + { + final int prime = 31; + int result = 0; + result = prime * result + (colour ? 1 : 0); + result = prime * result + (leftUpgrade != null ? leftUpgrade.hashCode() : 0); + result = prime * result + (rightUpgrade != null ? rightUpgrade.hashCode() : 0); + result = prime * result + (overlay != null ? overlay.hashCode() : 0); + result = prime * result + (christmas ? 1 : 0); + result = prime * result + (flip ? 1 : 0); + return result; + } + } + private final BakedModel familyModel; private final BakedModel colourModel; + private final HashMap cachedModels = new HashMap<>(); private final ModelOverrideList overrides; @@ -70,8 +122,7 @@ public class TurtleSmartItemModel implements BakedModel { @Nonnull @Override - public BakedModel apply( @Nonnull BakedModel originalModel, @Nonnull ItemStack stack, @Nullable ClientWorld world, - @Nullable LivingEntity entity ) + public BakedModel apply( BakedModel originalModel, ItemStack stack, @Nullable ClientWorld world, @Nullable LivingEntity entity, int seed ) { ItemTurtle turtle = (ItemTurtle) stack.getItem(); int colour = turtle.getColour( stack ); @@ -80,9 +131,7 @@ public class TurtleSmartItemModel implements BakedModel Identifier overlay = turtle.getOverlay( stack ); boolean christmas = HolidayUtil.getCurrentHoliday() == Holiday.CHRISTMAS; String label = turtle.getLabel( stack ); - // TODO make upside down turtle items render properly (currently inivisible) - //boolean flip = label != null && (label.equals("Dinnerbone") || label.equals("Grumm")); - boolean flip = false; + boolean flip = label != null && (label.equals( "Dinnerbone" ) || label.equals( "Grumm" )); TurtleModelCombination combo = new TurtleModelCombination( colour != -1, leftUpgrade, rightUpgrade, overlay, christmas, flip ); BakedModel model = cachedModels.get( combo ); @@ -143,12 +192,18 @@ public class TurtleSmartItemModel implements BakedModel return familyModel.isBuiltin(); } - @Nonnull @Override @Deprecated - public Sprite getSprite() + public Sprite getParticleSprite() { - return familyModel.getSprite(); + return familyModel.getParticleSprite(); + } + + @Nonnull + @Override + public ModelOverrideList getOverrides() + { + return overrides; } @Nonnull @@ -159,63 +214,4 @@ public class TurtleSmartItemModel implements BakedModel return familyModel.getTransformation(); } - @Nonnull - @Override - public ModelOverrideList getOverrides() - { - return overrides; - } - - private static class TurtleModelCombination - { - final boolean colour; - final ITurtleUpgrade leftUpgrade; - final ITurtleUpgrade rightUpgrade; - final Identifier overlay; - final boolean christmas; - final boolean flip; - - TurtleModelCombination( boolean colour, ITurtleUpgrade leftUpgrade, ITurtleUpgrade rightUpgrade, Identifier overlay, boolean christmas, - boolean flip ) - { - this.colour = colour; - this.leftUpgrade = leftUpgrade; - this.rightUpgrade = rightUpgrade; - this.overlay = overlay; - this.christmas = christmas; - this.flip = flip; - } - - @Override - public int hashCode() - { - final int prime = 31; - int result = 0; - result = prime * result + (colour ? 1 : 0); - result = prime * result + (leftUpgrade != null ? leftUpgrade.hashCode() : 0); - result = prime * result + (rightUpgrade != null ? rightUpgrade.hashCode() : 0); - result = prime * result + (overlay != null ? overlay.hashCode() : 0); - result = prime * result + (christmas ? 1 : 0); - result = prime * result + (flip ? 1 : 0); - return result; - } - - @Override - public boolean equals( Object other ) - { - if( other == this ) - { - return true; - } - if( !(other instanceof TurtleModelCombination) ) - { - return false; - } - - TurtleModelCombination otherCombo = (TurtleModelCombination) other; - return otherCombo.colour == colour && otherCombo.leftUpgrade == leftUpgrade && otherCombo.rightUpgrade == rightUpgrade && Objects.equal( - otherCombo.overlay, overlay ) && otherCombo.christmas == christmas && otherCombo.flip == flip; - } - } - } diff --git a/src/main/java/dan200/computercraft/core/apis/ComputerAccess.java b/src/main/java/dan200/computercraft/core/apis/ComputerAccess.java index 39c4a2ff3..77203c656 100644 --- a/src/main/java/dan200/computercraft/core/apis/ComputerAccess.java +++ b/src/main/java/dan200/computercraft/core/apis/ComputerAccess.java @@ -5,6 +5,7 @@ */ package dan200.computercraft.core.apis; +import dan200.computercraft.ComputerCraft; import dan200.computercraft.api.filesystem.IMount; import dan200.computercraft.api.filesystem.IWritableMount; import dan200.computercraft.api.peripheral.IComputerAccess; @@ -30,6 +31,10 @@ public abstract class ComputerAccess implements IComputerAccess public void unmountAll() { FileSystem fileSystem = environment.getFileSystem(); + if( !mounts.isEmpty() ) + { + ComputerCraft.log.warn( "Peripheral or API called mount but did not call unmount for {}", mounts ); + } for( String mount : mounts ) { fileSystem.unmount( mount ); diff --git a/src/main/java/dan200/computercraft/core/apis/IAPIEnvironment.java b/src/main/java/dan200/computercraft/core/apis/IAPIEnvironment.java index 25960c580..1f6ffe08a 100644 --- a/src/main/java/dan200/computercraft/core/apis/IAPIEnvironment.java +++ b/src/main/java/dan200/computercraft/core/apis/IAPIEnvironment.java @@ -62,6 +62,7 @@ public interface IAPIEnvironment @Nullable IPeripheral getPeripheral( ComputerSide side ); + @Nullable String getLabel(); void setLabel( @Nullable String label ); diff --git a/src/main/java/dan200/computercraft/core/apis/handles/HandleGeneric.java b/src/main/java/dan200/computercraft/core/apis/handles/HandleGeneric.java index fc1954354..e799c37cd 100644 --- a/src/main/java/dan200/computercraft/core/apis/handles/HandleGeneric.java +++ b/src/main/java/dan200/computercraft/core/apis/handles/HandleGeneric.java @@ -96,9 +96,8 @@ public abstract class HandleGeneric protected static SeekableByteChannel asSeekable( Channel channel ) { - if( !(channel instanceof SeekableByteChannel) ) return null; + if( !(channel instanceof SeekableByteChannel seekable) ) return null; - SeekableByteChannel seekable = (SeekableByteChannel) channel; try { seekable.position( seekable.position() ); diff --git a/src/main/java/dan200/computercraft/core/apis/http/NetworkUtils.java b/src/main/java/dan200/computercraft/core/apis/http/NetworkUtils.java index cbbaf20c5..ffd895b0b 100644 --- a/src/main/java/dan200/computercraft/core/apis/http/NetworkUtils.java +++ b/src/main/java/dan200/computercraft/core/apis/http/NetworkUtils.java @@ -20,6 +20,8 @@ import io.netty.handler.codec.http.websocketx.WebSocketHandshakeException; import io.netty.handler.ssl.SslContext; import io.netty.handler.ssl.SslContextBuilder; import io.netty.handler.timeout.ReadTimeoutException; +import io.netty.handler.traffic.AbstractTrafficShapingHandler; +import io.netty.handler.traffic.GlobalTrafficShapingHandler; import javax.annotation.Nonnull; import javax.net.ssl.SSLException; @@ -28,9 +30,7 @@ import javax.net.ssl.TrustManagerFactory; import java.net.InetSocketAddress; import java.net.URI; import java.security.KeyStore; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.SynchronousQueue; -import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** @@ -38,10 +38,8 @@ import java.util.concurrent.TimeUnit; */ public final class NetworkUtils { - public static final ExecutorService EXECUTOR = new ThreadPoolExecutor( - 4, Integer.MAX_VALUE, - 60L, TimeUnit.SECONDS, - new SynchronousQueue<>(), + public static final ScheduledThreadPoolExecutor EXECUTOR = new ScheduledThreadPoolExecutor( + 4, ThreadUtils.builder( "Network" ) .setPriority( Thread.MIN_PRIORITY + (Thread.NORM_PRIORITY - Thread.MIN_PRIORITY) / 2 ) .build() @@ -52,6 +50,15 @@ public final class NetworkUtils .build() ); + public static final AbstractTrafficShapingHandler SHAPING_HANDLER = new GlobalTrafficShapingHandler( + EXECUTOR, ComputerCraft.httpUploadBandwidth, ComputerCraft.httpDownloadBandwidth + ); + + static + { + EXECUTOR.setKeepAliveTime( 60, TimeUnit.SECONDS ); + } + private NetworkUtils() { } @@ -107,8 +114,18 @@ public final class NetworkUtils } } + public static void reloadConfig() + { + SHAPING_HANDLER.configure( ComputerCraft.httpUploadBandwidth, ComputerCraft.httpDownloadBandwidth ); + } + + public static void reset() + { + SHAPING_HANDLER.trafficCounter().resetCumulativeTime(); + } + /** - * Create a {@link InetSocketAddress} from a {@link URI}. + * Create a {@link InetSocketAddress} from a {@link java.net.URI}. * * Note, this may require a DNS lookup, and so should not be executed on the main CC thread. * @@ -184,7 +201,7 @@ public final class NetworkUtils { return "Timed out"; } - else if( cause instanceof SSLHandshakeException || (cause instanceof DecoderException && cause.getCause() instanceof SSLHandshakeException) ) + else if( cause instanceof SSLHandshakeException || cause instanceof DecoderException && cause.getCause() instanceof SSLHandshakeException ) { return "Could not create a secure connection"; } diff --git a/src/main/java/dan200/computercraft/core/apis/http/options/AddressRule.java b/src/main/java/dan200/computercraft/core/apis/http/options/AddressRule.java index 955e89d4d..13365b453 100644 --- a/src/main/java/dan200/computercraft/core/apis/http/options/AddressRule.java +++ b/src/main/java/dan200/computercraft/core/apis/http/options/AddressRule.java @@ -75,7 +75,7 @@ public final class AddressRule if( this.port != null && this.port != port ) return false; return predicate.matches( domain ) || predicate.matches( address ) - || (ipv4Address != null && predicate.matches( ipv4Address )); + || ipv4Address != null && predicate.matches( ipv4Address ); } public static Options apply( Iterable rules, String domain, InetSocketAddress socketAddress ) diff --git a/src/main/java/dan200/computercraft/core/apis/http/request/HttpRequest.java b/src/main/java/dan200/computercraft/core/apis/http/request/HttpRequest.java index 2049575ff..74957b204 100644 --- a/src/main/java/dan200/computercraft/core/apis/http/request/HttpRequest.java +++ b/src/main/java/dan200/computercraft/core/apis/http/request/HttpRequest.java @@ -167,6 +167,7 @@ public class HttpRequest extends Resource } ChannelPipeline p = ch.pipeline(); + p.addLast( NetworkUtils.SHAPING_HANDLER ); if( sslContext != null ) { p.addLast( sslContext.newHandler( ch.alloc(), uri.getHost(), socketAddress.getPort() ) ); diff --git a/src/main/java/dan200/computercraft/core/apis/http/request/HttpRequestHandler.java b/src/main/java/dan200/computercraft/core/apis/http/request/HttpRequestHandler.java index 4ef42ff44..003b3da5f 100644 --- a/src/main/java/dan200/computercraft/core/apis/http/request/HttpRequestHandler.java +++ b/src/main/java/dan200/computercraft/core/apis/http/request/HttpRequestHandler.java @@ -100,9 +100,8 @@ public final class HttpRequestHandler extends SimpleChannelInboundHandler 0 ) { @@ -137,9 +136,8 @@ public final class HttpRequestHandler extends SimpleChannelInboundHandler protected void initChannel( SocketChannel ch ) { ChannelPipeline p = ch.pipeline(); + p.addLast( NetworkUtils.SHAPING_HANDLER ); if( sslContext != null ) { p.addLast( sslContext.newHandler( ch.alloc(), uri.getHost(), socketAddress.getPort() ) ); } + String subprotocol = headers.get( HttpHeaderNames.SEC_WEBSOCKET_PROTOCOL ); WebSocketClientHandshaker handshaker = WebSocketClientHandshakerFactory.newHandshaker( - uri, WebSocketVersion.V13, null, true, headers, + uri, WebSocketVersion.V13, subprotocol, true, headers, options.websocketMessage <= 0 ? MAX_MESSAGE_SIZE : options.websocketMessage ); p.addLast( new HttpClientCodec(), new HttpObjectAggregator( 8192 ), - WebSocketClientCompressionHandler.INSTANCE, + WebsocketCompressionHandler.INSTANCE, new WebsocketHandler( Websocket.this, handshaker, options ) ); } diff --git a/src/main/java/dan200/computercraft/core/apis/http/websocket/WebsocketCompressionHandler.java b/src/main/java/dan200/computercraft/core/apis/http/websocket/WebsocketCompressionHandler.java new file mode 100644 index 000000000..e29a87837 --- /dev/null +++ b/src/main/java/dan200/computercraft/core/apis/http/websocket/WebsocketCompressionHandler.java @@ -0,0 +1,38 @@ +/* + * 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.apis.http.websocket; + +import io.netty.channel.ChannelHandler; +import io.netty.handler.codec.compression.ZlibCodecFactory; +import io.netty.handler.codec.http.websocketx.extensions.WebSocketClientExtensionHandler; +import io.netty.handler.codec.http.websocketx.extensions.compression.DeflateFrameClientExtensionHandshaker; +import io.netty.handler.codec.http.websocketx.extensions.compression.PerMessageDeflateClientExtensionHandshaker; +import io.netty.handler.codec.http.websocketx.extensions.compression.WebSocketClientCompressionHandler; + +import static io.netty.handler.codec.http.websocketx.extensions.compression.PerMessageDeflateServerExtensionHandshaker.MAX_WINDOW_SIZE; + +/** + * An alternative to {@link WebSocketClientCompressionHandler} which supports the {@literal client_no_context_takeover} + * extension. Makes CC slightly more flexible. + */ +@ChannelHandler.Sharable +final class WebsocketCompressionHandler extends WebSocketClientExtensionHandler +{ + public static final WebsocketCompressionHandler INSTANCE = new WebsocketCompressionHandler(); + + private WebsocketCompressionHandler() + { + super( + new PerMessageDeflateClientExtensionHandshaker( + 6, ZlibCodecFactory.isSupportingWindowSizeAndMemLevel(), MAX_WINDOW_SIZE, + true, false + ), + new DeflateFrameClientExtensionHandshaker( false ), + new DeflateFrameClientExtensionHandshaker( true ) + ); + + } +} diff --git a/src/main/java/dan200/computercraft/core/apis/http/websocket/WebsocketHandler.java b/src/main/java/dan200/computercraft/core/apis/http/websocket/WebsocketHandler.java index 4916f5fbd..25001e19c 100644 --- a/src/main/java/dan200/computercraft/core/apis/http/websocket/WebsocketHandler.java +++ b/src/main/java/dan200/computercraft/core/apis/http/websocket/WebsocketHandler.java @@ -55,9 +55,8 @@ public class WebsocketHandler extends SimpleChannelInboundHandler return; } - if( msg instanceof FullHttpResponse ) + if( msg instanceof FullHttpResponse response ) { - FullHttpResponse response = (FullHttpResponse) msg; throw new IllegalStateException( "Unexpected FullHttpResponse (getStatus=" + response.status() + ", content=" + response.content().toString( CharsetUtil.UTF_8 ) + ')' ); } @@ -76,9 +75,8 @@ public class WebsocketHandler extends SimpleChannelInboundHandler websocket.environment().addTrackingChange( TrackingField.WEBSOCKET_INCOMING, converted.length ); websocket.environment().queueEvent( MESSAGE_EVENT, websocket.address(), converted, true ); } - else if( frame instanceof CloseWebSocketFrame ) + else if( frame instanceof CloseWebSocketFrame closeFrame ) { - CloseWebSocketFrame closeFrame = (CloseWebSocketFrame) frame; websocket.close( closeFrame.statusCode(), closeFrame.reasonText() ); } else if( frame instanceof PingWebSocketFrame ) diff --git a/src/main/java/dan200/computercraft/core/asm/Reflect.java b/src/main/java/dan200/computercraft/core/asm/Reflect.java index 5504f3bc8..57a624683 100644 --- a/src/main/java/dan200/computercraft/core/asm/Reflect.java +++ b/src/main/java/dan200/computercraft/core/asm/Reflect.java @@ -18,7 +18,7 @@ import static org.objectweb.asm.Opcodes.ICONST_0; final class Reflect { - static final Type OPTIONAL_IN = Optional.class.getTypeParameters()[0]; + static final java.lang.reflect.Type OPTIONAL_IN = Optional.class.getTypeParameters()[0]; private Reflect() { @@ -52,12 +52,11 @@ final class Reflect { if( underlying instanceof Class ) return (Class) underlying; - if( underlying instanceof ParameterizedType ) + if( underlying instanceof ParameterizedType type ) { - ParameterizedType type = (ParameterizedType) underlying; if( !allowParameter ) { - for( Type arg : type.getActualTypeArguments() ) + for( java.lang.reflect.Type arg : type.getActualTypeArguments() ) { if( arg instanceof WildcardType ) continue; if( arg instanceof TypeVariable && ((TypeVariable) arg).getName().startsWith( "capture#" ) ) diff --git a/src/main/java/dan200/computercraft/core/computer/Environment.java b/src/main/java/dan200/computercraft/core/computer/Environment.java index 3931629f1..ecc8aea80 100644 --- a/src/main/java/dan200/computercraft/core/computer/Environment.java +++ b/src/main/java/dan200/computercraft/core/computer/Environment.java @@ -309,9 +309,9 @@ public final class Environment implements IAPIEnvironment { int index = side.ordinal(); IPeripheral existing = peripherals[index]; - if( (existing == null && peripheral != null) || - (existing != null && peripheral == null) || - (existing != null && !existing.equals( peripheral )) ) + if( existing == null && peripheral != null || + existing != null && peripheral == null || + existing != null && !existing.equals( peripheral ) ) { peripherals[index] = peripheral; if( peripheralListener != null ) peripheralListener.onPeripheralChanged( side, peripheral ); diff --git a/src/main/java/dan200/computercraft/core/filesystem/MountWrapper.java b/src/main/java/dan200/computercraft/core/filesystem/MountWrapper.java index 96b11f24f..0df456cc1 100644 --- a/src/main/java/dan200/computercraft/core/filesystem/MountWrapper.java +++ b/src/main/java/dan200/computercraft/core/filesystem/MountWrapper.java @@ -293,9 +293,8 @@ class MountWrapper private FileSystemException localExceptionOf( @Nullable String localPath, @Nonnull IOException e ) { - if( !location.isEmpty() && e instanceof FileOperationException ) + if( !location.isEmpty() && e instanceof FileOperationException ex ) { - FileOperationException ex = (FileOperationException) e; if( ex.getFilename() != null ) return localExceptionOf( ex.getFilename(), ex.getMessage() ); } diff --git a/src/main/java/dan200/computercraft/core/filesystem/ResourceMount.java b/src/main/java/dan200/computercraft/core/filesystem/ResourceMount.java index b31350f6b..4614fa471 100644 --- a/src/main/java/dan200/computercraft/core/filesystem/ResourceMount.java +++ b/src/main/java/dan200/computercraft/core/filesystem/ResourceMount.java @@ -17,10 +17,9 @@ import dan200.computercraft.shared.util.IoUtil; import net.minecraft.resource.ReloadableResourceManager; import net.minecraft.resource.Resource; import net.minecraft.resource.ResourceManager; -import net.minecraft.resource.ResourceReloader; +import net.minecraft.resource.SynchronousResourceReloader; import net.minecraft.util.Identifier; import net.minecraft.util.InvalidIdentifierException; -import net.minecraft.util.profiler.Profiler; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -30,8 +29,6 @@ import java.io.InputStream; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; public final class ResourceMount implements IMount @@ -114,6 +111,7 @@ public final class ResourceMount implements IMount existingNamespace = file.getNamespace(); if( !file.getNamespace().equals( namespace ) ) continue; + if( !FileSystem.contains( subPath, file.getPath() ) ) continue; // Some packs seem to include the parent? String localPath = FileSystem.toLocal( file.getPath(), subPath ); create( newRoot, localPath ); @@ -300,7 +298,7 @@ public final class ResourceMount implements IMount * 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. */ - static class Listener implements ResourceReloader + static class Listener implements SynchronousResourceReloader { private static final Listener INSTANCE = new Listener(); @@ -308,19 +306,9 @@ public final class ResourceMount implements IMount private final Set managers = Collections.newSetFromMap( new WeakHashMap<>() ); @Override - public CompletableFuture reload( Synchronizer synchronizer, ResourceManager manager, Profiler prepareProfiler, Profiler applyProfiler, Executor prepareExecutor, Executor applyExecutor ) + public void reload( @Nonnull ResourceManager manager ) { - return CompletableFuture.runAsync( () -> { - prepareProfiler.push( "Mount reloading" ); - try - { - for( ResourceMount mount : mounts ) mount.load(); - } - finally - { - prepareProfiler.pop(); - } - }, prepareExecutor ); + for( ResourceMount mount : mounts ) mount.load(); } synchronized void add( ReloadableResourceManager manager, ResourceMount mount ) diff --git a/src/main/java/dan200/computercraft/core/lua/CobaltLuaMachine.java b/src/main/java/dan200/computercraft/core/lua/CobaltLuaMachine.java index 82093db4e..f444729e2 100644 --- a/src/main/java/dan200/computercraft/core/lua/CobaltLuaMachine.java +++ b/src/main/java/dan200/computercraft/core/lua/CobaltLuaMachine.java @@ -97,7 +97,7 @@ public class CobaltLuaMachine implements ILuaMachine globals.load( state, new CoroutineLib() ); globals.load( state, new Bit32Lib() ); globals.load( state, new Utf8Lib() ); - if( ComputerCraft.debugEnable ) globals.load( state, new DebugLib() ); + globals.load( state, new DebugLib() ); // Remove globals we don't want to expose globals.rawset( "collectgarbage", Constants.NIL ); @@ -260,14 +260,12 @@ public class CobaltLuaMachine implements ILuaMachine if( object instanceof Number ) return valueOf( ((Number) object).doubleValue() ); if( object instanceof Boolean ) return valueOf( (Boolean) object ); if( object instanceof String ) return valueOf( object.toString() ); - if( object instanceof byte[] ) + if( object instanceof byte[] b ) { - byte[] b = (byte[]) object; return valueOf( Arrays.copyOf( b, b.length ) ); } - if( object instanceof ByteBuffer ) + if( object instanceof ByteBuffer b ) { - ByteBuffer b = (ByteBuffer) object; byte[] bytes = new byte[b.remaining()]; b.get( bytes ); return valueOf( bytes ); @@ -304,9 +302,8 @@ public class CobaltLuaMachine implements ILuaMachine return table; } - if( object instanceof Collection ) + if( object instanceof Collection objects ) { - Collection objects = (Collection) object; LuaTable table = new LuaTable( objects.size(), 0 ); values.put( object, table ); int i = 0; @@ -314,9 +311,8 @@ public class CobaltLuaMachine implements ILuaMachine return table; } - if( object instanceof Object[] ) + if( object instanceof Object[] objects ) { - Object[] objects = (Object[]) object; LuaTable table = new LuaTable( objects.length, 0 ); values.put( object, table ); for( int i = 0; i < objects.length; i++ ) table.rawset( i + 1, toValue( objects[i], values ) ); @@ -367,6 +363,7 @@ public class CobaltLuaMachine implements ILuaMachine case Constants.TSTRING: return value.toString(); case Constants.TTABLE: + { // Table: // Start remembering stuff if( objects == null ) @@ -408,6 +405,7 @@ public class CobaltLuaMachine implements ILuaMachine } } return table; + } default: return null; } diff --git a/src/main/java/dan200/computercraft/core/lua/LuaContext.java b/src/main/java/dan200/computercraft/core/lua/LuaContext.java index 626d15fad..0e5c792c8 100644 --- a/src/main/java/dan200/computercraft/core/lua/LuaContext.java +++ b/src/main/java/dan200/computercraft/core/lua/LuaContext.java @@ -9,6 +9,8 @@ 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; @@ -66,4 +68,11 @@ 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/core/terminal/Terminal.java b/src/main/java/dan200/computercraft/core/terminal/Terminal.java index 8215e4d19..56a7dc3e2 100644 --- a/src/main/java/dan200/computercraft/core/terminal/Terminal.java +++ b/src/main/java/dan200/computercraft/core/terminal/Terminal.java @@ -419,6 +419,7 @@ public class Terminal { if( c >= '0' && c <= '9' ) return c - '0'; if( c >= 'a' && c <= 'f' ) return c - 'a' + 10; + if( c >= 'A' && c <= 'F' ) return c - 'A' + 10; return 15 - def.ordinal(); } } diff --git a/src/main/java/dan200/computercraft/core/terminal/TextBuffer.java b/src/main/java/dan200/computercraft/core/terminal/TextBuffer.java index 1c399764b..6266b072a 100644 --- a/src/main/java/dan200/computercraft/core/terminal/TextBuffer.java +++ b/src/main/java/dan200/computercraft/core/terminal/TextBuffer.java @@ -79,6 +79,7 @@ public class TextBuffer } } + @Override public String toString() { return new String( text ); diff --git a/src/main/java/dan200/computercraft/fabric/mixin/AffineTransformationAccess.java b/src/main/java/dan200/computercraft/fabric/mixin/AffineTransformationAccess.java deleted file mode 100644 index ced81eaaa..000000000 --- a/src/main/java/dan200/computercraft/fabric/mixin/AffineTransformationAccess.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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.fabric.mixin; - -import net.minecraft.util.math.AffineTransformation; -import net.minecraft.util.math.Quaternion; -import net.minecraft.util.math.Vec3f; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -@Mixin( AffineTransformation.class ) -public interface AffineTransformationAccess -{ - @Accessor - Vec3f getTranslation(); - - @Accessor - Vec3f getScale(); - - @Accessor - Quaternion getRotation1(); -} diff --git a/src/main/java/dan200/computercraft/fabric/mixin/BakedQuadAccess.java b/src/main/java/dan200/computercraft/fabric/mixin/BakedQuadAccess.java deleted file mode 100644 index fa7541eca..000000000 --- a/src/main/java/dan200/computercraft/fabric/mixin/BakedQuadAccess.java +++ /dev/null @@ -1,18 +0,0 @@ -/* - * 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.fabric.mixin; - -import net.minecraft.client.render.model.BakedQuad; -import net.minecraft.client.texture.Sprite; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -@Mixin( BakedQuad.class ) -public interface BakedQuadAccess -{ - @Accessor - Sprite getSprite(); -} diff --git a/src/main/java/dan200/computercraft/fabric/mixin/MixinBlock.java b/src/main/java/dan200/computercraft/fabric/mixin/MixinBlock.java index 05ed3bde3..559c8faf7 100644 --- a/src/main/java/dan200/computercraft/fabric/mixin/MixinBlock.java +++ b/src/main/java/dan200/computercraft/fabric/mixin/MixinBlock.java @@ -7,13 +7,18 @@ package dan200.computercraft.fabric.mixin; import dan200.computercraft.shared.util.DropConsumer; import net.minecraft.block.Block; +import net.minecraft.entity.ItemEntity; import net.minecraft.item.ItemStack; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; + +import java.util.function.Supplier; + import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; /** * Captures block drops. @@ -23,12 +28,16 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin( Block.class ) public class MixinBlock { - @Inject( method = "dropStack", - at = @At( value = "INVOKE", target = "Lnet/minecraft/world/World;spawnEntity(Lnet/minecraft/entity/Entity;)Z" ), + @Inject( method = "dropStack(Lnet/minecraft/world/World;Ljava/util/function/Supplier;Lnet/minecraft/item/ItemStack;)V", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/entity/ItemEntity;setToDefaultPickupDelay()V" + ), + locals = LocalCapture.CAPTURE_FAILSOFT, cancellable = true ) - private static void dropStack( World world, BlockPos pos, ItemStack stack, CallbackInfo callbackInfo ) + private static void dropStack( World world, Supplier itemEntitySupplier, ItemStack stack, CallbackInfo callbackInfo, ItemEntity itemEntity ) { - if( DropConsumer.onHarvestDrops( world, pos, stack ) ) + if( DropConsumer.onHarvestDrops( world, itemEntity.getBlockPos(), stack ) ) { callbackInfo.cancel(); } diff --git a/src/main/java/dan200/computercraft/fabric/mixin/MixinGameRenderer.java b/src/main/java/dan200/computercraft/fabric/mixin/MixinGameRenderer.java new file mode 100644 index 000000000..7bb875fc3 --- /dev/null +++ b/src/main/java/dan200/computercraft/fabric/mixin/MixinGameRenderer.java @@ -0,0 +1,43 @@ +/* + * 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.fabric.mixin; + +import com.mojang.datafixers.util.Pair; +import dan200.computercraft.client.render.MonitorTextureBufferShader; +import dan200.computercraft.client.render.RenderTypes; +import net.minecraft.client.gl.Program; +import net.minecraft.client.render.GameRenderer; +import net.minecraft.client.render.Shader; +import net.minecraft.resource.ResourceManager; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +import java.io.IOException; +import java.util.List; +import java.util.function.Consumer; + +@Mixin( GameRenderer.class ) +public class MixinGameRenderer +{ + @Inject( method = "loadShaders", at = @At( value = "INVOKE_ASSIGN", target = "Ljava/util/List;add(Ljava/lang/Object;)Z", ordinal = 53 ), locals = LocalCapture.CAPTURE_FAILSOFT ) + private void loadShaders( ResourceManager manager, CallbackInfo info, List list, List>> list2 ) throws IOException + { + list2.add( Pair.of( new Shader( + manager, + "terminal", + RenderTypes.TERMINAL_WITHOUT_DEPTH.getVertexFormat() + ), shader -> RenderTypes.terminalShader = shader ) ); + list2.add( Pair.of( new MonitorTextureBufferShader( + manager, + "monitor_tbo", + RenderTypes.MONITOR_TBO.getVertexFormat() + ), shader -> RenderTypes.monitorTboShader = (MonitorTextureBufferShader) shader ) ); + } +} diff --git a/src/main/java/dan200/computercraft/fabric/mixin/MixinItemFrameEntityRenderer.java b/src/main/java/dan200/computercraft/fabric/mixin/MixinItemFrameEntityRenderer.java index 47364d858..92ba04c51 100644 --- a/src/main/java/dan200/computercraft/fabric/mixin/MixinItemFrameEntityRenderer.java +++ b/src/main/java/dan200/computercraft/fabric/mixin/MixinItemFrameEntityRenderer.java @@ -12,6 +12,7 @@ import net.fabricmc.api.Environment; import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.entity.ItemFrameEntityRenderer; import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.entity.EntityType; import net.minecraft.entity.decoration.ItemFrameEntity; import net.minecraft.item.ItemStack; import org.spongepowered.asm.mixin.Mixin; @@ -23,16 +24,27 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Environment( EnvType.CLIENT ) public class MixinItemFrameEntityRenderer { - @Inject( method = "render", at = @At( "HEAD" ), cancellable = true ) + @Inject( + method = "render", + at = @At( + value = "INVOKE", + target = "Lnet/minecraft/client/util/math/MatrixStack;multiply(Lnet/minecraft/util/math/Quaternion;)V", + ordinal = 2, + shift = At.Shift.AFTER + ), + cancellable = true ) private void renderItem( ItemFrameEntity itemFrameEntity, float f, float g, MatrixStack matrixStack, - VertexConsumerProvider vertexConsumerProvider, int i, CallbackInfo info + VertexConsumerProvider vertexConsumerProvider, int itemFrameEntityLight, CallbackInfo info ) { ItemStack stack = itemFrameEntity.getHeldItemStack(); if( stack.getItem() instanceof ItemPrintout ) { - ItemPrintoutRenderer.INSTANCE.renderInFrame( matrixStack, vertexConsumerProvider, stack ); + int light = itemFrameEntity.getType() == EntityType.GLOW_ITEM_FRAME ? 0xf000d2 : itemFrameEntityLight; // See getLightVal. + ItemPrintoutRenderer.INSTANCE.renderInFrame( matrixStack, vertexConsumerProvider, stack, light ); + // TODO: need to find how to make if statement instead return, like it doing Forge + matrixStack.pop(); info.cancel(); } } diff --git a/src/main/java/dan200/computercraft/fabric/mixin/MixinMatrix4f.java b/src/main/java/dan200/computercraft/fabric/mixin/MixinMatrix4f.java new file mode 100644 index 000000000..247151863 --- /dev/null +++ b/src/main/java/dan200/computercraft/fabric/mixin/MixinMatrix4f.java @@ -0,0 +1,52 @@ +/* + * 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.fabric.mixin; + +import dan200.computercraft.fabric.mixininterface.IMatrix4f; +import net.minecraft.util.math.Matrix4f; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin( Matrix4f.class ) +public class MixinMatrix4f implements IMatrix4f +{ + @Shadow protected float a00; + @Shadow protected float a01; + @Shadow protected float a02; + @Shadow protected float a03; + @Shadow protected float a10; + @Shadow protected float a11; + @Shadow protected float a12; + @Shadow protected float a13; + @Shadow protected float a20; + @Shadow protected float a21; + @Shadow protected float a22; + @Shadow protected float a23; + @Shadow protected float a30; + @Shadow protected float a31; + @Shadow protected float a32; + @Shadow protected float a33; + + public void setFloatArray( float[] values ) + { + a00 = values[0]; + a01 = values[1]; + a02 = values[2]; + a03 = values[3]; + a10 = values[4]; + a11 = values[5]; + a12 = values[6]; + a13 = values[7]; + a20 = values[8]; + a21 = values[9]; + a22 = values[10]; + a23 = values[11]; + a30 = values[12]; + a31 = values[13]; + a32 = values[14]; + a33 = values[15]; + } +} diff --git a/src/main/java/dan200/computercraft/fabric/mixin/MixinServerPlayerInteractionManager.java b/src/main/java/dan200/computercraft/fabric/mixin/MixinServerPlayerInteractionManager.java index 3ca7e4620..c714c2346 100644 --- a/src/main/java/dan200/computercraft/fabric/mixin/MixinServerPlayerInteractionManager.java +++ b/src/main/java/dan200/computercraft/fabric/mixin/MixinServerPlayerInteractionManager.java @@ -34,7 +34,7 @@ public class MixinServerPlayerInteractionManager ActionResult actionResult = state.onUse( world, player, hand, hitResult ); if( actionResult.isAccepted() ) { - Criteria.ITEM_USED_ON_BLOCK.test( player, pos, stack ); + Criteria.ITEM_USED_ON_BLOCK.trigger( player, pos, stack ); cir.setReturnValue( actionResult ); } } diff --git a/src/main/java/dan200/computercraft/fabric/mixin/MixinWorld.java b/src/main/java/dan200/computercraft/fabric/mixin/MixinWorld.java index 4d8c34bf0..d264ca983 100644 --- a/src/main/java/dan200/computercraft/fabric/mixin/MixinWorld.java +++ b/src/main/java/dan200/computercraft/fabric/mixin/MixinWorld.java @@ -7,7 +7,6 @@ package dan200.computercraft.fabric.mixin; import dan200.computercraft.shared.common.TileGeneric; import net.minecraft.block.entity.BlockEntity; -import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -29,10 +28,10 @@ public class MixinWorld @Shadow protected boolean iteratingTickingBlockEntities; - @Inject( method = "setBlockEntity", at = @At( "HEAD" ) ) - public void setBlockEntity( BlockPos pos, @Nullable BlockEntity entity, CallbackInfo info ) + @Inject( method = "addBlockEntity", at = @At( "HEAD" ) ) + public void addBlockEntity( @Nullable BlockEntity entity, CallbackInfo info ) { - if( !World.isOutOfBuildLimitVertically( pos ) && entity != null && !entity.isRemoved() && iteratingTickingBlockEntities ) + if( entity != null && !entity.isRemoved() && entity.getWorld().isInBuildLimit( entity.getPos() ) && iteratingTickingBlockEntities ) { setWorld( entity, this ); } @@ -42,11 +41,11 @@ public class MixinWorld { if( entity.getWorld() != world && entity instanceof TileGeneric ) { - entity.setLocation( (World) world, entity.getPos() ); + entity.setWorld( (World) world ); //TODO why? } } - @Inject( method = "addBlockEntities", at = @At( "HEAD" ) ) + // @Inject( method = "addBlockEntities", at = @At( "HEAD" ) ) public void addBlockEntities( Collection entities, CallbackInfo info ) { if( iteratingTickingBlockEntities ) diff --git a/src/main/java/dan200/computercraft/fabric/mixin/MusicDiscItemAccessor.java b/src/main/java/dan200/computercraft/fabric/mixin/MusicDiscItemAccessor.java deleted file mode 100644 index 58a9fc4c8..000000000 --- a/src/main/java/dan200/computercraft/fabric/mixin/MusicDiscItemAccessor.java +++ /dev/null @@ -1,18 +0,0 @@ -/* - * 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.fabric.mixin; - -import net.minecraft.item.MusicDiscItem; -import net.minecraft.sound.SoundEvent; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -@Mixin( MusicDiscItem.class ) -public interface MusicDiscItemAccessor -{ - @Accessor - SoundEvent getSound(); -} diff --git a/src/main/java/dan200/computercraft/fabric/mixin/SignBlockEntityAccess.java b/src/main/java/dan200/computercraft/fabric/mixin/SignBlockEntityAccess.java index e1aa351da..10e381803 100644 --- a/src/main/java/dan200/computercraft/fabric/mixin/SignBlockEntityAccess.java +++ b/src/main/java/dan200/computercraft/fabric/mixin/SignBlockEntityAccess.java @@ -14,5 +14,5 @@ import org.spongepowered.asm.mixin.gen.Accessor; public interface SignBlockEntityAccess { @Accessor - Text[] getText(); + Text[] getTexts(); } diff --git a/src/main/java/dan200/computercraft/fabric/mixin/SoundEventAccess.java b/src/main/java/dan200/computercraft/fabric/mixin/SoundEventAccess.java deleted file mode 100644 index 87e9c7896..000000000 --- a/src/main/java/dan200/computercraft/fabric/mixin/SoundEventAccess.java +++ /dev/null @@ -1,18 +0,0 @@ -/* - * 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.fabric.mixin; - -import net.minecraft.sound.SoundEvent; -import net.minecraft.util.Identifier; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -@Mixin( SoundEvent.class ) -public interface SoundEventAccess -{ - @Accessor - Identifier getId(); -} diff --git a/src/main/java/dan200/computercraft/fabric/mixininterface/IMatrix4f.java b/src/main/java/dan200/computercraft/fabric/mixininterface/IMatrix4f.java new file mode 100644 index 000000000..b8b4e7ef8 --- /dev/null +++ b/src/main/java/dan200/computercraft/fabric/mixininterface/IMatrix4f.java @@ -0,0 +1,11 @@ +/* + * 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.fabric.mixininterface; + +public interface IMatrix4f +{ + void setFloatArray( float[] values ); +} diff --git a/src/main/java/dan200/computercraft/shared/BundledRedstone.java b/src/main/java/dan200/computercraft/shared/BundledRedstone.java index 8c8be9b79..656ffafab 100644 --- a/src/main/java/dan200/computercraft/shared/BundledRedstone.java +++ b/src/main/java/dan200/computercraft/shared/BundledRedstone.java @@ -14,25 +14,24 @@ import net.minecraft.util.math.Direction; import net.minecraft.world.World; import javax.annotation.Nonnull; -import java.util.LinkedHashSet; +import java.util.ArrayList; import java.util.Objects; -import java.util.Set; public final class BundledRedstone { - private static final Set providers = new LinkedHashSet<>(); + private static final ArrayList providers = new ArrayList<>(); private BundledRedstone() {} public static synchronized void register( @Nonnull IBundledRedstoneProvider provider ) { Objects.requireNonNull( provider, "provider cannot be null" ); - providers.add( provider ); + if( !providers.contains( provider ) ) providers.add( provider ); } public static int getDefaultOutput( @Nonnull World world, @Nonnull BlockPos pos, @Nonnull Direction side ) { - return World.isInBuildLimit( pos ) ? DefaultBundledRedstoneProvider.getDefaultBundledRedstoneOutput( world, pos, side ) : -1; + return world.isInBuildLimit( pos ) ? DefaultBundledRedstoneProvider.getDefaultBundledRedstoneOutput( world, pos, side ) : -1; } public static int getOutput( World world, BlockPos pos, Direction side ) @@ -43,7 +42,7 @@ public final class BundledRedstone private static int getUnmaskedOutput( World world, BlockPos pos, Direction side ) { - if( !World.isInBuildLimit( pos ) ) + if( !world.isInBuildLimit( pos ) ) { return -1; } diff --git a/src/main/java/dan200/computercraft/shared/ComputerCraftRegistry.java b/src/main/java/dan200/computercraft/shared/ComputerCraftRegistry.java index 3e45b482f..969577732 100644 --- a/src/main/java/dan200/computercraft/shared/ComputerCraftRegistry.java +++ b/src/main/java/dan200/computercraft/shared/ComputerCraftRegistry.java @@ -13,12 +13,17 @@ import dan200.computercraft.shared.computer.blocks.BlockComputer; import dan200.computercraft.shared.computer.blocks.TileCommandComputer; import dan200.computercraft.shared.computer.blocks.TileComputer; import dan200.computercraft.shared.computer.core.ComputerFamily; -import dan200.computercraft.shared.computer.inventory.ContainerComputer; +import dan200.computercraft.shared.computer.inventory.ComputerMenuWithoutInventory; +import dan200.computercraft.shared.computer.inventory.ContainerComputerBase; import dan200.computercraft.shared.computer.inventory.ContainerViewComputer; import dan200.computercraft.shared.computer.items.ItemComputer; import dan200.computercraft.shared.media.items.ItemDisk; import dan200.computercraft.shared.media.items.ItemPrintout; import dan200.computercraft.shared.media.items.ItemTreasureDisk; +import dan200.computercraft.shared.network.container.ComputerContainerData; +import dan200.computercraft.shared.network.container.ContainerData; +import dan200.computercraft.shared.network.container.HeldItemContainerData; +import dan200.computercraft.shared.network.container.ViewComputerContainerData; import dan200.computercraft.shared.peripheral.diskdrive.BlockDiskDrive; import dan200.computercraft.shared.peripheral.diskdrive.ContainerDiskDrive; import dan200.computercraft.shared.peripheral.diskdrive.TileDiskDrive; @@ -32,7 +37,6 @@ import dan200.computercraft.shared.peripheral.printer.ContainerPrinter; import dan200.computercraft.shared.peripheral.printer.TilePrinter; import dan200.computercraft.shared.peripheral.speaker.BlockSpeaker; import dan200.computercraft.shared.peripheral.speaker.TileSpeaker; -import dan200.computercraft.shared.pocket.inventory.ContainerPocketComputer; import dan200.computercraft.shared.pocket.items.ItemPocketComputer; import dan200.computercraft.shared.pocket.peripherals.PocketModem; import dan200.computercraft.shared.pocket.peripherals.PocketSpeaker; @@ -42,13 +46,10 @@ import dan200.computercraft.shared.turtle.core.TurtlePlayer; import dan200.computercraft.shared.turtle.inventory.ContainerTurtle; import dan200.computercraft.shared.turtle.items.ItemTurtle; import dan200.computercraft.shared.turtle.upgrades.*; -import dan200.computercraft.shared.util.FixedPointTileEntityType; import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.fabricmc.fabric.api.object.builder.v1.block.entity.FabricBlockEntityTypeBuilder; import net.fabricmc.fabric.api.screenhandler.v1.ScreenHandlerRegistry; -import net.minecraft.block.AbstractBlock; -import net.minecraft.block.Block; -import net.minecraft.block.Blocks; -import net.minecraft.block.Material; +import net.minecraft.block.*; import net.minecraft.block.entity.BlockEntity; import net.minecraft.block.entity.BlockEntityType; import net.minecraft.entity.EntityType; @@ -61,12 +62,10 @@ import net.minecraft.screen.ScreenHandler; import net.minecraft.screen.ScreenHandlerType; import net.minecraft.sound.BlockSoundGroup; import net.minecraft.util.Identifier; +import net.minecraft.util.math.BlockPos; import net.minecraft.util.registry.Registry; -import java.util.Objects; import java.util.function.BiFunction; -import java.util.function.Function; -import java.util.function.Supplier; import static net.minecraft.util.registry.Registry.BLOCK_ENTITY_TYPE; @@ -91,31 +90,31 @@ public final class ComputerCraftRegistry public static final class ModBlocks { public static final BlockComputer COMPUTER_NORMAL = register( "computer_normal", - new BlockComputer( properties(), ComputerFamily.NORMAL, ModTiles.COMPUTER_NORMAL ) ); + new BlockComputer( properties(), ComputerFamily.NORMAL, ComputerCraftRegistry.ModTiles.COMPUTER_NORMAL ) ); public static final BlockComputer COMPUTER_ADVANCED = register( "computer_advanced", new BlockComputer( properties(), ComputerFamily.ADVANCED, - ModTiles.COMPUTER_ADVANCED ) ); + ComputerCraftRegistry.ModTiles.COMPUTER_ADVANCED ) ); public static final BlockComputer COMPUTER_COMMAND = register( "computer_command", new BlockComputer( FabricBlockSettings.copyOf( Blocks.STONE ) .strength( -1, 6000000.0F ), ComputerFamily.COMMAND, - ModTiles.COMPUTER_COMMAND ) ); + ComputerCraftRegistry.ModTiles.COMPUTER_COMMAND ) ); public static final BlockTurtle TURTLE_NORMAL = register( "turtle_normal", - new BlockTurtle( turtleProperties(), ComputerFamily.NORMAL, ModTiles.TURTLE_NORMAL ) ); + new BlockTurtle( turtleProperties(), ComputerFamily.NORMAL, ComputerCraftRegistry.ModTiles.TURTLE_NORMAL ) ); public static final BlockTurtle TURTLE_ADVANCED = register( "turtle_advanced", - new BlockTurtle( turtleProperties(), ComputerFamily.ADVANCED, ModTiles.TURTLE_ADVANCED ) ); + new BlockTurtle( turtleProperties(), ComputerFamily.ADVANCED, ComputerCraftRegistry.ModTiles.TURTLE_ADVANCED ) ); public static final BlockSpeaker SPEAKER = register( "speaker", new BlockSpeaker( properties() ) ); public static final BlockDiskDrive DISK_DRIVE = register( "disk_drive", new BlockDiskDrive( properties() ) ); public static final BlockPrinter PRINTER = register( "printer", new BlockPrinter( properties() ) ); - public static final BlockMonitor MONITOR_NORMAL = register( "monitor_normal", new BlockMonitor( properties(), ModTiles.MONITOR_NORMAL ) ); - public static final BlockMonitor MONITOR_ADVANCED = register( "monitor_advanced", new BlockMonitor( properties(), ModTiles.MONITOR_ADVANCED ) ); + public static final BlockMonitor MONITOR_NORMAL = register( "monitor_normal", new BlockMonitor( properties(), ModTiles.MONITOR_NORMAL, false ) ); + public static final BlockMonitor MONITOR_ADVANCED = register( "monitor_advanced", new BlockMonitor( properties(), ModTiles.MONITOR_ADVANCED, true ) ); public static final BlockWirelessModem WIRELESS_MODEM_NORMAL = register( "wireless_modem_normal", - new BlockWirelessModem( properties(), ModTiles.WIRELESS_MODEM_NORMAL ) ); + new BlockWirelessModem( properties(), ComputerCraftRegistry.ModTiles.WIRELESS_MODEM_NORMAL, ComputerFamily.NORMAL ) ); public static final BlockWirelessModem WIRELESS_MODEM_ADVANCED = register( "wireless_modem_advanced", - new BlockWirelessModem( properties(), ModTiles.WIRELESS_MODEM_ADVANCED ) ); + new BlockWirelessModem( properties(), ComputerCraftRegistry.ModTiles.WIRELESS_MODEM_ADVANCED, ComputerFamily.ADVANCED ) ); public static final BlockWiredModemFull WIRED_MODEM_FULL = register( "wired_modem_full", - new BlockWiredModemFull( emProperties(), ModTiles.WIRED_MODEM_FULL ) ); + new BlockWiredModemFull( emProperties(), ComputerCraftRegistry.ModTiles.WIRED_MODEM_FULL ) ); public static final BlockCable CABLE = register( "cable", new BlockCable( emProperties() ) ); private static Block.Settings properties() @@ -149,46 +148,52 @@ public final class ComputerCraftRegistry public static class ModTiles { - public static final BlockEntityType MONITOR_NORMAL = ofBlock( () -> ModBlocks.MONITOR_NORMAL, + public static final BlockEntityType MONITOR_NORMAL = ofBlock( ModBlocks.MONITOR_NORMAL, "monitor_normal", - f -> new TileMonitor( f, false ) ); - public static final BlockEntityType MONITOR_ADVANCED = ofBlock( () -> ModBlocks.MONITOR_ADVANCED, + ( blockPos, blockState ) -> new TileMonitor( ModTiles.MONITOR_NORMAL, false, blockPos, blockState ) ); + public static final BlockEntityType MONITOR_ADVANCED = ofBlock( ModBlocks.MONITOR_ADVANCED, "monitor_advanced", - f -> new TileMonitor( f, true ) ); - public static final BlockEntityType COMPUTER_NORMAL = ofBlock( () -> ModBlocks.COMPUTER_NORMAL, + ( blockPos, blockState ) -> new TileMonitor( ModTiles.MONITOR_ADVANCED, true, blockPos, blockState ) ); + public static final BlockEntityType COMPUTER_NORMAL = ofBlock( ModBlocks.COMPUTER_NORMAL, "computer_normal", - f -> new TileComputer( ComputerFamily.NORMAL, f ) ); - public static final BlockEntityType COMPUTER_ADVANCED = ofBlock( () -> ModBlocks.COMPUTER_ADVANCED, + ( blockPos, blockState ) -> new TileComputer( ComputerFamily.NORMAL, ModTiles.COMPUTER_NORMAL, blockPos, blockState ) ); + public static final BlockEntityType COMPUTER_ADVANCED = ofBlock( ModBlocks.COMPUTER_ADVANCED, "computer_advanced", - f -> new TileComputer( ComputerFamily.ADVANCED, f ) ); - public static final BlockEntityType COMPUTER_COMMAND = ofBlock( () -> ModBlocks.COMPUTER_COMMAND, + ( blockPos, blockState ) -> new TileComputer( ComputerFamily.ADVANCED, ModTiles.COMPUTER_ADVANCED, blockPos, blockState ) ); + public static final BlockEntityType COMPUTER_COMMAND = ofBlock( ModBlocks.COMPUTER_COMMAND, "computer_command", - f -> new TileCommandComputer( ComputerFamily.COMMAND, f ) ); - public static final BlockEntityType TURTLE_NORMAL = ofBlock( () -> ModBlocks.TURTLE_NORMAL, + ( blockPos, blockState ) -> new TileCommandComputer( ComputerFamily.COMMAND, ModTiles.COMPUTER_COMMAND, blockPos, blockState ) ); + public static final BlockEntityType TURTLE_NORMAL = ofBlock( ModBlocks.TURTLE_NORMAL, "turtle_normal", - f -> new TileTurtle( f, ComputerFamily.NORMAL ) ); - public static final BlockEntityType TURTLE_ADVANCED = ofBlock( () -> ModBlocks.TURTLE_ADVANCED, + ( blockPos, blockState ) -> new TileTurtle( ModTiles.TURTLE_NORMAL, blockPos, blockState, ComputerFamily.NORMAL ) ); + public static final BlockEntityType TURTLE_ADVANCED = ofBlock( ModBlocks.TURTLE_ADVANCED, "turtle_advanced", - f -> new TileTurtle( f, ComputerFamily.ADVANCED ) ); - public static final BlockEntityType SPEAKER = ofBlock( () -> ModBlocks.SPEAKER, "speaker", TileSpeaker::new ); - public static final BlockEntityType DISK_DRIVE = ofBlock( () -> ModBlocks.DISK_DRIVE, "disk_drive", TileDiskDrive::new ); - public static final BlockEntityType PRINTER = ofBlock( () -> ModBlocks.PRINTER, "printer", TilePrinter::new ); - public static final BlockEntityType WIRED_MODEM_FULL = ofBlock( () -> ModBlocks.WIRED_MODEM_FULL, + ( blockPos, blockState ) -> new TileTurtle( ModTiles.TURTLE_ADVANCED, blockPos, blockState, ComputerFamily.ADVANCED ) ); + public static final BlockEntityType SPEAKER = ofBlock( ModBlocks.SPEAKER, "speaker", + ( blockPos, blockState ) -> new TileSpeaker( ModTiles.SPEAKER, blockPos, blockState ) ); + public static final BlockEntityType DISK_DRIVE = ofBlock( ModBlocks.DISK_DRIVE, "disk_drive", + ( blockPos, blockState ) -> new TileDiskDrive( ModTiles.DISK_DRIVE, blockPos, blockState ) ); + public static final BlockEntityType PRINTER = ofBlock( ModBlocks.PRINTER, "printer", + ( blockPos, blockState ) -> new TilePrinter( ModTiles.PRINTER, blockPos, blockState ) ); + public static final BlockEntityType WIRED_MODEM_FULL = ofBlock( ModBlocks.WIRED_MODEM_FULL, "wired_modem_full", - TileWiredModemFull::new ); - public static final BlockEntityType CABLE = ofBlock( () -> ModBlocks.CABLE, "cable", TileCable::new ); - public static final BlockEntityType WIRELESS_MODEM_NORMAL = ofBlock( () -> ModBlocks.WIRELESS_MODEM_NORMAL, + ( blockPos, blockState ) -> new TileWiredModemFull( ModTiles.WIRED_MODEM_FULL, blockPos, blockState ) ); + public static final BlockEntityType CABLE = ofBlock( ModBlocks.CABLE, "cable", + ( blockPos, blockState ) -> new TileCable( ModTiles.CABLE, blockPos, blockState ) ); + public static final BlockEntityType WIRELESS_MODEM_NORMAL = ofBlock( ModBlocks.WIRELESS_MODEM_NORMAL, "wireless_modem_normal", - f -> new TileWirelessModem( f, false ) ); - public static final BlockEntityType WIRELESS_MODEM_ADVANCED = ofBlock( () -> ModBlocks.WIRELESS_MODEM_ADVANCED, + ( blockPos, blockState ) -> new TileWirelessModem( ModTiles.WIRELESS_MODEM_NORMAL, false, blockPos, blockState ) ); + public static final BlockEntityType WIRELESS_MODEM_ADVANCED = ofBlock( ModBlocks.WIRELESS_MODEM_ADVANCED, "wireless_modem_advanced", - f -> new TileWirelessModem( f, true ) ); + ( blockPos, blockState ) -> new TileWirelessModem( ModTiles.WIRELESS_MODEM_ADVANCED, true, blockPos, blockState ) ); - private static BlockEntityType ofBlock( Supplier block, String id, Function, T> factory ) + private static BlockEntityType ofBlock( Block block, String id, BiFunction factory ) { + BlockEntityType blockEntityType = FabricBlockEntityTypeBuilder.create( factory::apply, block ).build(); return Registry.register( BLOCK_ENTITY_TYPE, new Identifier( MOD_ID, id ), - FixedPointTileEntityType.create( Objects.requireNonNull( block ), factory ) ); + blockEntityType + ); } } @@ -252,24 +257,20 @@ public final class ComputerCraftRegistry public static class ModContainers { - public static final ScreenHandlerType COMPUTER = registerExtended( "computer", ContainerComputer::new ); - public static final ScreenHandlerType POCKET_COMPUTER = registerExtended( "pocket_computer", ContainerPocketComputer::new ); - public static final ScreenHandlerType TURTLE = registerExtended( "turtle", ContainerTurtle::new ); + public static final ScreenHandlerType COMPUTER = ContainerData.toType( new Identifier( MOD_ID, "computer" ), ModContainers.COMPUTER, ComputerContainerData::new, ComputerMenuWithoutInventory::new ); + public static final ScreenHandlerType POCKET_COMPUTER = ContainerData.toType( new Identifier( MOD_ID, "pocket_computer" ), ModContainers.POCKET_COMPUTER, ComputerContainerData::new, ComputerMenuWithoutInventory::new ); + public static final ScreenHandlerType POCKET_COMPUTER_NO_TERM = ContainerData.toType( new Identifier( MOD_ID, "pocket_computer_no_term" ), ModContainers.POCKET_COMPUTER_NO_TERM, ComputerContainerData::new, ComputerMenuWithoutInventory::new ); + public static final ScreenHandlerType TURTLE = ContainerData.toType( new Identifier( MOD_ID, "turtle" ), ComputerContainerData::new, ContainerTurtle::new ); public static final ScreenHandlerType DISK_DRIVE = registerSimple( "disk_drive", ContainerDiskDrive::new ); public static final ScreenHandlerType PRINTER = registerSimple( "printer", ContainerPrinter::new ); - public static final ScreenHandlerType PRINTOUT = registerExtended( "printout", ContainerHeldItem::createPrintout ); - public static final ScreenHandlerType VIEW_COMPUTER = registerExtended( "view_computer", ContainerViewComputer::new ); + public static final ScreenHandlerType PRINTOUT = ContainerData.toType( new Identifier( MOD_ID, "printout" ), HeldItemContainerData::new, ContainerHeldItem::createPrintout ); + public static final ScreenHandlerType VIEW_COMPUTER = ContainerData.toType( new Identifier( MOD_ID, "view_computer" ), ViewComputerContainerData::new, ContainerViewComputer::new ); private static ScreenHandlerType registerSimple( String id, ScreenHandlerRegistry.SimpleClientHandlerFactory function ) { return ScreenHandlerRegistry.registerSimple( new Identifier( MOD_ID, id ), function ); } - - private static ScreenHandlerType registerExtended( String id, ScreenHandlerRegistry.ExtendedClientHandlerFactory function ) - { - return ScreenHandlerRegistry.registerExtended( new Identifier( MOD_ID, id ), function ); - } } public static final class TurtleUpgrades diff --git a/src/main/java/dan200/computercraft/shared/Peripherals.java b/src/main/java/dan200/computercraft/shared/Peripherals.java index 38fc5676e..4cab4a12a 100644 --- a/src/main/java/dan200/computercraft/shared/Peripherals.java +++ b/src/main/java/dan200/computercraft/shared/Peripherals.java @@ -35,7 +35,7 @@ public final class Peripherals @Nullable public static IPeripheral getPeripheral( World world, BlockPos pos, Direction side ) { - return World.isInBuildLimit( pos ) && !world.isClient ? getPeripheralAt( world, pos, side ) : null; + return world.isInBuildLimit( pos ) && !world.isClient ? getPeripheralAt( world, pos, side ) : null; } @Nullable diff --git a/src/main/java/dan200/computercraft/shared/TurtlePermissions.java b/src/main/java/dan200/computercraft/shared/TurtlePermissions.java index f6236a770..19d336d06 100644 --- a/src/main/java/dan200/computercraft/shared/TurtlePermissions.java +++ b/src/main/java/dan200/computercraft/shared/TurtlePermissions.java @@ -3,12 +3,8 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared; -import com.google.common.eventbus.Subscribe; -import dan200.computercraft.ComputerCraft; -import dan200.computercraft.api.turtle.event.TurtleActionEvent; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.server.MinecraftServer; import net.minecraft.server.world.ServerWorld; @@ -17,23 +13,14 @@ import net.minecraft.world.World; public final class TurtlePermissions { + public static boolean isBlockEnterable( World world, BlockPos pos, PlayerEntity player ) + { + MinecraftServer server = world.getServer(); + return server == null || world.isClient || world instanceof ServerWorld && !server.isSpawnProtected( (ServerWorld) world, pos, player ); + } + public static boolean isBlockEditable( World world, BlockPos pos, PlayerEntity player ) { return isBlockEnterable( world, pos, player ); } - - public static boolean isBlockEnterable( World world, BlockPos pos, PlayerEntity player ) - { - MinecraftServer server = world.getServer(); - return server == null || world.isClient || (world instanceof ServerWorld && !server.isSpawnProtected( (ServerWorld) world, pos, player )); - } - - @Subscribe - public void onTurtleAction( TurtleActionEvent event ) - { - if( ComputerCraft.turtleDisabledActions.contains( event.getAction() ) ) - { - event.setCanceled( true, "Action has been disabled" ); - } - } } diff --git a/src/main/java/dan200/computercraft/shared/command/CommandComputerCraft.java b/src/main/java/dan200/computercraft/shared/command/CommandComputerCraft.java index e10a31cc7..c225b7c59 100644 --- a/src/main/java/dan200/computercraft/shared/command/CommandComputerCraft.java +++ b/src/main/java/dan200/computercraft/shared/command/CommandComputerCraft.java @@ -245,7 +245,7 @@ public final class CommandComputerCraft @Override public ScreenHandler createMenu( int id, @Nonnull PlayerInventory player, @Nonnull PlayerEntity entity ) { - return new ContainerViewComputer( id, computer ); + return new ContainerViewComputer( id, player, computer ); } } ); return 1; diff --git a/src/main/java/dan200/computercraft/shared/command/UserLevel.java b/src/main/java/dan200/computercraft/shared/command/UserLevel.java index ec1994247..1f24c7a82 100644 --- a/src/main/java/dan200/computercraft/shared/command/UserLevel.java +++ b/src/main/java/dan200/computercraft/shared/command/UserLevel.java @@ -56,18 +56,17 @@ public enum UserLevel implements Predicate public boolean test( ServerCommandSource source ) { if( this == ANYONE ) return true; - - if( this == OWNER || this == OWNER_OP ) - { - MinecraftServer server = source.getServer(); - Entity sender = source.getEntity(); - if( server.isSinglePlayer() && sender instanceof PlayerEntity && - ((PlayerEntity) sender).getGameProfile().getName().equalsIgnoreCase( server.getServerModName() ) ) - { - return true; - } - } - + if( this == OWNER ) return isOwner( source ); + if( this == OWNER_OP && isOwner( source ) ) return true; return source.hasPermissionLevel( toLevel() ); } + + private static boolean isOwner( ServerCommandSource source ) + { + MinecraftServer server = source.getServer(); + Entity sender = source.getEntity(); + return server.isDedicated() + ? source.getEntity() == null && source.hasPermissionLevel( 4 ) && source.getName().equals( "Server" ) + : sender instanceof PlayerEntity && ((PlayerEntity) sender).getGameProfile().getName().equalsIgnoreCase( server.getServerModName() ); + } } diff --git a/src/main/java/dan200/computercraft/shared/common/BlockGeneric.java b/src/main/java/dan200/computercraft/shared/common/BlockGeneric.java index 4781320ac..8150b6ec2 100644 --- a/src/main/java/dan200/computercraft/shared/common/BlockGeneric.java +++ b/src/main/java/dan200/computercraft/shared/common/BlockGeneric.java @@ -18,7 +18,6 @@ import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.math.BlockPos; -import net.minecraft.world.BlockView; import net.minecraft.world.World; import javax.annotation.Nonnull; @@ -35,6 +34,11 @@ public abstract class BlockGeneric extends BlockWithEntity this.type = type; } + public BlockEntityType getType() + { + return type; + } + @Override public BlockRenderType getRenderType( BlockState state ) { @@ -94,8 +98,12 @@ public abstract class BlockGeneric extends BlockWithEntity @Nullable @Override - public BlockEntity createBlockEntity( @Nonnull BlockView world ) + public BlockEntity createBlockEntity( BlockPos pos, BlockState state ) { - return type.instantiate(); + if ( this.type != null ) + { + return type.instantiate( pos, state ); + } + return null; } } diff --git a/src/main/java/dan200/computercraft/shared/common/ContainerHeldItem.java b/src/main/java/dan200/computercraft/shared/common/ContainerHeldItem.java index 64a5fe91f..e3150f8e8 100644 --- a/src/main/java/dan200/computercraft/shared/common/ContainerHeldItem.java +++ b/src/main/java/dan200/computercraft/shared/common/ContainerHeldItem.java @@ -36,11 +36,6 @@ public class ContainerHeldItem extends ScreenHandler .copy(); } - public static ContainerHeldItem createPrintout( int id, PlayerInventory inventory, PacketByteBuf data ) - { - return createPrintout( id, inventory, new HeldItemContainerData( data ) ); - } - public static ContainerHeldItem createPrintout( int id, PlayerInventory inventory, HeldItemContainerData data ) { return new ContainerHeldItem( ComputerCraftRegistry.ModContainers.PRINTOUT, id, inventory.player, data.getHand() ); diff --git a/src/main/java/dan200/computercraft/shared/common/ServerTerminal.java b/src/main/java/dan200/computercraft/shared/common/ServerTerminal.java index e3573ae15..db7829e8f 100644 --- a/src/main/java/dan200/computercraft/shared/common/ServerTerminal.java +++ b/src/main/java/dan200/computercraft/shared/common/ServerTerminal.java @@ -8,6 +8,8 @@ package dan200.computercraft.shared.common; import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.shared.network.client.TerminalState; +import net.minecraft.nbt.NbtCompound; + import java.util.concurrent.atomic.AtomicBoolean; import net.minecraft.nbt.NbtCompound; diff --git a/src/main/java/dan200/computercraft/shared/common/TileGeneric.java b/src/main/java/dan200/computercraft/shared/common/TileGeneric.java index 941cc3893..787a18442 100644 --- a/src/main/java/dan200/computercraft/shared/common/TileGeneric.java +++ b/src/main/java/dan200/computercraft/shared/common/TileGeneric.java @@ -21,9 +21,9 @@ import javax.annotation.Nonnull; public abstract class TileGeneric extends BlockEntity implements BlockEntityClientSerializable { - public TileGeneric( BlockEntityType type ) + public TileGeneric( BlockEntityType type, BlockPos pos, BlockState state ) { - super( type ); + super( type, pos, state ); } public void destroy() 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 079133e89..a739044e2 100644 --- a/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java +++ b/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java @@ -204,7 +204,7 @@ public class CommandAPI implements ILuaAPI World world = computer.getWorld(); BlockPos min = new BlockPos( Math.min( minX, maxX ), Math.min( minY, maxY ), Math.min( minZ, maxZ ) ); BlockPos max = new BlockPos( Math.max( minX, maxX ), Math.max( minY, maxY ), Math.max( minZ, maxZ ) ); - if( !World.isInBuildLimit( min ) || !World.isInBuildLimit( max ) ) + if( !world.isInBuildLimit( min ) || !world.isInBuildLimit( max ) ) { throw new LuaException( "Co-ordinates out of range" ); } @@ -289,7 +289,7 @@ public class CommandAPI implements ILuaAPI // Get the details of the block World world = computer.getWorld(); BlockPos position = new BlockPos( x, y, z ); - if( World.isInBuildLimit( position ) ) + if( world.isInBuildLimit( position ) ) { return getBlockInfo( world, position ); } diff --git a/src/main/java/dan200/computercraft/shared/computer/blocks/BlockComputer.java b/src/main/java/dan200/computercraft/shared/computer/blocks/BlockComputer.java index f597a84e2..39b725791 100644 --- a/src/main/java/dan200/computercraft/shared/computer/blocks/BlockComputer.java +++ b/src/main/java/dan200/computercraft/shared/computer/blocks/BlockComputer.java @@ -6,11 +6,13 @@ package dan200.computercraft.shared.computer.blocks; +import dan200.computercraft.shared.ComputerCraftRegistry; import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ComputerState; import dan200.computercraft.shared.computer.items.ComputerItemFactory; import net.minecraft.block.Block; import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntity; import net.minecraft.block.entity.BlockEntityType; import net.minecraft.item.ItemPlacementContext; import net.minecraft.item.ItemStack; @@ -18,6 +20,7 @@ import net.minecraft.state.StateManager; import net.minecraft.state.property.DirectionProperty; import net.minecraft.state.property.EnumProperty; import net.minecraft.state.property.Properties; +import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; import javax.annotation.Nonnull; @@ -56,4 +59,21 @@ public class BlockComputer extends BlockComputerBase { return tile instanceof TileComputer ? ComputerItemFactory.create( (TileComputer) tile ) : ItemStack.EMPTY; } + + public BlockEntityType getTypeByFamily( ComputerFamily family ) + { + return switch ( family ) + { + case COMMAND -> ComputerCraftRegistry.ModTiles.COMPUTER_COMMAND; + case ADVANCED -> ComputerCraftRegistry.ModTiles.COMPUTER_ADVANCED; + default -> ComputerCraftRegistry.ModTiles.COMPUTER_NORMAL; + }; + } + + @Nullable + @Override + public BlockEntity createBlockEntity( BlockPos pos, BlockState state ) + { + return new TileComputer( getFamily(), getTypeByFamily( getFamily() ), pos, state ); + } } diff --git a/src/main/java/dan200/computercraft/shared/computer/blocks/BlockComputerBase.java b/src/main/java/dan200/computercraft/shared/computer/blocks/BlockComputerBase.java index 4f98a1e54..7a366c74a 100644 --- a/src/main/java/dan200/computercraft/shared/computer/blocks/BlockComputerBase.java +++ b/src/main/java/dan200/computercraft/shared/computer/blocks/BlockComputerBase.java @@ -15,6 +15,7 @@ import dan200.computercraft.shared.computer.core.ServerComputer; import dan200.computercraft.shared.computer.items.IComputerItem; import net.minecraft.block.BlockState; import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.BlockEntityTicker; import net.minecraft.block.entity.BlockEntityType; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.player.PlayerEntity; @@ -211,4 +212,16 @@ public abstract class BlockComputerBase extends Bloc state.onStacksDropped( serverWorld, pos, player.getMainHandStack() ); } } + + @Nullable + @Override + public BlockEntityTicker getTicker( World world, BlockState state, BlockEntityType type ) + { + return world.isClient ? null : ( world1, pos, state1, tile ) -> { + if ( tile instanceof TileComputerBase computer ) + { + computer.serverTick(); + } + }; + } } diff --git a/src/main/java/dan200/computercraft/shared/computer/blocks/TileCommandComputer.java b/src/main/java/dan200/computercraft/shared/computer/blocks/TileCommandComputer.java index 60dd39182..c9cc998e1 100644 --- a/src/main/java/dan200/computercraft/shared/computer/blocks/TileCommandComputer.java +++ b/src/main/java/dan200/computercraft/shared/computer/blocks/TileCommandComputer.java @@ -10,6 +10,7 @@ import dan200.computercraft.ComputerCraft; import dan200.computercraft.shared.computer.apis.CommandAPI; import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ServerComputer; +import net.minecraft.block.BlockState; import net.minecraft.block.entity.BlockEntityType; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.server.MinecraftServer; @@ -19,6 +20,7 @@ import net.minecraft.server.world.ServerWorld; import net.minecraft.text.LiteralText; import net.minecraft.text.Text; import net.minecraft.text.TranslatableText; +import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec2f; import net.minecraft.util.math.Vec3d; import net.minecraft.world.GameRules; @@ -32,9 +34,9 @@ public class TileCommandComputer extends TileComputer { private final CommandReceiver receiver; - public TileCommandComputer( ComputerFamily family, BlockEntityType type ) + public TileCommandComputer( ComputerFamily family, BlockEntityType type, BlockPos pos, BlockState state ) { - super( family, type ); + super( family, type, pos, state ); receiver = new CommandReceiver(); } diff --git a/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputer.java b/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputer.java index 078a9925a..280f4c4d5 100644 --- a/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputer.java +++ b/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputer.java @@ -8,15 +8,17 @@ package dan200.computercraft.shared.computer.blocks; import dan200.computercraft.ComputerCraft; import dan200.computercraft.core.computer.ComputerSide; +import dan200.computercraft.shared.ComputerCraftRegistry; import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ComputerState; import dan200.computercraft.shared.computer.core.ServerComputer; -import dan200.computercraft.shared.computer.inventory.ContainerComputer; +import dan200.computercraft.shared.computer.inventory.ComputerMenuWithoutInventory; import net.minecraft.block.BlockState; import net.minecraft.block.entity.BlockEntityType; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.screen.ScreenHandler; +import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; import javax.annotation.Nonnull; @@ -26,9 +28,9 @@ public class TileComputer extends TileComputerBase { private ComputerProxy proxy; - public TileComputer( ComputerFamily family, BlockEntityType type ) + public TileComputer( ComputerFamily family, BlockEntityType type, BlockPos pos, BlockState state ) { - super( type, family ); + super( type, family, pos, state ); } public boolean isUsableByPlayer( PlayerEntity player ) @@ -103,7 +105,7 @@ public class TileComputer extends TileComputerBase @Override public ScreenHandler createMenu( int id, @Nonnull PlayerInventory inventory, @Nonnull PlayerEntity player ) { - return new ContainerComputer( id, this ); + return new ComputerMenuWithoutInventory( ComputerCraftRegistry.ModContainers.COMPUTER, id, inventory, this::isUsableByPlayer, createServerComputer(), getFamily() ); } } diff --git a/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputerBase.java b/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputerBase.java index b8ddfbc60..0e6955f9c 100644 --- a/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputerBase.java +++ b/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputerBase.java @@ -37,7 +37,6 @@ import net.minecraft.text.TranslatableText; import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; import net.minecraft.util.Nameable; -import net.minecraft.util.Tickable; import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; @@ -47,7 +46,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.Objects; -public abstract class TileComputerBase extends TileGeneric implements IComputerTile, Tickable, IPeripheralTile, Nameable, +public abstract class TileComputerBase extends TileGeneric implements IComputerTile, IPeripheralTile, Nameable, ExtendedScreenHandlerFactory { private static final String NBT_ID = "ComputerId"; @@ -61,9 +60,9 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT private boolean on = false; private boolean fresh = false; - public TileComputerBase( BlockEntityType type, ComputerFamily family ) + public TileComputerBase( BlockEntityType type, ComputerFamily family, BlockPos pos, BlockState state ) { - super( type ); + super( type, pos, state ); this.family = family; } @@ -271,44 +270,40 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT } } - @Override - public void tick() + public void serverTick() { - if( !getWorld().isClient ) + ServerComputer computer = createServerComputer(); + if( computer == null ) { - ServerComputer computer = createServerComputer(); - if( computer == null ) - { - return; - } + return; + } - // If the computer isn't on and should be, then turn it on - if( startOn || (fresh && on) ) - { - computer.turnOn(); - startOn = false; - } + // If the computer isn't on and should be, then turn it on + if( startOn || fresh && on ) + { + computer.turnOn(); + startOn = false; + } - computer.keepAlive(); + computer.keepAlive(); - fresh = false; - computerID = computer.getID(); - label = computer.getLabel(); - on = computer.isOn(); + fresh = false; + computerID = computer.getID(); + label = computer.getLabel(); + on = computer.isOn(); - if( computer.hasOutputChanged() ) - { - updateOutput(); - } + if( computer.hasOutputChanged() ) + { + updateOutput(); + } - // Update the block state if needed. We don't fire a block update intentionally, - // as this only really is needed on the client side. - updateBlockState( computer.getState() ); + // Update the block state if needed. We don't fire a block update intentionally, + // as this only really is needed on the client side. + updateBlockState( computer.getState() ); - if( computer.hasOutputChanged() ) - { - updateOutput(); - } + if( computer.hasOutputChanged() ) + { + updateOutput(); } } @@ -325,9 +320,9 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT protected abstract void updateBlockState( ComputerState newState ); @Override - public void readNbt( @Nonnull BlockState state, @Nonnull NbtCompound nbt ) + public void readNbt( @Nonnull NbtCompound nbt ) { - super.readNbt( state, nbt ); + super.readNbt( nbt ); // Load ID, label and power state computerID = nbt.contains( NBT_ID ) ? nbt.getInt( NBT_ID ) : -1; @@ -349,7 +344,6 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT nbt.putString( NBT_LABEL, label ); } nbt.putBoolean( NBT_ON, on ); - return super.writeNbt( nbt ); } @@ -362,7 +356,7 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT private void updateInput( BlockPos neighbour ) { - if( getWorld() == null || getWorld().isClient ) + if( getWorld() == null || this.world.isClient ) { return; } @@ -384,12 +378,12 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT } // If the position is not any adjacent one, update all inputs. - updateInput(); + this.updateInput(); } private void updateInput( Direction dir ) { - if( getWorld() == null || getWorld().isClient ) + if( getWorld() == null || this.world.isClient ) { return; } @@ -412,7 +406,7 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT @Override public final void setComputerID( int id ) { - if( getWorld().isClient || computerID == id ) + if( this.world.isClient || computerID == id ) { return; } @@ -437,7 +431,7 @@ public abstract class TileComputerBase extends TileGeneric implements IComputerT @Override public final void setLabel( String label ) { - if( getWorld().isClient || Objects.equals( this.label, label ) ) + if( this.world.isClient || Objects.equals( this.label, label ) ) { return; } diff --git a/src/main/java/dan200/computercraft/shared/computer/core/ComputerState.java b/src/main/java/dan200/computercraft/shared/computer/core/ComputerState.java index c88169a2c..29e427049 100644 --- a/src/main/java/dan200/computercraft/shared/computer/core/ComputerState.java +++ b/src/main/java/dan200/computercraft/shared/computer/core/ComputerState.java @@ -12,13 +12,15 @@ import javax.annotation.Nonnull; public enum ComputerState implements StringIdentifiable { - OFF( "off" ), ON( "on" ), BLINKING( "blinking" ); + OFF( "off", "" ), ON( "on", "_on" ), BLINKING( "blinking", "_blink" ); private final String name; + private final String texture; - ComputerState( String name ) + ComputerState( String name, String texture ) { this.name = name; + this.texture = texture; } @Nonnull @@ -33,4 +35,10 @@ public enum ComputerState implements StringIdentifiable { return name; } + + @Nonnull + public String getTexture() + { + return texture; + } } diff --git a/src/main/java/dan200/computercraft/shared/computer/core/IContainerComputer.java b/src/main/java/dan200/computercraft/shared/computer/core/IContainerComputer.java index b25eae0a0..d3ff52edf 100644 --- a/src/main/java/dan200/computercraft/shared/computer/core/IContainerComputer.java +++ b/src/main/java/dan200/computercraft/shared/computer/core/IContainerComputer.java @@ -6,13 +6,19 @@ package dan200.computercraft.shared.computer.core; +import dan200.computercraft.shared.computer.upload.FileSlice; +import dan200.computercraft.shared.computer.upload.FileUpload; +import net.minecraft.server.network.ServerPlayerEntity; + import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.util.List; +import java.util.UUID; /** * An instance of {@link Container} which provides a computer. You should implement this if you provide custom computers/GUIs to interact with them. */ -@FunctionalInterface +//@FunctionalInterface public interface IContainerComputer { /** @@ -31,8 +37,37 @@ public interface IContainerComputer * @return This container's input. */ @Nonnull - default InputState getInput() - { - return new InputState( this ); - } + InputState getInput(); + + /** + * Start a file upload into this container. + * + * @param uploadId The unique ID of this upload. + * @param files The files to upload. + */ + void startUpload( @Nonnull UUID uploadId, @Nonnull List files ); + + /** + * Append more data to partially uploaded files. + * + * @param uploadId The unique ID of this upload. + * @param slices Additional parts of file data to upload. + */ + void continueUpload( @Nonnull UUID uploadId, @Nonnull List slices ); + + /** + * Finish off an upload. This either writes the uploaded files or + * + * @param uploader The player uploading files. + * @param uploadId The unique ID of this upload. + */ + void finishUpload( @Nonnull ServerPlayerEntity uploader, @Nonnull UUID uploadId ); + + /** + * Continue an upload. + * + * @param uploader The player uploading files. + * @param overwrite Whether the files should be overwritten or not. + */ + void confirmUpload( @Nonnull ServerPlayerEntity uploader, boolean overwrite ); } diff --git a/src/main/java/dan200/computercraft/shared/computer/inventory/ComputerMenuWithoutInventory.java b/src/main/java/dan200/computercraft/shared/computer/inventory/ComputerMenuWithoutInventory.java new file mode 100644 index 000000000..f3384de72 --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/computer/inventory/ComputerMenuWithoutInventory.java @@ -0,0 +1,41 @@ +/* + * 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.computer.inventory; + +import dan200.computercraft.shared.computer.core.ComputerFamily; +import dan200.computercraft.shared.computer.core.IComputer; +import dan200.computercraft.shared.network.container.ComputerContainerData; +import dan200.computercraft.shared.util.InvisibleSlot; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.screen.ScreenHandlerType; + +import java.util.function.Predicate; + +/** + * A computer menu which does not have any visible inventory. + * + * This adds invisible versions of the player's hotbars slots, to ensure they're synced to the client when changed. + */ +public class ComputerMenuWithoutInventory extends ContainerComputerBase +{ + public ComputerMenuWithoutInventory( ScreenHandlerType type, int id, PlayerInventory player, Predicate canUse, IComputer computer, ComputerFamily family ) + { + super( type, id, canUse, computer, family ); + addSlots( player ); + } + + public ComputerMenuWithoutInventory( ScreenHandlerType type, int id, PlayerInventory player, ComputerContainerData data ) + { + super( type, id, player, data ); + addSlots( player ); + } + + private void addSlots( PlayerInventory player ) + { + for( int i = 0; i < 9; i++ ) addSlot( new InvisibleSlot( player, i ) ); + } +} diff --git a/src/main/java/dan200/computercraft/shared/computer/inventory/ContainerComputer.java b/src/main/java/dan200/computercraft/shared/computer/inventory/ContainerComputer.java deleted file mode 100644 index f4202bbe1..000000000 --- a/src/main/java/dan200/computercraft/shared/computer/inventory/ContainerComputer.java +++ /dev/null @@ -1,25 +0,0 @@ -/* - * 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.computer.inventory; - -import dan200.computercraft.shared.ComputerCraftRegistry; -import dan200.computercraft.shared.computer.blocks.TileComputer; -import net.minecraft.entity.player.PlayerInventory; -import net.minecraft.network.PacketByteBuf; - -public class ContainerComputer extends ContainerComputerBase -{ - public ContainerComputer( int id, TileComputer tile ) - { - super( ComputerCraftRegistry.ModContainers.COMPUTER, id, tile::isUsableByPlayer, tile.createServerComputer(), tile.getFamily() ); - } - - public ContainerComputer( int i, PlayerInventory playerInventory, PacketByteBuf packetByteBuf ) - { - super( ComputerCraftRegistry.ModContainers.COMPUTER, i, playerInventory, packetByteBuf ); - } -} diff --git a/src/main/java/dan200/computercraft/shared/computer/inventory/ContainerComputerBase.java b/src/main/java/dan200/computercraft/shared/computer/inventory/ContainerComputerBase.java index a696c8618..e1d42bd5e 100644 --- a/src/main/java/dan200/computercraft/shared/computer/inventory/ContainerComputerBase.java +++ b/src/main/java/dan200/computercraft/shared/computer/inventory/ContainerComputerBase.java @@ -7,36 +7,53 @@ package dan200.computercraft.shared.computer.inventory; import dan200.computercraft.ComputerCraft; +import dan200.computercraft.core.filesystem.FileSystem; +import dan200.computercraft.core.filesystem.FileSystemException; +import dan200.computercraft.core.filesystem.FileSystemWrapper; import dan200.computercraft.shared.computer.core.*; +import dan200.computercraft.shared.computer.upload.FileSlice; +import dan200.computercraft.shared.computer.upload.FileUpload; +import dan200.computercraft.shared.computer.upload.UploadResult; +import dan200.computercraft.shared.network.NetworkHandler; +import dan200.computercraft.shared.network.client.UploadResultMessage; import dan200.computercraft.shared.network.container.ComputerContainerData; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; -import net.minecraft.network.PacketByteBuf; import net.minecraft.screen.ScreenHandler; import net.minecraft.screen.ScreenHandlerType; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.TranslatableText; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import java.util.Objects; +import java.io.IOException; +import java.nio.channels.WritableByteChannel; +import java.util.*; +import java.util.function.Function; import java.util.function.Predicate; -public class ContainerComputerBase extends ScreenHandler implements IContainerComputer +public abstract class ContainerComputerBase extends ScreenHandler implements IContainerComputer { + private static final String LIST_PREFIX = "\n \u2022 "; + private final Predicate canUse; private final IComputer computer; private final ComputerFamily family; private final InputState input = new InputState( this ); - protected ContainerComputerBase( ScreenHandlerType type, int id, PlayerInventory player, PacketByteBuf packetByteBuf ) + private UUID toUploadId; + private List toUpload; + + public ContainerComputerBase( ScreenHandlerType type, int id, PlayerInventory player, ComputerContainerData data ) { this( type, id, x -> true, - getComputer( player, new ComputerContainerData( new PacketByteBuf( packetByteBuf.copy() ) ) ), - new ComputerContainerData( new PacketByteBuf( packetByteBuf.copy() ) ).getFamily() ); + getComputer( player, data ), + data.getFamily() ); } - protected ContainerComputerBase( ScreenHandlerType type, int id, Predicate canUse, IComputer computer, + public ContainerComputerBase( ScreenHandlerType type, int id, Predicate canUse, IComputer computer, ComputerFamily family ) { super( type, id ); @@ -93,4 +110,121 @@ public class ContainerComputerBase extends ScreenHandler implements IContainerCo { return canUse.test( player ); } + + @Override + public void startUpload( @Nonnull UUID uuid, @Nonnull List files ) + { + toUploadId = uuid; + toUpload = files; + } + + @Override + public void continueUpload( @Nonnull UUID uploadId, @Nonnull List slices ) + { + if( toUploadId == null || toUpload == null || !toUploadId.equals( uploadId ) ) + { + ComputerCraft.log.warn( "Invalid continueUpload call, skipping." ); + return; + } + + for( FileSlice slice : slices ) slice.apply( toUpload ); + } + + @Override + public void finishUpload( @Nonnull ServerPlayerEntity uploader, @Nonnull UUID uploadId ) + { + if( toUploadId == null || toUpload == null || toUpload.isEmpty() || !toUploadId.equals( uploadId ) ) + { + ComputerCraft.log.warn( "Invalid finishUpload call, skipping." ); + return; + } + + UploadResultMessage message = finishUpload( false ); + NetworkHandler.sendToPlayer( uploader, message ); + } + + @Override + public void confirmUpload( @Nonnull ServerPlayerEntity uploader, boolean overwrite ) + { + if( toUploadId == null || toUpload == null || toUpload.isEmpty() ) + { + ComputerCraft.log.warn( "Invalid finishUpload call, skipping." ); + return; + } + + UploadResultMessage message = finishUpload( true ); + NetworkHandler.sendToPlayer( uploader, message ); + } + + @Nonnull + private UploadResultMessage finishUpload( boolean forceOverwrite ) + { + ServerComputer computer = (ServerComputer) getComputer(); + if( computer == null ) return UploadResultMessage.COMPUTER_OFF; + + FileSystem fs = computer.getComputer().getEnvironment().getFileSystem(); + if( fs == null ) return UploadResultMessage.COMPUTER_OFF; + + for( FileUpload upload : toUpload ) + { + if( !upload.checksumMatches() ) + { + ComputerCraft.log.warn( "Checksum failed to match for {}.", upload.getName() ); + return new UploadResultMessage( UploadResult.ERROR, new TranslatableText( "gui.computercraft.upload.failed.corrupted" ) ); + } + } + + try + { + List overwrite = new ArrayList<>(); + List files = toUpload; + toUpload = null; + for( FileUpload upload : files ) + { + if( !fs.exists( upload.getName() ) ) continue; + if( fs.isDir( upload.getName() ) ) + { + return new UploadResultMessage( + UploadResult.ERROR, + new TranslatableText( "gui.computercraft.upload.failed.overwrite_dir", upload.getName() ) + ); + } + + overwrite.add( upload.getName() ); + } + + if( !overwrite.isEmpty() && !forceOverwrite ) + { + StringJoiner joiner = new StringJoiner( LIST_PREFIX, LIST_PREFIX, "" ); + for( String value : overwrite ) joiner.add( value ); + toUpload = files; + return new UploadResultMessage( + UploadResult.CONFIRM_OVERWRITE, + new TranslatableText( "gui.computercraft.upload.overwrite.detail", joiner.toString() ) + ); + } + + long availableSpace = fs.getFreeSpace( "/" ); + long neededSpace = 0; + for( FileUpload upload : files ) neededSpace += Math.max( 512, upload.getBytes().remaining() ); + if( neededSpace > availableSpace ) return UploadResultMessage.OUT_OF_SPACE; + + for( FileUpload file : files ) + { + try( FileSystemWrapper channel = fs.openForWrite( file.getName(), false, Function.identity() ) ) + { + channel.get().write( file.getBytes() ); + } + } + + return new UploadResultMessage( + UploadResult.SUCCESS, new TranslatableText( "gui.computercraft.upload.success.msg", files.size() ) + ); + } + catch( FileSystemException | IOException e ) + { + ComputerCraft.log.error( "Error uploading files", e ); + return new UploadResultMessage( UploadResult.ERROR, new TranslatableText( "gui.computercraft.upload.failed.generic", e.getMessage() ) ); + } + } } diff --git a/src/main/java/dan200/computercraft/shared/computer/inventory/ContainerViewComputer.java b/src/main/java/dan200/computercraft/shared/computer/inventory/ContainerViewComputer.java index c2fa650c6..341908650 100644 --- a/src/main/java/dan200/computercraft/shared/computer/inventory/ContainerViewComputer.java +++ b/src/main/java/dan200/computercraft/shared/computer/inventory/ContainerViewComputer.java @@ -14,25 +14,23 @@ import dan200.computercraft.shared.computer.core.ServerComputer; import dan200.computercraft.shared.network.container.ViewComputerContainerData; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerInventory; -import net.minecraft.network.PacketByteBuf; import javax.annotation.Nonnull; -public class ContainerViewComputer extends ContainerComputerBase +public class ContainerViewComputer extends ComputerMenuWithoutInventory { private final int width; private final int height; - public ContainerViewComputer( int id, ServerComputer computer ) + public ContainerViewComputer( int id, PlayerInventory player, ServerComputer computer ) { - super( ComputerCraftRegistry.ModContainers.VIEW_COMPUTER, id, player -> canInteractWith( computer, player ), computer, computer.getFamily() ); + super( ComputerCraftRegistry.ModContainers.VIEW_COMPUTER, id, player, p -> canInteractWith( computer, p ), computer, computer.getFamily() ); width = height = 0; } - public ContainerViewComputer( int id, PlayerInventory player, PacketByteBuf packetByteBuf ) + public ContainerViewComputer( int id, PlayerInventory player, ViewComputerContainerData data ) { - super( ComputerCraftRegistry.ModContainers.VIEW_COMPUTER, id, player, packetByteBuf ); - ViewComputerContainerData data = new ViewComputerContainerData( new PacketByteBuf( packetByteBuf.copy() ) ); + super( ComputerCraftRegistry.ModContainers.VIEW_COMPUTER, id, player, data ); width = data.getWidth(); height = data.getHeight(); } diff --git a/src/main/java/dan200/computercraft/shared/computer/items/IComputerItem.java b/src/main/java/dan200/computercraft/shared/computer/items/IComputerItem.java index 326b2adac..e887dbb6b 100644 --- a/src/main/java/dan200/computercraft/shared/computer/items/IComputerItem.java +++ b/src/main/java/dan200/computercraft/shared/computer/items/IComputerItem.java @@ -9,6 +9,7 @@ package dan200.computercraft.shared.computer.items; import dan200.computercraft.shared.computer.core.ComputerFamily; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NbtCompound; + import javax.annotation.Nonnull; public interface IComputerItem diff --git a/src/main/java/dan200/computercraft/shared/computer/recipe/ComputerFamilyRecipe.java b/src/main/java/dan200/computercraft/shared/computer/recipe/ComputerFamilyRecipe.java index 9971dd31b..d882505f9 100644 --- a/src/main/java/dan200/computercraft/shared/computer/recipe/ComputerFamilyRecipe.java +++ b/src/main/java/dan200/computercraft/shared/computer/recipe/ComputerFamilyRecipe.java @@ -45,7 +45,7 @@ public abstract class ComputerFamilyRecipe extends ComputerConvertRecipe ComputerFamily family = RecipeUtil.getFamily( json, "family" ); RecipeUtil.ShapedTemplate template = RecipeUtil.getTemplate( json ); - ItemStack result = getItem( JsonHelper.getObject( json, "result" ) ); + ItemStack result = outputFromJson( JsonHelper.getObject( json, "result" ) ); return create( identifier, group, template.width, template.height, template.ingredients, result, family ); } diff --git a/src/main/java/dan200/computercraft/shared/computer/upload/FileSlice.java b/src/main/java/dan200/computercraft/shared/computer/upload/FileSlice.java new file mode 100644 index 000000000..2f290e660 --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/computer/upload/FileSlice.java @@ -0,0 +1,63 @@ +/* + * 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.computer.upload; + +import dan200.computercraft.ComputerCraft; + +import java.nio.ByteBuffer; +import java.util.List; + +public class FileSlice +{ + private final int fileId; + private final int offset; + private final ByteBuffer bytes; + + public FileSlice( int fileId, int offset, ByteBuffer bytes ) + { + this.fileId = fileId; + this.offset = offset; + this.bytes = bytes; + } + + public int getFileId() + { + return fileId; + } + + public int getOffset() + { + return offset; + } + + public ByteBuffer getBytes() + { + return bytes; + } + + public void apply( List files ) + { + if( fileId < 0 || fileId >= files.size() ) + { + ComputerCraft.log.warn( "File ID is out-of-bounds (0 <= {} < {})", fileId, files.size() ); + return; + } + + ByteBuffer file = files.get( fileId ).getBytes(); + if( offset < 0 || offset + bytes.remaining() > file.capacity() ) + { + ComputerCraft.log.warn( "File offset is out-of-bounds (0 <= {} <= {})", offset, file.capacity() - offset ); + return; + } + + bytes.rewind(); + file.position( offset ); + file.put( bytes ); + file.rewind(); + + if( bytes.remaining() != 0 ) throw new IllegalStateException( "Should have read the whole buffer" ); + } +} diff --git a/src/main/java/dan200/computercraft/shared/computer/upload/FileUpload.java b/src/main/java/dan200/computercraft/shared/computer/upload/FileUpload.java new file mode 100644 index 000000000..6f70ad9e1 --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/computer/upload/FileUpload.java @@ -0,0 +1,80 @@ +/* + * 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.computer.upload; + +import dan200.computercraft.ComputerCraft; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import java.nio.ByteBuffer; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; + +public class FileUpload +{ + public static final int CHECKSUM_LENGTH = 32; + + private final String name; + private final int length; + private final ByteBuffer bytes; + private final byte[] checksum; + + public FileUpload( String name, ByteBuffer bytes, byte[] checksum ) + { + this.name = name; + this.bytes = bytes; + length = bytes.remaining(); + this.checksum = checksum; + } + + @Nonnull + public String getName() + { + return name; + } + + @Nonnull + public ByteBuffer getBytes() + { + return bytes; + } + + public int getLength() + { + return length; + } + + @Nonnull + public byte[] getChecksum() + { + return checksum; + } + + public boolean checksumMatches() + { + // This is meant to be a checksum. Doesn't need to be cryptographically secure, hence non constant time. + byte[] digest = getDigest( bytes ); + return digest != null && Arrays.equals( checksum, digest ); + } + + @Nullable + public static byte[] getDigest( ByteBuffer bytes ) + { + try + { + bytes.rewind(); + MessageDigest digest = MessageDigest.getInstance( "SHA-256" ); + digest.update( bytes ); + return digest.digest(); + } + catch( NoSuchAlgorithmException e ) + { + ComputerCraft.log.warn( "Failed to compute digest ({})", e.toString() ); + return null; + } + } +} diff --git a/src/main/java/dan200/computercraft/shared/computer/upload/UploadResult.java b/src/main/java/dan200/computercraft/shared/computer/upload/UploadResult.java new file mode 100644 index 000000000..2c332c0ef --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/computer/upload/UploadResult.java @@ -0,0 +1,25 @@ +/* + * 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.computer.upload; + +import net.minecraft.text.Text; +import net.minecraft.text.TranslatableText; + +public enum UploadResult +{ + SUCCESS, + ERROR, + CONFIRM_OVERWRITE; + + public static final Text SUCCESS_TITLE = new TranslatableText( "gui.computercraft.upload.success" ); + + public static final Text FAILED_TITLE = new TranslatableText( "gui.computercraft.upload.failed" ); + public static final Text COMPUTER_OFF_MSG = new TranslatableText( "gui.computercraft.upload.failed.computer_off" ); + public static final Text OUT_OF_SPACE_MSG = new TranslatableText( "gui.computercraft.upload.failed.out_of_space" ); + public static final Text TOO_MUCH_MSG = new TranslatableText( "gui.computercraft.upload.failed.too_much" ); + + public static final Text UPLOAD_OVERWRITE = new TranslatableText( "gui.computercraft.upload.overwrite" ); +} diff --git a/src/main/java/dan200/computercraft/shared/data/PlayerCreativeLootCondition.java b/src/main/java/dan200/computercraft/shared/data/PlayerCreativeLootCondition.java index e52690853..debdffb46 100644 --- a/src/main/java/dan200/computercraft/shared/data/PlayerCreativeLootCondition.java +++ b/src/main/java/dan200/computercraft/shared/data/PlayerCreativeLootCondition.java @@ -35,7 +35,7 @@ public final class PlayerCreativeLootCondition implements LootCondition public boolean test( LootContext lootContext ) { Entity entity = lootContext.get( LootContextParameters.THIS_ENTITY ); - return entity instanceof PlayerEntity && ((PlayerEntity) entity).abilities.creativeMode; + return entity instanceof PlayerEntity && ((PlayerEntity) entity).getAbilities().creativeMode; } @Nonnull diff --git a/src/main/java/dan200/computercraft/shared/media/items/RecordMedia.java b/src/main/java/dan200/computercraft/shared/media/items/RecordMedia.java index 730f8584f..6a4a7f8d2 100644 --- a/src/main/java/dan200/computercraft/shared/media/items/RecordMedia.java +++ b/src/main/java/dan200/computercraft/shared/media/items/RecordMedia.java @@ -3,11 +3,9 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.media.items; import dan200.computercraft.api.media.IMedia; -import dan200.computercraft.fabric.mixin.MusicDiscItemAccessor; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.item.MusicDiscItem; @@ -53,6 +51,6 @@ public final class RecordMedia implements IMedia { return null; } - return ((MusicDiscItemAccessor) item).getSound(); + return ((MusicDiscItem) item).getSound(); } } diff --git a/src/main/java/dan200/computercraft/shared/network/NetworkHandler.java b/src/main/java/dan200/computercraft/shared/network/NetworkHandler.java index 91b2e92b0..e37f4411e 100644 --- a/src/main/java/dan200/computercraft/shared/network/NetworkHandler.java +++ b/src/main/java/dan200/computercraft/shared/network/NetworkHandler.java @@ -14,6 +14,7 @@ import it.unimi.dsi.fastutil.ints.Int2ObjectMap; import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; import it.unimi.dsi.fastutil.objects.Object2IntMap; import it.unimi.dsi.fastutil.objects.Object2IntOpenHashMap; +import me.shedaniel.cloth.api.utils.v1.GameInstanceUtils; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.fabricmc.fabric.api.network.ClientSidePacketRegistry; @@ -30,6 +31,7 @@ import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.util.Identifier; import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; +import net.minecraft.world.chunk.WorldChunk; import java.util.function.BiConsumer; import java.util.function.Function; @@ -56,18 +58,25 @@ public final class NetworkHandler } // Server messages - registerMainThread( 0, ComputerActionServerMessage::new ); - registerMainThread( 1, QueueEventServerMessage::new ); - registerMainThread( 2, RequestComputerMessage::new ); - registerMainThread( 3, KeyEventServerMessage::new ); - registerMainThread( 4, MouseEventServerMessage::new ); + registerMainThread( 0, ComputerActionServerMessage.class, ComputerActionServerMessage::new ); + registerMainThread( 1, QueueEventServerMessage.class, QueueEventServerMessage::new ); + registerMainThread( 2, RequestComputerMessage.class, RequestComputerMessage::new ); + registerMainThread( 3, KeyEventServerMessage.class, KeyEventServerMessage::new ); + registerMainThread( 4, MouseEventServerMessage.class, MouseEventServerMessage::new ); + registerMainThread( 5, UploadFileMessage.class, UploadFileMessage::new ); + registerMainThread( 6, ContinueUploadMessage.class, ContinueUploadMessage::new ); // Client messages - registerMainThread( 10, ChatTableClientMessage::new ); - registerMainThread( 11, ComputerDataClientMessage::new ); - registerMainThread( 12, ComputerDeletedClientMessage::new ); - registerMainThread( 13, ComputerTerminalClientMessage::new ); + registerMainThread( 10, ChatTableClientMessage.class, ChatTableClientMessage::new ); + registerMainThread( 11, ComputerDataClientMessage.class, ComputerDataClientMessage::new ); + registerMainThread( 12, ComputerDeletedClientMessage.class, ComputerDeletedClientMessage::new ); + registerMainThread( 13, ComputerTerminalClientMessage.class, ComputerTerminalClientMessage::new ); registerMainThread( 14, PlayRecordClientMessage.class, PlayRecordClientMessage::new ); + registerMainThread( 15, MonitorClientMessage.class, MonitorClientMessage::new ); + registerMainThread( 16, SpeakerPlayClientMessage.class, SpeakerPlayClientMessage::new ); + registerMainThread( 17, SpeakerStopClientMessage.class, SpeakerStopClientMessage::new ); + registerMainThread( 18, SpeakerMoveClientMessage.class, SpeakerMoveClientMessage::new ); + registerMainThread( 19, UploadResultMessage.class, UploadResultMessage::new ); } private static void receive( PacketContext context, PacketByteBuf buffer ) @@ -77,22 +86,6 @@ public final class NetworkHandler .accept( context, buffer ); } - /** - * /** Register packet, and a thread-unsafe handler for it. - * - * @param The type of the packet to send. - * @param id The identifier for this packet type - * @param factory The factory for this type of packet. - */ - private static void registerMainThread( int id, Supplier factory ) - { - registerMainThread( id, getType( factory ), buf -> { - T instance = factory.get(); - instance.fromBytes( buf ); - return instance; - } ); - } - /** * /** Register packet, and a thread-unsafe handler for it. * @@ -118,11 +111,6 @@ public final class NetworkHandler .getClass(); } - public static void sendToPlayer( PlayerEntity player, NetworkMessage packet ) - { - ((ServerPlayerEntity) player).networkHandler.sendPacket( new CustomPayloadS2CPacket( ID, encode( packet ) ) ); - } - private static PacketByteBuf encode( NetworkMessage message ) { PacketByteBuf buf = new PacketByteBuf( Unpooled.buffer() ); @@ -131,6 +119,18 @@ public final class NetworkHandler return buf; } + public static void sendToPlayer( PlayerEntity player, NetworkMessage packet ) + { + ((ServerPlayerEntity) player).networkHandler.sendPacket( new CustomPayloadS2CPacket( ID, encode( packet ) ) ); + } + + public static void sendToAllPlayers( NetworkMessage packet ) + { + MinecraftServer server = GameInstanceUtils.getServer(); + server.getPlayerManager() + .sendToAll( new CustomPayloadS2CPacket( ID, encode( packet ) ) ); + } + public static void sendToAllPlayers( MinecraftServer server, NetworkMessage packet ) { server.getPlayerManager() @@ -149,4 +149,15 @@ public final class NetworkHandler .getPlayerManager() .sendToAround( null, pos.x, pos.y, pos.z, range, world.getRegistryKey(), new CustomPayloadS2CPacket( ID, encode( packet ) ) ); } + + public static void sendToAllTracking( NetworkMessage packet, WorldChunk chunk ) + { + for( PlayerEntity player : chunk.getWorld().getPlayers() ) + { + if ( chunk.getWorld().getRegistryKey().getValue() == player.getEntityWorld().getRegistryKey().getValue() && player.getChunkPos().equals( chunk.getPos() ) ) + { + ((ServerPlayerEntity) player).networkHandler.sendPacket( new CustomPayloadS2CPacket( ID, encode( packet ) ) ); + } + } + } } diff --git a/src/main/java/dan200/computercraft/shared/network/NetworkMessage.java b/src/main/java/dan200/computercraft/shared/network/NetworkMessage.java index 911014907..b1ab12c69 100644 --- a/src/main/java/dan200/computercraft/shared/network/NetworkMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/NetworkMessage.java @@ -25,18 +25,6 @@ public interface NetworkMessage */ void toBytes( @Nonnull PacketByteBuf buf ); - /** - * Read this packet from a buffer. - * - * This may be called on any thread, so this should be a pure operation. - * - * @param buf The buffer to read data from. - */ - default void fromBytes( @Nonnull PacketByteBuf buf ) - { - throw new IllegalStateException( "Should have been registered using a \"from bytes\" method" ); - } - /** * Handle this {@link NetworkMessage}. * diff --git a/src/main/java/dan200/computercraft/shared/network/client/ChatTableClientMessage.java b/src/main/java/dan200/computercraft/shared/network/client/ChatTableClientMessage.java index 0933ecdb7..c21f60edf 100644 --- a/src/main/java/dan200/computercraft/shared/network/client/ChatTableClientMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/client/ChatTableClientMessage.java @@ -19,7 +19,7 @@ import javax.annotation.Nonnull; public class ChatTableClientMessage implements NetworkMessage { - private TableBuilder table; + private final TableBuilder table; public ChatTableClientMessage( TableBuilder table ) { @@ -30,39 +30,7 @@ public class ChatTableClientMessage implements NetworkMessage this.table = table; } - public ChatTableClientMessage() - { - } - - @Override - public void toBytes( @Nonnull PacketByteBuf buf ) - { - buf.writeVarInt( table.getId() ); - buf.writeVarInt( table.getColumns() ); - buf.writeBoolean( table.getHeaders() != null ); - if( table.getHeaders() != null ) - { - for( Text header : table.getHeaders() ) - { - buf.writeText( header ); - } - } - - buf.writeVarInt( table.getRows() - .size() ); - for( Text[] row : table.getRows() ) - { - for( Text column : row ) - { - buf.writeText( column ); - } - } - - buf.writeVarInt( table.getAdditional() ); - } - - @Override - public void fromBytes( @Nonnull PacketByteBuf buf ) + public ChatTableClientMessage( @Nonnull PacketByteBuf buf ) { int id = buf.readVarInt(); int columns = buf.readVarInt(); @@ -96,6 +64,33 @@ public class ChatTableClientMessage implements NetworkMessage this.table = table; } + @Override + public void toBytes( @Nonnull PacketByteBuf buf ) + { + buf.writeVarInt( table.getId() ); + buf.writeVarInt( table.getColumns() ); + buf.writeBoolean( table.getHeaders() != null ); + if( table.getHeaders() != null ) + { + for( Text header : table.getHeaders() ) + { + buf.writeText( header ); + } + } + + buf.writeVarInt( table.getRows() + .size() ); + for( Text[] row : table.getRows() ) + { + for( Text column : row ) + { + buf.writeText( column ); + } + } + + buf.writeVarInt( table.getAdditional() ); + } + @Override @Environment( EnvType.CLIENT ) public void handle( PacketContext context ) diff --git a/src/main/java/dan200/computercraft/shared/network/client/ComputerClientMessage.java b/src/main/java/dan200/computercraft/shared/network/client/ComputerClientMessage.java index 792de6845..ad04ce2aa 100644 --- a/src/main/java/dan200/computercraft/shared/network/client/ComputerClientMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/client/ComputerClientMessage.java @@ -18,15 +18,16 @@ import javax.annotation.Nonnull; */ public abstract class ComputerClientMessage implements NetworkMessage { - private int instanceId; + private final int instanceId; public ComputerClientMessage( int instanceId ) { this.instanceId = instanceId; } - public ComputerClientMessage() + public ComputerClientMessage( @Nonnull PacketByteBuf buf ) { + instanceId = buf.readVarInt(); } public int getInstanceId() @@ -40,12 +41,6 @@ public abstract class ComputerClientMessage implements NetworkMessage buf.writeVarInt( instanceId ); } - @Override - public void fromBytes( @Nonnull PacketByteBuf buf ) - { - instanceId = buf.readVarInt(); - } - public ClientComputer getComputer() { ClientComputer computer = ComputerCraft.clientComputerRegistry.get( instanceId ); diff --git a/src/main/java/dan200/computercraft/shared/network/client/ComputerDataClientMessage.java b/src/main/java/dan200/computercraft/shared/network/client/ComputerDataClientMessage.java index 568338fe1..405d7e29e 100644 --- a/src/main/java/dan200/computercraft/shared/network/client/ComputerDataClientMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/client/ComputerDataClientMessage.java @@ -19,8 +19,8 @@ import javax.annotation.Nonnull; */ public class ComputerDataClientMessage extends ComputerClientMessage { - private ComputerState state; - private NbtCompound userData; + private final ComputerState state; + private final NbtCompound userData; public ComputerDataClientMessage( ServerComputer computer ) { @@ -29,8 +29,11 @@ public class ComputerDataClientMessage extends ComputerClientMessage userData = computer.getUserData(); } - public ComputerDataClientMessage() + public ComputerDataClientMessage( @Nonnull PacketByteBuf buf ) { + super( buf ); + state = buf.readEnumConstant( ComputerState.class ); + userData = buf.readNbt(); } @Override @@ -41,14 +44,6 @@ public class ComputerDataClientMessage extends ComputerClientMessage buf.writeNbt( userData ); } - @Override - public void fromBytes( @Nonnull PacketByteBuf buf ) - { - super.fromBytes( buf ); - state = buf.readEnumConstant( ComputerState.class ); - userData = buf.readNbt(); - } - @Override public void handle( PacketContext context ) { diff --git a/src/main/java/dan200/computercraft/shared/network/client/ComputerDeletedClientMessage.java b/src/main/java/dan200/computercraft/shared/network/client/ComputerDeletedClientMessage.java index 4883e6610..7ae774055 100644 --- a/src/main/java/dan200/computercraft/shared/network/client/ComputerDeletedClientMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/client/ComputerDeletedClientMessage.java @@ -8,6 +8,7 @@ package dan200.computercraft.shared.network.client; import dan200.computercraft.ComputerCraft; import net.fabricmc.fabric.api.network.PacketContext; +import net.minecraft.network.PacketByteBuf; public class ComputerDeletedClientMessage extends ComputerClientMessage { @@ -16,8 +17,9 @@ public class ComputerDeletedClientMessage extends ComputerClientMessage super( instanceId ); } - public ComputerDeletedClientMessage() + public ComputerDeletedClientMessage( PacketByteBuf buffer ) { + super( buffer ); } @Override diff --git a/src/main/java/dan200/computercraft/shared/network/client/ComputerTerminalClientMessage.java b/src/main/java/dan200/computercraft/shared/network/client/ComputerTerminalClientMessage.java index 75ddb5250..a6a7b745c 100644 --- a/src/main/java/dan200/computercraft/shared/network/client/ComputerTerminalClientMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/client/ComputerTerminalClientMessage.java @@ -13,7 +13,7 @@ import javax.annotation.Nonnull; public class ComputerTerminalClientMessage extends ComputerClientMessage { - private TerminalState state; + private final TerminalState state; public ComputerTerminalClientMessage( int instanceId, TerminalState state ) { @@ -21,8 +21,10 @@ public class ComputerTerminalClientMessage extends ComputerClientMessage this.state = state; } - public ComputerTerminalClientMessage() + public ComputerTerminalClientMessage( @Nonnull PacketByteBuf buf ) { + super( buf ); + state = new TerminalState( buf ); } @Override @@ -32,13 +34,6 @@ public class ComputerTerminalClientMessage extends ComputerClientMessage state.write( buf ); } - @Override - public void fromBytes( @Nonnull PacketByteBuf buf ) - { - super.fromBytes( buf ); - state = new TerminalState( buf ); - } - @Override public void handle( PacketContext context ) { diff --git a/src/main/java/dan200/computercraft/shared/network/client/PlayRecordClientMessage.java b/src/main/java/dan200/computercraft/shared/network/client/PlayRecordClientMessage.java index 3e623a892..35587fdcf 100644 --- a/src/main/java/dan200/computercraft/shared/network/client/PlayRecordClientMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/client/PlayRecordClientMessage.java @@ -6,7 +6,6 @@ package dan200.computercraft.shared.network.client; -import dan200.computercraft.fabric.mixin.SoundEventAccess; import dan200.computercraft.shared.network.NetworkMessage; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; @@ -74,7 +73,7 @@ public class PlayRecordClientMessage implements NetworkMessage { buf.writeBoolean( true ); buf.writeString( name ); - buf.writeIdentifier( ((SoundEventAccess) soundEvent).getId() ); + buf.writeIdentifier( soundEvent.getId() ); } } diff --git a/src/main/java/dan200/computercraft/shared/network/client/SpeakerMoveClientMessage.java b/src/main/java/dan200/computercraft/shared/network/client/SpeakerMoveClientMessage.java new file mode 100644 index 000000000..9bfe74a03 --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/network/client/SpeakerMoveClientMessage.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.shared.network.client; + +import dan200.computercraft.client.SoundManager; +import dan200.computercraft.shared.network.NetworkMessage; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.network.PacketContext; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.util.math.Vec3d; + +import javax.annotation.Nonnull; +import java.util.UUID; + +/** + * Starts a sound on the client. + * + * Used by speakers to play sounds. + * + * @see dan200.computercraft.shared.peripheral.speaker.TileSpeaker + */ +public class SpeakerMoveClientMessage implements NetworkMessage +{ + private final UUID source; + private final Vec3d pos; + + public SpeakerMoveClientMessage( UUID source, Vec3d pos ) + { + this.source = source; + this.pos = pos; + } + + public SpeakerMoveClientMessage( PacketByteBuf buf ) + { + source = buf.readUuid(); + pos = new Vec3d( buf.readDouble(), buf.readDouble(), buf.readDouble() ); + } + + @Override + public void toBytes( @Nonnull PacketByteBuf buf ) + { + buf.writeUuid( source ); + buf.writeDouble( pos.getX() ); + buf.writeDouble( pos.getY() ); + buf.writeDouble( pos.getZ() ); + } + + @Override + @Environment( EnvType.CLIENT ) + public void handle( PacketContext context ) + { + SoundManager.moveSound( source, pos ); + } +} diff --git a/src/main/java/dan200/computercraft/shared/network/client/SpeakerPlayClientMessage.java b/src/main/java/dan200/computercraft/shared/network/client/SpeakerPlayClientMessage.java new file mode 100644 index 000000000..97e8d3b69 --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/network/client/SpeakerPlayClientMessage.java @@ -0,0 +1,73 @@ +/* + * 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.network.client; + +import dan200.computercraft.client.SoundManager; +import dan200.computercraft.shared.network.NetworkMessage; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.network.PacketContext; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.sound.SoundEvent; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.Vec3d; + +import javax.annotation.Nonnull; +import java.util.UUID; + +/** + * Starts a sound on the client. + * + * Used by speakers to play sounds. + * + * @see dan200.computercraft.shared.peripheral.speaker.TileSpeaker + */ +public class SpeakerPlayClientMessage implements NetworkMessage +{ + private final UUID source; + private final Vec3d pos; + private final Identifier sound; + private final float volume; + private final float pitch; + + public SpeakerPlayClientMessage( UUID source, Vec3d pos, Identifier event, float volume, float pitch ) + { + this.source = source; + this.pos = pos; + sound = event; + this.volume = volume; + this.pitch = pitch; + } + + public SpeakerPlayClientMessage( PacketByteBuf buf ) + { + source = buf.readUuid(); + pos = new Vec3d( buf.readDouble(), buf.readDouble(), buf.readDouble() ); + sound = buf.readIdentifier(); + volume = buf.readFloat(); + pitch = buf.readFloat(); + } + + @Override + public void toBytes( @Nonnull PacketByteBuf buf ) + { + buf.writeUuid( source ); + buf.writeDouble( pos.getX() ); + buf.writeDouble( pos.getY() ); + buf.writeDouble( pos.getZ() ); + buf.writeIdentifier( sound ); + buf.writeFloat( volume ); + buf.writeFloat( pitch ); + } + + @Override + @Environment( EnvType.CLIENT ) + public void handle( PacketContext context ) + { + SoundEvent sound = new SoundEvent( this.sound ); + SoundManager.playSound( source, pos, sound, volume, pitch ); + } +} diff --git a/src/main/java/dan200/computercraft/shared/network/client/SpeakerStopClientMessage.java b/src/main/java/dan200/computercraft/shared/network/client/SpeakerStopClientMessage.java new file mode 100644 index 000000000..bdc630244 --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/network/client/SpeakerStopClientMessage.java @@ -0,0 +1,51 @@ +/* + * 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.network.client; + +import dan200.computercraft.client.SoundManager; +import dan200.computercraft.shared.network.NetworkMessage; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.network.PacketContext; +import net.minecraft.network.PacketByteBuf; + +import javax.annotation.Nonnull; +import java.util.UUID; + +/** + * Stops a sound on the client + * + * Called when a speaker is broken. + * + * @see dan200.computercraft.shared.peripheral.speaker.TileSpeaker + */ +public class SpeakerStopClientMessage implements NetworkMessage +{ + private final UUID source; + + public SpeakerStopClientMessage( UUID source ) + { + this.source = source; + } + + public SpeakerStopClientMessage( PacketByteBuf buf ) + { + source = buf.readUuid(); + } + + @Override + public void toBytes( @Nonnull PacketByteBuf buf ) + { + buf.writeUuid( source ); + } + + @Override + @Environment( EnvType.CLIENT ) + public void handle( PacketContext context ) + { + SoundManager.stopSound( source ); + } +} diff --git a/src/main/java/dan200/computercraft/shared/network/client/UploadResultMessage.java b/src/main/java/dan200/computercraft/shared/network/client/UploadResultMessage.java new file mode 100644 index 000000000..012908bd4 --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/network/client/UploadResultMessage.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.shared.network.client; + +import dan200.computercraft.client.gui.ComputerScreenBase; +import dan200.computercraft.client.gui.OptionScreen; +import dan200.computercraft.shared.computer.upload.UploadResult; +import dan200.computercraft.shared.network.NetworkMessage; +import net.fabricmc.fabric.api.network.PacketContext; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.screen.Screen; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.text.Text; + +import javax.annotation.Nonnull; + +public class UploadResultMessage implements NetworkMessage +{ + public static final UploadResultMessage COMPUTER_OFF = new UploadResultMessage( UploadResult.ERROR, UploadResult.COMPUTER_OFF_MSG ); + public static final UploadResultMessage OUT_OF_SPACE = new UploadResultMessage( UploadResult.ERROR, UploadResult.OUT_OF_SPACE_MSG ); + + private final UploadResult result; + private final Text message; + + public UploadResultMessage( UploadResult result, Text message ) + { + this.result = result; + this.message = message; + } + + public UploadResultMessage( @Nonnull PacketByteBuf buf ) + { + result = buf.readEnumConstant( UploadResult.class ); + message = buf.readText(); + } + + @Override + public void toBytes( @Nonnull PacketByteBuf buf ) + { + buf.writeEnumConstant( result ); + buf.writeText( message ); + } + + @Override + public void handle( PacketContext context ) + { + MinecraftClient minecraft = MinecraftClient.getInstance(); + + Screen screen = OptionScreen.unwrap( minecraft.currentScreen ); + if( screen instanceof ComputerScreenBase ) + { + ((ComputerScreenBase) screen).uploadResult( result, message ); + } + } +} diff --git a/src/main/java/dan200/computercraft/shared/network/container/ComputerContainerData.java b/src/main/java/dan200/computercraft/shared/network/container/ComputerContainerData.java index 5b97cca97..7edd25185 100644 --- a/src/main/java/dan200/computercraft/shared/network/container/ComputerContainerData.java +++ b/src/main/java/dan200/computercraft/shared/network/container/ComputerContainerData.java @@ -6,17 +6,14 @@ package dan200.computercraft.shared.network.container; -import dan200.computercraft.ComputerCraft; import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ServerComputer; import net.minecraft.network.PacketByteBuf; -import net.minecraft.util.Identifier; public class ComputerContainerData implements ContainerData { - private static final Identifier IDENTIFIER = new Identifier( ComputerCraft.MOD_ID, "computer_container_data" ); - private int id; - private ComputerFamily family; + private final int id; + private final ComputerFamily family; public ComputerContainerData( ServerComputer computer ) { @@ -26,18 +23,8 @@ public class ComputerContainerData implements ContainerData public ComputerContainerData( PacketByteBuf byteBuf ) { - fromBytes( byteBuf ); - } - - public void fromBytes( PacketByteBuf buf ) - { - id = buf.readInt(); - family = buf.readEnumConstant( ComputerFamily.class ); - } - - public Identifier getId() - { - return IDENTIFIER; + id = byteBuf.readInt(); + family = byteBuf.readEnumConstant( ComputerFamily.class ); } @Override diff --git a/src/main/java/dan200/computercraft/shared/network/container/ContainerData.java b/src/main/java/dan200/computercraft/shared/network/container/ContainerData.java index 10f167418..c29d774b8 100644 --- a/src/main/java/dan200/computercraft/shared/network/container/ContainerData.java +++ b/src/main/java/dan200/computercraft/shared/network/container/ContainerData.java @@ -24,18 +24,27 @@ import java.util.function.Function; public interface ContainerData { static ScreenHandlerType toType( Identifier identifier, Function reader, - Factory factory ) + Factory factory ) { return ScreenHandlerRegistry.registerExtended( identifier, ( id, playerInventory, packetByteBuf ) -> factory.create( id, playerInventory, reader.apply( packetByteBuf ) ) ); } + static ScreenHandlerType toType( Identifier identifier, ScreenHandlerType type, Function reader, + FixedFactory factory ) + { + return ScreenHandlerRegistry.registerExtended( identifier, + ( id, playerInventory, packetByteBuf ) -> factory.create( type, id, + playerInventory, + reader.apply( packetByteBuf ) ) ); + } void toBytes( PacketByteBuf buf ); default void open( PlayerEntity player, NamedScreenHandlerFactory owner ) { + if ( player.world.isClient ) return; player.openHandledScreen( owner ); } @@ -43,4 +52,9 @@ public interface ContainerData { C create( int id, @Nonnull PlayerInventory inventory, T data ); } + + interface FixedFactory + { + C create( ScreenHandlerType type, int id, @Nonnull PlayerInventory inventory, T data ); + } } diff --git a/src/main/java/dan200/computercraft/shared/network/container/ViewComputerContainerData.java b/src/main/java/dan200/computercraft/shared/network/container/ViewComputerContainerData.java index 7d5943804..7999ca681 100644 --- a/src/main/java/dan200/computercraft/shared/network/container/ViewComputerContainerData.java +++ b/src/main/java/dan200/computercraft/shared/network/container/ViewComputerContainerData.java @@ -6,11 +6,9 @@ package dan200.computercraft.shared.network.container; -import dan200.computercraft.ComputerCraft; import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.shared.computer.core.ServerComputer; import net.minecraft.network.PacketByteBuf; -import net.minecraft.util.Identifier; import javax.annotation.Nonnull; @@ -21,9 +19,8 @@ import javax.annotation.Nonnull; */ public class ViewComputerContainerData extends ComputerContainerData { - private static final Identifier IDENTIFIER = new Identifier( ComputerCraft.MOD_ID, "view_computer_container_data" ); - private int width; - private int height; + private final int width; + private final int height; public ViewComputerContainerData( ServerComputer computer ) { @@ -40,24 +37,11 @@ public class ViewComputerContainerData extends ComputerContainerData } } - public ViewComputerContainerData( PacketByteBuf packetByteBuf ) + public ViewComputerContainerData( PacketByteBuf buffer ) { - super( new PacketByteBuf( packetByteBuf.copy() ) ); - fromBytes( packetByteBuf ); - } - - @Override - public void fromBytes( PacketByteBuf buf ) - { - super.fromBytes( buf ); - width = buf.readVarInt(); - height = buf.readVarInt(); - } - - @Override - public Identifier getId() - { - return IDENTIFIER; + super( buffer ); + width = buffer.readVarInt(); + height = buffer.readVarInt(); } @Override diff --git a/src/main/java/dan200/computercraft/shared/network/server/ComputerActionServerMessage.java b/src/main/java/dan200/computercraft/shared/network/server/ComputerActionServerMessage.java index 0bdc624e8..00395bbca 100644 --- a/src/main/java/dan200/computercraft/shared/network/server/ComputerActionServerMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/server/ComputerActionServerMessage.java @@ -8,13 +8,14 @@ package dan200.computercraft.shared.network.server; import dan200.computercraft.shared.computer.core.IContainerComputer; import dan200.computercraft.shared.computer.core.ServerComputer; +import net.fabricmc.fabric.api.network.PacketContext; import net.minecraft.network.PacketByteBuf; import javax.annotation.Nonnull; public class ComputerActionServerMessage extends ComputerServerMessage { - private Action action; + private final Action action; public ComputerActionServerMessage( int instanceId, Action action ) { @@ -22,8 +23,10 @@ public class ComputerActionServerMessage extends ComputerServerMessage this.action = action; } - public ComputerActionServerMessage() + public ComputerActionServerMessage( @Nonnull PacketByteBuf buf ) { + super( buf ); + action = buf.readEnumConstant( Action.class ); } @Override @@ -34,14 +37,7 @@ public class ComputerActionServerMessage extends ComputerServerMessage } @Override - public void fromBytes( @Nonnull PacketByteBuf buf ) - { - super.fromBytes( buf ); - action = buf.readEnumConstant( Action.class ); - } - - @Override - protected void handle( @Nonnull ServerComputer computer, @Nonnull IContainerComputer container ) + protected void handle( PacketContext context, @Nonnull ServerComputer computer, @Nonnull IContainerComputer container ) { switch( action ) { diff --git a/src/main/java/dan200/computercraft/shared/network/server/ComputerServerMessage.java b/src/main/java/dan200/computercraft/shared/network/server/ComputerServerMessage.java index a97bc299b..3acf35e38 100644 --- a/src/main/java/dan200/computercraft/shared/network/server/ComputerServerMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/server/ComputerServerMessage.java @@ -22,15 +22,16 @@ import javax.annotation.Nonnull; */ public abstract class ComputerServerMessage implements NetworkMessage { - private int instanceId; + private final int instanceId; public ComputerServerMessage( int instanceId ) { this.instanceId = instanceId; } - public ComputerServerMessage() + public ComputerServerMessage( @Nonnull PacketByteBuf buf ) { + instanceId = buf.readVarInt(); } @Override @@ -39,12 +40,6 @@ public abstract class ComputerServerMessage implements NetworkMessage buf.writeVarInt( instanceId ); } - @Override - public void fromBytes( @Nonnull PacketByteBuf buf ) - { - instanceId = buf.readVarInt(); - } - @Override public void handle( PacketContext context ) { @@ -60,8 +55,8 @@ public abstract class ComputerServerMessage implements NetworkMessage return; } - handle( computer, container ); + handle( context, computer, container ); } - protected abstract void handle( @Nonnull ServerComputer computer, @Nonnull IContainerComputer container ); + protected abstract void handle( PacketContext context, @Nonnull ServerComputer computer, @Nonnull IContainerComputer container ); } diff --git a/src/main/java/dan200/computercraft/shared/network/server/ContinueUploadMessage.java b/src/main/java/dan200/computercraft/shared/network/server/ContinueUploadMessage.java new file mode 100644 index 000000000..557433f63 --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/network/server/ContinueUploadMessage.java @@ -0,0 +1,45 @@ +/* + * 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.network.server; + +import dan200.computercraft.shared.computer.core.IContainerComputer; +import dan200.computercraft.shared.computer.core.ServerComputer; +import net.fabricmc.fabric.api.network.PacketContext; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.server.network.ServerPlayerEntity; + +import javax.annotation.Nonnull; + +public class ContinueUploadMessage extends ComputerServerMessage +{ + private final boolean overwrite; + + public ContinueUploadMessage( int instanceId, boolean overwrite ) + { + super( instanceId ); + this.overwrite = overwrite; + } + + public ContinueUploadMessage( @Nonnull PacketByteBuf buf ) + { + super( buf ); + overwrite = buf.readBoolean(); + } + + @Override + public void toBytes( @Nonnull PacketByteBuf buf ) + { + super.toBytes( buf ); + buf.writeBoolean( overwrite ); + } + + @Override + protected void handle( PacketContext context, @Nonnull ServerComputer computer, @Nonnull IContainerComputer container ) + { + ServerPlayerEntity player = (ServerPlayerEntity) context.getPlayer(); + if( player != null ) container.confirmUpload( player, overwrite ); + } +} diff --git a/src/main/java/dan200/computercraft/shared/network/server/KeyEventServerMessage.java b/src/main/java/dan200/computercraft/shared/network/server/KeyEventServerMessage.java index 03b7c11f2..cf00e23f0 100644 --- a/src/main/java/dan200/computercraft/shared/network/server/KeyEventServerMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/server/KeyEventServerMessage.java @@ -9,6 +9,7 @@ package dan200.computercraft.shared.network.server; import dan200.computercraft.shared.computer.core.IContainerComputer; import dan200.computercraft.shared.computer.core.InputState; import dan200.computercraft.shared.computer.core.ServerComputer; +import net.fabricmc.fabric.api.network.PacketContext; import net.minecraft.network.PacketByteBuf; import javax.annotation.Nonnull; @@ -19,8 +20,8 @@ public class KeyEventServerMessage extends ComputerServerMessage public static final int TYPE_REPEAT = 1; public static final int TYPE_UP = 2; - private int type; - private int key; + private final int type; + private final int key; public KeyEventServerMessage( int instanceId, int type, int key ) { @@ -29,8 +30,11 @@ public class KeyEventServerMessage extends ComputerServerMessage this.key = key; } - public KeyEventServerMessage() + public KeyEventServerMessage( @Nonnull PacketByteBuf buf ) { + super( buf ); + type = buf.readByte(); + key = buf.readVarInt(); } @Override @@ -42,15 +46,7 @@ public class KeyEventServerMessage extends ComputerServerMessage } @Override - public void fromBytes( @Nonnull PacketByteBuf buf ) - { - super.fromBytes( buf ); - type = buf.readByte(); - key = buf.readVarInt(); - } - - @Override - protected void handle( @Nonnull ServerComputer computer, @Nonnull IContainerComputer container ) + protected void handle( PacketContext context, @Nonnull ServerComputer computer, @Nonnull IContainerComputer container ) { InputState input = container.getInput(); if( type == TYPE_UP ) diff --git a/src/main/java/dan200/computercraft/shared/network/server/MouseEventServerMessage.java b/src/main/java/dan200/computercraft/shared/network/server/MouseEventServerMessage.java index fbccad974..872f168ed 100644 --- a/src/main/java/dan200/computercraft/shared/network/server/MouseEventServerMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/server/MouseEventServerMessage.java @@ -9,6 +9,7 @@ package dan200.computercraft.shared.network.server; import dan200.computercraft.shared.computer.core.IContainerComputer; import dan200.computercraft.shared.computer.core.InputState; import dan200.computercraft.shared.computer.core.ServerComputer; +import net.fabricmc.fabric.api.network.PacketContext; import net.minecraft.network.PacketByteBuf; import javax.annotation.Nonnull; @@ -20,10 +21,10 @@ public class MouseEventServerMessage extends ComputerServerMessage public static final int TYPE_UP = 2; public static final int TYPE_SCROLL = 3; - private int type; - private int x; - private int y; - private int arg; + private final int type; + private final int x; + private final int y; + private final int arg; public MouseEventServerMessage( int instanceId, int type, int arg, int x, int y ) { @@ -34,8 +35,13 @@ public class MouseEventServerMessage extends ComputerServerMessage this.y = y; } - public MouseEventServerMessage() + public MouseEventServerMessage( @Nonnull PacketByteBuf buf ) { + super( buf ); + type = buf.readByte(); + arg = buf.readVarInt(); + x = buf.readVarInt(); + y = buf.readVarInt(); } @Override @@ -49,17 +55,7 @@ public class MouseEventServerMessage extends ComputerServerMessage } @Override - public void fromBytes( @Nonnull PacketByteBuf buf ) - { - super.fromBytes( buf ); - type = buf.readByte(); - arg = buf.readVarInt(); - x = buf.readVarInt(); - y = buf.readVarInt(); - } - - @Override - protected void handle( @Nonnull ServerComputer computer, @Nonnull IContainerComputer container ) + protected void handle( PacketContext context, @Nonnull ServerComputer computer, @Nonnull IContainerComputer container ) { InputState input = container.getInput(); switch( type ) diff --git a/src/main/java/dan200/computercraft/shared/network/server/QueueEventServerMessage.java b/src/main/java/dan200/computercraft/shared/network/server/QueueEventServerMessage.java index 69f94a300..730d7b6f9 100644 --- a/src/main/java/dan200/computercraft/shared/network/server/QueueEventServerMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/server/QueueEventServerMessage.java @@ -9,6 +9,7 @@ package dan200.computercraft.shared.network.server; import dan200.computercraft.shared.computer.core.IContainerComputer; import dan200.computercraft.shared.computer.core.ServerComputer; import dan200.computercraft.shared.util.NBTUtil; +import net.fabricmc.fabric.api.network.PacketContext; import net.minecraft.nbt.NbtCompound; import net.minecraft.network.PacketByteBuf; @@ -23,8 +24,8 @@ import javax.annotation.Nullable; */ public class QueueEventServerMessage extends ComputerServerMessage { - private String event; - private Object[] args; + private final String event; + private final Object[] args; public QueueEventServerMessage( int instanceId, @Nonnull String event, @Nullable Object[] args ) { @@ -33,8 +34,13 @@ public class QueueEventServerMessage extends ComputerServerMessage this.args = args; } - public QueueEventServerMessage() + public QueueEventServerMessage( @Nonnull PacketByteBuf buf ) { + super( buf ); + event = buf.readString( Short.MAX_VALUE ); + + NbtCompound args = buf.readNbt(); + this.args = args == null ? null : NBTUtil.decodeObjects( args ); } @Override @@ -46,17 +52,7 @@ public class QueueEventServerMessage extends ComputerServerMessage } @Override - public void fromBytes( @Nonnull PacketByteBuf buf ) - { - super.fromBytes( buf ); - event = buf.readString( Short.MAX_VALUE ); - - NbtCompound args = buf.readNbt(); - this.args = args == null ? null : NBTUtil.decodeObjects( args ); - } - - @Override - protected void handle( @Nonnull ServerComputer computer, @Nonnull IContainerComputer container ) + protected void handle( PacketContext context, @Nonnull ServerComputer computer, @Nonnull IContainerComputer container ) { computer.queueEvent( event, args ); } diff --git a/src/main/java/dan200/computercraft/shared/network/server/RequestComputerMessage.java b/src/main/java/dan200/computercraft/shared/network/server/RequestComputerMessage.java index 2a0afddac..af7f8b29d 100644 --- a/src/main/java/dan200/computercraft/shared/network/server/RequestComputerMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/server/RequestComputerMessage.java @@ -16,15 +16,16 @@ import javax.annotation.Nonnull; public class RequestComputerMessage implements NetworkMessage { - private int instance; + private final int instance; public RequestComputerMessage( int instance ) { this.instance = instance; } - public RequestComputerMessage() + public RequestComputerMessage( @Nonnull PacketByteBuf buf ) { + instance = buf.readVarInt(); } @Override @@ -33,12 +34,6 @@ public class RequestComputerMessage implements NetworkMessage buf.writeVarInt( instance ); } - @Override - public void fromBytes( @Nonnull PacketByteBuf buf ) - { - instance = buf.readVarInt(); - } - @Override public void handle( PacketContext context ) { diff --git a/src/main/java/dan200/computercraft/shared/network/server/UploadFileMessage.java b/src/main/java/dan200/computercraft/shared/network/server/UploadFileMessage.java new file mode 100644 index 000000000..136ce08bb --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/network/server/UploadFileMessage.java @@ -0,0 +1,186 @@ +/* + * 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.network.server; + +import dan200.computercraft.ComputerCraft; +import dan200.computercraft.shared.computer.core.IContainerComputer; +import dan200.computercraft.shared.computer.core.ServerComputer; +import dan200.computercraft.shared.computer.upload.FileSlice; +import dan200.computercraft.shared.computer.upload.FileUpload; +import dan200.computercraft.shared.network.NetworkHandler; +import io.netty.handler.codec.DecoderException; +import net.fabricmc.fabric.api.network.PacketContext; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.server.network.ServerPlayerEntity; + +import javax.annotation.Nonnull; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +public class UploadFileMessage extends ComputerServerMessage +{ + public static final int MAX_SIZE = 512 * 1024; + static final int MAX_PACKET_SIZE = 30 * 1024; // Max packet size is 32767. + + public static final int MAX_FILES = 32; + public static final int MAX_FILE_NAME = 128; + + private static final int FLAG_FIRST = 1; + private static final int FLAG_LAST = 2; + + private final UUID uuid; + private final int flag; + private final List files; + private final List slices; + + UploadFileMessage( int instanceId, UUID uuid, int flag, List files, List slices ) + { + super( instanceId ); + this.uuid = uuid; + this.flag = flag; + this.files = files; + this.slices = slices; + } + + public UploadFileMessage( @Nonnull PacketByteBuf buf ) + { + super( buf ); + uuid = buf.readUuid(); + int flag = this.flag = buf.readByte(); + + int totalSize = 0; + if( (flag & FLAG_FIRST) != 0 ) + { + int nFiles = buf.readVarInt(); + if( nFiles >= MAX_FILES ) throw new DecoderException( "Too many files" ); + + List files = this.files = new ArrayList<>( nFiles ); + for( int i = 0; i < nFiles; i++ ) + { + String name = buf.readString( MAX_FILE_NAME ); + int size = buf.readVarInt(); + if( size > MAX_SIZE || (totalSize += size) >= MAX_SIZE ) + { + throw new DecoderException( "Files are too large" ); + } + + byte[] digest = new byte[FileUpload.CHECKSUM_LENGTH]; + buf.readBytes( digest ); + + files.add( new FileUpload( name, ByteBuffer.allocateDirect( size ), digest ) ); + } + } + else + { + files = null; + } + + int nSlices = buf.readVarInt(); + List slices = this.slices = new ArrayList<>( nSlices ); + for( int i = 0; i < nSlices; i++ ) + { + int fileId = buf.readUnsignedByte(); + int offset = buf.readVarInt(); + + int size = buf.readUnsignedShort(); + if( size > MAX_PACKET_SIZE ) throw new DecoderException( "File is too large" ); + + ByteBuffer buffer = ByteBuffer.allocateDirect( size ); + buf.readBytes( buffer ); + buffer.flip(); + + slices.add( new FileSlice( fileId, offset, buffer ) ); + } + } + + @Override + public void toBytes( @Nonnull PacketByteBuf buf ) + { + super.toBytes( buf ); + buf.writeUuid( uuid ); + buf.writeByte( flag ); + + if( (flag & FLAG_FIRST) != 0 ) + { + buf.writeVarInt( files.size() ); + for( FileUpload file : files ) + { + buf.writeString( file.getName(), MAX_FILE_NAME ); + buf.writeVarInt( file.getLength() ); + buf.writeBytes( file.getChecksum() ); + } + } + + buf.writeVarInt( slices.size() ); + for( FileSlice slice : slices ) + { + buf.writeByte( slice.getFileId() ); + buf.writeVarInt( slice.getOffset() ); + + slice.getBytes().rewind(); + buf.writeShort( slice.getBytes().remaining() ); + buf.writeBytes( slice.getBytes() ); + } + } + + public static void send( int instanceId, List files ) + { + UUID uuid = UUID.randomUUID(); + + int remaining = MAX_PACKET_SIZE; + for( FileUpload file : files ) remaining -= file.getName().length() * 4 + FileUpload.CHECKSUM_LENGTH; + + boolean first = true; + List slices = new ArrayList<>( files.size() ); + for( int fileId = 0; fileId < files.size(); fileId++ ) + { + FileUpload file = files.get( fileId ); + ByteBuffer contents = file.getBytes(); + int capacity = contents.limit(); + + int currentOffset = 0; + while( currentOffset < capacity ) + { + if( remaining <= 0 ) + { + NetworkHandler.sendToServer( first + ? new UploadFileMessage( instanceId, uuid, FLAG_FIRST, files, new ArrayList<>( slices ) ) + : new UploadFileMessage( instanceId, uuid, 0, null, new ArrayList<>( slices ) ) ); + slices.clear(); + remaining = MAX_PACKET_SIZE; + first = false; + } + + int canWrite = Math.min( remaining, capacity - currentOffset ); + + ComputerCraft.log.info( "Adding slice from {} to {}", currentOffset, currentOffset + canWrite - 1 ); + contents.position( currentOffset ).limit( currentOffset + canWrite ); + slices.add( new FileSlice( fileId, currentOffset, contents.slice() ) ); + currentOffset += canWrite; + } + + contents.position( 0 ).limit( capacity ); + } + + NetworkHandler.sendToServer( first + ? new UploadFileMessage( instanceId, uuid, FLAG_FIRST | FLAG_LAST, files, new ArrayList<>( slices ) ) + : new UploadFileMessage( instanceId, uuid, FLAG_LAST, null, new ArrayList<>( slices ) ) ); + } + + @Override + protected void handle( PacketContext context, @Nonnull ServerComputer computer, @Nonnull IContainerComputer container ) + { + ServerPlayerEntity player = (ServerPlayerEntity) context.getPlayer(); + if( player != null ) + { + if( (flag & FLAG_FIRST) != 0 ) container.startUpload( uuid, files ); + container.continueUpload( uuid, slices ); + if( (flag & FLAG_LAST) != 0 ) container.finishUpload( player, uuid ); + } + } +} diff --git a/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/BlockDiskDrive.java b/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/BlockDiskDrive.java index 35ec91d6f..295054c01 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/BlockDiskDrive.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/BlockDiskDrive.java @@ -11,6 +11,8 @@ import dan200.computercraft.shared.common.BlockGeneric; import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.BlockEntityTicker; +import net.minecraft.block.entity.BlockEntityType; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemPlacementContext; @@ -50,6 +52,13 @@ public class BlockDiskDrive extends BlockGeneric .getOpposite() ); } + @Nullable + @Override + public BlockEntityTicker getTicker( World world, BlockState state, BlockEntityType type ) + { + return world.isClient ? null : BlockDiskDrive.checkType( type, ComputerCraftRegistry.ModTiles.DISK_DRIVE, TileDiskDrive::tick ); + } + @Override public void afterBreak( @Nonnull World world, @Nonnull PlayerEntity player, @Nonnull BlockPos pos, @Nonnull BlockState state, @Nullable BlockEntity te, @Nonnull ItemStack stack @@ -88,4 +97,11 @@ public class BlockDiskDrive extends BlockGeneric { properties.add( FACING, STATE ); } + + @Nullable + @Override + public BlockEntity createBlockEntity( BlockPos pos, BlockState state ) + { + return new TileDiskDrive( ComputerCraftRegistry.ModTiles.DISK_DRIVE, pos, state ); + } } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/TileDiskDrive.java b/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/TileDiskDrive.java index f896b31bc..957f209b3 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/TileDiskDrive.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/TileDiskDrive.java @@ -32,10 +32,10 @@ import net.minecraft.text.TranslatableText; import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; import net.minecraft.util.Nameable; -import net.minecraft.util.Tickable; import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; +import net.minecraft.world.World; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -43,7 +43,7 @@ import java.util.HashMap; import java.util.Map; import java.util.Set; -public final class TileDiskDrive extends TileGeneric implements DefaultInventory, Tickable, IPeripheralTile, Nameable, NamedScreenHandlerFactory +public final class TileDiskDrive extends TileGeneric implements DefaultInventory, IPeripheralTile, Nameable, NamedScreenHandlerFactory { private static final String NBT_NAME = "CustomName"; private static final String NBT_ITEM = "Item"; @@ -57,9 +57,9 @@ public final class TileDiskDrive extends TileGeneric implements DefaultInventory private boolean restartRecord = false; private boolean ejectQueued; - public TileDiskDrive( BlockEntityType type ) + public TileDiskDrive( BlockEntityType type, BlockPos pos, BlockState state ) { - super( type ); + super( type, pos, state ); } @Override @@ -108,9 +108,9 @@ public final class TileDiskDrive extends TileGeneric implements DefaultInventory } @Override - public void readNbt( @Nonnull BlockState state, @Nonnull NbtCompound nbt ) + public void readNbt( @Nonnull NbtCompound nbt ) { - super.readNbt( state, nbt ); + super.readNbt( nbt ); customName = nbt.contains( NBT_NAME ) ? Text.Serializer.fromJson( nbt.getString( NBT_NAME ) ) : null; if( nbt.contains( NBT_ITEM ) ) { @@ -148,40 +148,39 @@ public final class TileDiskDrive extends TileGeneric implements DefaultInventory super.markDirty(); } - @Override - public void tick() + public static void tick( World world, BlockPos pos, BlockState state, TileDiskDrive tileDiskDrive ) { // Ejection - if( ejectQueued ) + if( tileDiskDrive.ejectQueued ) { - ejectContents( false ); - ejectQueued = false; + tileDiskDrive.ejectContents( false ); + tileDiskDrive.ejectQueued = false; } // Music - synchronized( this ) + synchronized( tileDiskDrive ) { - if( !world.isClient && recordPlaying != recordQueued || restartRecord ) + if( !world.isClient && tileDiskDrive.recordPlaying != tileDiskDrive.recordQueued || tileDiskDrive.restartRecord ) { - restartRecord = false; - if( recordQueued ) + tileDiskDrive.restartRecord = false; + if( tileDiskDrive.recordQueued ) { - IMedia contents = getDiskMedia(); - SoundEvent record = contents != null ? contents.getAudio( diskStack ) : null; + IMedia contents = tileDiskDrive.getDiskMedia(); + SoundEvent record = contents != null ? contents.getAudio( tileDiskDrive.diskStack ) : null; if( record != null ) { - recordPlaying = true; - playRecord(); + tileDiskDrive.recordPlaying = true; + tileDiskDrive.playRecord(); } else { - recordQueued = false; + tileDiskDrive.recordQueued = false; } } else { - stopRecord(); - recordPlaying = false; + tileDiskDrive.stopRecord(); + tileDiskDrive.recordPlaying = false; } } } @@ -479,7 +478,7 @@ public final class TileDiskDrive extends TileGeneric implements DefaultInventory private synchronized void ejectContents( boolean destroyed ) { - if( getWorld().isClient || diskStack.isEmpty() ) + if( this.world.isClient || diskStack.isEmpty() ) { return; } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/generic/data/ItemData.java b/src/main/java/dan200/computercraft/shared/peripheral/generic/data/ItemData.java index a5589be06..a5781483a 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/generic/data/ItemData.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/generic/data/ItemData.java @@ -13,11 +13,12 @@ import net.minecraft.item.EnchantedBookItem; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NbtCompound; -import net.minecraft.nbt.NbtElement; import net.minecraft.nbt.NbtList; +import net.minecraft.nbt.NbtElement; import net.minecraft.tag.ServerTagManagerHolder; import net.minecraft.tag.TagGroup; import net.minecraft.text.Text; +import net.minecraft.util.registry.Registry; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -71,7 +72,7 @@ public class ItemData // requireNonNull is safe because we got the Identifiers out of the TagGroup to start with. Would be nicer // to stream the tags directly but TagGroup isn't a collection :( - TagGroup itemTags = ServerTagManagerHolder.getTagManager().getItems(); + TagGroup itemTags = ServerTagManagerHolder.getTagManager().getOrCreateTagGroup( Registry.ITEM_KEY ); data.put( "tags", DataHelpers.getTags( itemTags.getTagIds().stream() .filter( id -> Objects.requireNonNull( itemTags.getTag( id ) ).contains( stack.getItem() ) ) .collect( Collectors.toList() ) diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/BlockCable.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/BlockCable.java index 27874fc9f..6388751e5 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/BlockCable.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/BlockCable.java @@ -271,4 +271,11 @@ public class BlockCable extends BlockGeneric implements Waterloggable .with( DOWN, false ); } } + + @Nullable + @Override + public BlockEntity createBlockEntity( BlockPos pos, BlockState state ) + { + return new TileCable( ComputerCraftRegistry.ModTiles.CABLE, pos, state ); + } } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/BlockWiredModemFull.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/BlockWiredModemFull.java index bdc49847e..a14875443 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/BlockWiredModemFull.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/BlockWiredModemFull.java @@ -6,12 +6,17 @@ package dan200.computercraft.shared.peripheral.modem.wired; +import dan200.computercraft.shared.ComputerCraftRegistry; import dan200.computercraft.shared.common.BlockGeneric; import net.minecraft.block.Block; import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntity; import net.minecraft.block.entity.BlockEntityType; import net.minecraft.state.StateManager; import net.minecraft.state.property.BooleanProperty; +import net.minecraft.util.math.BlockPos; + +import javax.annotation.Nullable; public class BlockWiredModemFull extends BlockGeneric { @@ -31,4 +36,11 @@ public class BlockWiredModemFull extends BlockGeneric { builder.add( MODEM_ON, PERIPHERAL_ON ); } + + @Nullable + @Override + public BlockEntity createBlockEntity( BlockPos pos, BlockState state ) + { + return new TileWiredModemFull( ComputerCraftRegistry.ModTiles.WIRED_MODEM_FULL, pos, state ); + } } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileCable.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileCable.java index 32f851d32..9795fa1ef 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileCable.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileCable.java @@ -74,9 +74,9 @@ public class TileCable extends TileGeneric implements IPeripheralTile private boolean hasModemDirection = false; private boolean connectionsFormed = false; - public TileCable( BlockEntityType type ) + public TileCable( BlockEntityType type, BlockPos pos, BlockState state ) { - super( type ); + super( type, pos, state ); } @Override @@ -119,7 +119,7 @@ public class TileCable extends TileGeneric implements IPeripheralTile return ActionResult.FAIL; } - if( getWorld().isClient ) + if( this.world.isClient ) { return ActionResult.SUCCESS; } @@ -377,9 +377,9 @@ public class TileCable extends TileGeneric implements IPeripheralTile } @Override - public void readNbt( @Nonnull BlockState state, @Nonnull NbtCompound nbt ) + public void readNbt( @Nonnull NbtCompound nbt ) { - super.readNbt( state, nbt ); + super.readNbt( nbt ); peripheralAccessAllowed = nbt.getBoolean( NBT_PERIPHERAL_ENABLED ); peripheral.read( nbt, "" ); } @@ -408,9 +408,10 @@ public class TileCable extends TileGeneric implements IPeripheralTile } @Override - public void resetBlock() + public void setCachedState( BlockState state ) { - super.resetBlock(); + super.setCachedState( state ); + if( state != null ) return; hasModemDirection = false; if( !world.isClient ) { diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileWiredModemFull.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileWiredModemFull.java index 5d2bb768a..abeebbe38 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileWiredModemFull.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/TileWiredModemFull.java @@ -49,9 +49,9 @@ public class TileWiredModemFull extends TileGeneric implements IPeripheralTile private boolean destroyed = false; private boolean connectionsFormed = false; - public TileWiredModemFull( BlockEntityType type ) + public TileWiredModemFull( BlockEntityType type, BlockPos pos, BlockState state ) { - super( type ); + super( type, pos, state ); for( int i = 0; i < peripherals.length; i++ ) { Direction facing = Direction.byId( i ); @@ -315,9 +315,9 @@ public class TileWiredModemFull extends TileGeneric implements IPeripheralTile } @Override - public void readNbt( @Nonnull BlockState state, @Nonnull NbtCompound nbt ) + public void readNbt( @Nonnull NbtCompound nbt ) { - super.readNbt( state, nbt ); + super.readNbt( nbt ); peripheralAccessAllowed = nbt.getBoolean( NBT_PERIPHERAL_ENABLED ); for( int i = 0; i < peripherals.length; i++ ) { diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/WiredModemPeripheral.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/WiredModemPeripheral.java index 90b7555b5..242354538 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/WiredModemPeripheral.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/WiredModemPeripheral.java @@ -7,6 +7,7 @@ package dan200.computercraft.shared.peripheral.modem.wired; import com.google.common.collect.ImmutableMap; +import dan200.computercraft.ComputerCraft; import dan200.computercraft.api.filesystem.IMount; import dan200.computercraft.api.filesystem.IWritableMount; import dan200.computercraft.api.lua.*; @@ -16,6 +17,7 @@ import dan200.computercraft.api.network.wired.IWiredSender; import dan200.computercraft.api.peripheral.IComputerAccess; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IWorkMonitor; +import dan200.computercraft.api.peripheral.NotAttachedException; import dan200.computercraft.core.apis.PeripheralAPI; import dan200.computercraft.core.asm.PeripheralMethod; import dan200.computercraft.shared.peripheral.modem.ModemPeripheral; @@ -24,10 +26,7 @@ import net.minecraft.world.World; import javax.annotation.Nonnull; import javax.annotation.Nullable; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.Objects; +import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; @@ -326,6 +325,9 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW private final String type; private final Map methodMap; + private volatile boolean attached; + private final Set mounts = new HashSet<>(); + RemotePeripheralWrapper( WiredModemElement element, IPeripheral peripheral, IComputerAccess computer, String name ) { this.element = element; @@ -339,6 +341,7 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW public void attach() { + attached = true; peripheral.attach( this ); computer.queueEvent( "peripheral", getAttachmentName() ); } @@ -347,6 +350,18 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW { peripheral.detach( this ); computer.queueEvent( "peripheral_detach", getAttachmentName() ); + attached = false; + + synchronized( this ) + { + if( !mounts.isEmpty() ) + { + ComputerCraft.log.warn( "Peripheral {} called mount but did not call unmount for {}", peripheral, mounts ); + } + + for( String mount : mounts ) computer.unmount( mount ); + mounts.clear(); + } } public String getType() @@ -372,51 +387,68 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW // IComputerAccess implementation @Override - public String mount( @Nonnull String desiredLocation, @Nonnull IMount mount ) + public synchronized String mount( @Nonnull String desiredLocation, @Nonnull IMount mount ) { - return computer.mount( desiredLocation, mount, name ); + if( !attached ) throw new NotAttachedException(); + String mounted = computer.mount( desiredLocation, mount, name ); + mounts.add( mounted ); + return mounted; } @Override - public String mount( @Nonnull String desiredLocation, @Nonnull IMount mount, @Nonnull String driveName ) + public synchronized String mount( @Nonnull String desiredLocation, @Nonnull IMount mount, @Nonnull String driveName ) { - return computer.mount( desiredLocation, mount, driveName ); + if( !attached ) throw new NotAttachedException(); + String mounted = computer.mount( desiredLocation, mount, driveName ); + mounts.add( mounted ); + return mounted; } @Nonnull @Override public String getAttachmentName() { + if( !attached ) throw new NotAttachedException(); return name; } @Override - public String mountWritable( @Nonnull String desiredLocation, @Nonnull IWritableMount mount ) + public synchronized String mountWritable( @Nonnull String desiredLocation, @Nonnull IWritableMount mount ) { - return computer.mountWritable( desiredLocation, mount, name ); + if( !attached ) throw new NotAttachedException(); + String mounted = computer.mountWritable( desiredLocation, mount, name ); + mounts.add( mounted ); + return mounted; } @Override - public String mountWritable( @Nonnull String desiredLocation, @Nonnull IWritableMount mount, @Nonnull String driveName ) + public synchronized String mountWritable( @Nonnull String desiredLocation, @Nonnull IWritableMount mount, @Nonnull String driveName ) { - return computer.mountWritable( desiredLocation, mount, driveName ); + if( !attached ) throw new NotAttachedException(); + String mounted = computer.mountWritable( desiredLocation, mount, driveName ); + mounts.add( mounted ); + return mounted; } @Override - public void unmount( String location ) + public synchronized void unmount( String location ) { + if( !attached ) throw new NotAttachedException(); computer.unmount( location ); + mounts.remove( location ); } @Override public int getID() { + if( !attached ) throw new NotAttachedException(); return computer.getID(); } @Override public void queueEvent( @Nonnull String event, Object... arguments ) { + if( !attached ) throw new NotAttachedException(); computer.queueEvent( event, arguments ); } @@ -424,6 +456,7 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW @Override public Map getAvailablePeripherals() { + if( !attached ) throw new NotAttachedException(); synchronized( element.getRemotePeripherals() ) { return ImmutableMap.copyOf( element.getRemotePeripherals() ); @@ -434,6 +467,7 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW @Override public IPeripheral getAvailablePeripheral( @Nonnull String name ) { + if( !attached ) throw new NotAttachedException(); synchronized( element.getRemotePeripherals() ) { return element.getRemotePeripherals() @@ -445,6 +479,7 @@ public abstract class WiredModemPeripheral extends ModemPeripheral implements IW @Override public IWorkMonitor getMainThreadMonitor() { + if( !attached ) throw new NotAttachedException(); return computer.getMainThreadMonitor(); } } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/wireless/BlockWirelessModem.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/wireless/BlockWirelessModem.java index 9ffeeab88..c6ebe3809 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/modem/wireless/BlockWirelessModem.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/wireless/BlockWirelessModem.java @@ -6,12 +6,15 @@ package dan200.computercraft.shared.peripheral.modem.wireless; +import dan200.computercraft.shared.ComputerCraftRegistry; import dan200.computercraft.shared.common.BlockGeneric; +import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.peripheral.modem.ModemShapes; import net.minecraft.block.Block; import net.minecraft.block.BlockState; import net.minecraft.block.ShapeContext; import net.minecraft.block.Waterloggable; +import net.minecraft.block.entity.BlockEntity; import net.minecraft.block.entity.BlockEntityType; import net.minecraft.fluid.FluidState; import net.minecraft.item.ItemPlacementContext; @@ -36,9 +39,12 @@ public class BlockWirelessModem extends BlockGeneric implements Waterloggable public static final DirectionProperty FACING = Properties.FACING; public static final BooleanProperty ON = BooleanProperty.of( "on" ); - public BlockWirelessModem( Settings settings, BlockEntityType type ) + private final ComputerFamily family; + + public BlockWirelessModem( Settings settings, BlockEntityType type, ComputerFamily family ) { super( settings, type ); + this.family = family; setDefaultState( getStateManager().getDefaultState() .with( FACING, Direction.NORTH ) .with( ON, false ) @@ -95,4 +101,20 @@ public class BlockWirelessModem extends BlockGeneric implements Waterloggable { builder.add( FACING, ON, WATERLOGGED ); } + + public BlockEntityType getTypeByFamily( ComputerFamily family ) + { + return switch ( family ) + { + case ADVANCED -> ComputerCraftRegistry.ModTiles.WIRELESS_MODEM_ADVANCED; + default -> ComputerCraftRegistry.ModTiles.WIRELESS_MODEM_NORMAL; + }; + } + + @Nullable + @Override + public BlockEntity createBlockEntity( BlockPos pos, BlockState state ) + { + return new TileWirelessModem( getTypeByFamily( family ), family == ComputerFamily.ADVANCED, pos, state ); + } } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/wireless/TileWirelessModem.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/wireless/TileWirelessModem.java index ac3f9e401..82d584a5b 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/modem/wireless/TileWirelessModem.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/wireless/TileWirelessModem.java @@ -29,9 +29,9 @@ public class TileWirelessModem extends TileGeneric implements IPeripheralTile private Direction modemDirection = Direction.DOWN; private boolean destroyed = false; - public TileWirelessModem( BlockEntityType type, boolean advanced ) + public TileWirelessModem( BlockEntityType type, boolean advanced, BlockPos pos, BlockState state ) { - super( type ); + super( type, pos, state ); this.advanced = advanced; modem = new Peripheral( this ); } @@ -44,9 +44,10 @@ public class TileWirelessModem extends TileGeneric implements IPeripheralTile } @Override - public void resetBlock() + public void setCachedState( BlockState state ) { - super.resetBlock(); + super.setCachedState( state ); + if( state != null ) return; hasModemDirection = false; world.getBlockTickScheduler() .schedule( getPos(), 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 cd427e379..22b3175e1 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/monitor/BlockMonitor.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/monitor/BlockMonitor.java @@ -7,6 +7,7 @@ package dan200.computercraft.shared.peripheral.monitor; import dan200.computercraft.api.turtle.FakePlayer; +import dan200.computercraft.shared.ComputerCraftRegistry; import dan200.computercraft.shared.common.BlockGeneric; import net.minecraft.block.Block; import net.minecraft.block.BlockState; @@ -34,9 +35,12 @@ public class BlockMonitor extends BlockGeneric static final EnumProperty STATE = EnumProperty.of( "state", MonitorEdgeState.class ); - public BlockMonitor( Settings settings, BlockEntityType type ) + public boolean advanced; + + public BlockMonitor( Settings settings, BlockEntityType type, boolean advanced ) { super( settings, type ); + this.advanced = advanced; // TODO: Test underwater - do we need isSolid at all? setDefaultState( getStateManager().getDefaultState() .with( ORIENTATION, Direction.NORTH ) @@ -48,7 +52,7 @@ public class BlockMonitor extends BlockGeneric @Nullable public BlockState getPlacementState( ItemPlacementContext context ) { - float pitch = context.getPlayer() == null ? 0 : context.getPlayer().pitch; + float pitch = context.getPlayer() == null ? 0 : context.getPlayer().getPitch(); Direction orientation; if( pitch > 66.5f ) { @@ -78,9 +82,8 @@ public class BlockMonitor extends BlockGeneric super.onPlaced( world, pos, blockState, livingEntity, itemStack ); BlockEntity entity = world.getBlockEntity( pos ); - if( entity instanceof TileMonitor && !world.isClient ) + if( entity instanceof TileMonitor monitor && !world.isClient ) { - TileMonitor monitor = (TileMonitor) entity; // Defer the block update if we're being placed by another TE. See #691 if( livingEntity == null || livingEntity instanceof FakePlayer ) { @@ -97,4 +100,11 @@ public class BlockMonitor extends BlockGeneric { builder.add( ORIENTATION, FACING, STATE ); } + + @Nullable + @Override + public BlockEntity createBlockEntity( BlockPos pos, BlockState state ) + { + return new TileMonitor( advanced ? ComputerCraftRegistry.ModTiles.MONITOR_ADVANCED : ComputerCraftRegistry.ModTiles.MONITOR_NORMAL, advanced, pos, state ); + } } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/monitor/ClientMonitor.java b/src/main/java/dan200/computercraft/shared/peripheral/monitor/ClientMonitor.java index eabaafb6f..db226210e 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/monitor/ClientMonitor.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/monitor/ClientMonitor.java @@ -8,7 +8,6 @@ package dan200.computercraft.shared.peripheral.monitor; import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.systems.RenderSystem; -import dan200.computercraft.client.gui.FixedWidthFontRenderer; import dan200.computercraft.shared.common.ClientTerminal; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; @@ -82,15 +81,15 @@ public final class ClientMonitor extends ClientTerminal deleteBuffers(); - tboBuffer = GlStateManager.genBuffers(); - GlStateManager.bindBuffers( GL31.GL_TEXTURE_BUFFER, tboBuffer ); + tboBuffer = GlStateManager._glGenBuffers(); + GlStateManager._glBindBuffer( GL31.GL_TEXTURE_BUFFER, tboBuffer ); GL15.glBufferData( GL31.GL_TEXTURE_BUFFER, 0, GL15.GL_STATIC_DRAW ); - tboTexture = GlStateManager.genTextures(); + tboTexture = GlStateManager._genTexture(); GL11.glBindTexture( GL31.GL_TEXTURE_BUFFER, tboTexture ); GL31.glTexBuffer( GL31.GL_TEXTURE_BUFFER, GL30.GL_R8UI, tboBuffer ); GL11.glBindTexture( GL31.GL_TEXTURE_BUFFER, 0 ); - GlStateManager.bindBuffers( GL31.GL_TEXTURE_BUFFER, 0 ); + GlStateManager._glBindBuffer( GL31.GL_TEXTURE_BUFFER, 0 ); addMonitor(); return true; @@ -102,7 +101,7 @@ public final class ClientMonitor extends ClientTerminal } deleteBuffers(); - buffer = new VertexBuffer( FixedWidthFontRenderer.TYPE.getVertexFormat() ); + buffer = new VertexBuffer(); addMonitor(); return true; @@ -122,7 +121,7 @@ public final class ClientMonitor extends ClientTerminal if( tboTexture != 0 ) { - GlStateManager.deleteTexture( tboTexture ); + GlStateManager._deleteTexture( tboTexture ); tboTexture = 0; } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorState.java b/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorState.java new file mode 100644 index 000000000..791c425ad --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorState.java @@ -0,0 +1,52 @@ +/* + * 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 javax.annotation.Nonnull; +import javax.annotation.Nullable; + +final class MonitorState +{ + public static final MonitorState UNLOADED = new MonitorState( State.UNLOADED, null ); + public static final MonitorState MISSING = new MonitorState( State.MISSING, null ); + + private final State state; + private final TileMonitor monitor; + + private MonitorState( @Nonnull State state, @Nullable TileMonitor monitor ) + { + this.state = state; + this.monitor = monitor; + } + + public static MonitorState present( @Nonnull TileMonitor monitor ) + { + return new MonitorState( State.PRESENT, monitor ); + } + + public boolean isPresent() + { + return state == State.PRESENT; + } + + public boolean isMissing() + { + return state == State.MISSING; + } + + @Nullable + public TileMonitor getMonitor() + { + return monitor; + } + + enum State + { + UNLOADED, + MISSING, + PRESENT, + } +} 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 4ba434d58..38fd20cfa 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/monitor/TileMonitor.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/monitor/TileMonitor.java @@ -51,6 +51,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile private ClientMonitor clientMonitor; private MonitorPeripheral peripheral; private boolean needsUpdate = false; + private boolean needsValidating = false; private boolean destroyed = false; private boolean visiting = false; private int width = 1; @@ -58,9 +59,9 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile private int xIndex = 0; private int yIndex = 0; - public TileMonitor( BlockEntityType type, boolean advanced ) + public TileMonitor( BlockEntityType type, boolean advanced, BlockPos pos, BlockState state ) { - super( type ); + super( type, pos, state ); this.advanced = advanced; } @@ -124,6 +125,13 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile @Override public void blockTick() { + + if( needsValidating ) + { + needsValidating = false; + validate(); + } + if( needsUpdate ) { needsUpdate = false; @@ -143,7 +151,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile { for( int y = 0; y < height; y++ ) { - TileMonitor monitor = getNeighbour( x, y ); + TileMonitor monitor = getNeighbour( x, y ).getMonitor(); if( monitor == null ) { continue; @@ -221,7 +229,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile } } - private TileMonitor getNeighbour( int x, int y ) + private MonitorState getNeighbour( int x, int y ) { BlockPos pos = getPos(); Direction right = getRight(); @@ -247,28 +255,27 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile return orientation == Direction.DOWN ? getDirection() : getDirection().getOpposite(); } - private TileMonitor getSimilarMonitorAt( BlockPos pos ) + private MonitorState getSimilarMonitorAt( BlockPos pos ) { if( pos.equals( getPos() ) ) { - return this; + return MonitorState.present( this ); } - int y = pos.getY(); World world = getWorld(); if( world == null || !world.isChunkLoaded( pos ) ) { - return null; + return MonitorState.UNLOADED; } BlockEntity tile = world.getBlockEntity( pos ); if( !(tile instanceof TileMonitor) ) { - return null; + return MonitorState.MISSING; } TileMonitor monitor = (TileMonitor) tile; - return !monitor.visiting && !monitor.destroyed && advanced == monitor.advanced && getDirection() == monitor.getDirection() && getOrientation() == monitor.getOrientation() ? monitor : null; + return !monitor.visiting && !monitor.destroyed && advanced == monitor.advanced && getDirection() == monitor.getDirection() && getOrientation() == monitor.getOrientation() ? MonitorState.present( monitor ) : MonitorState.MISSING; } // region Sizing and placement stuff @@ -286,9 +293,9 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile } @Override - public void readNbt( @Nonnull BlockState state, @Nonnull NbtCompound nbt ) + public void readNbt( @Nonnull NbtCompound nbt ) { - super.readNbt( state, nbt ); + super.readNbt( nbt ); xIndex = nbt.getInt( NBT_X ); yIndex = nbt.getInt( NBT_Y ); @@ -300,20 +307,20 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile @Nonnull @Override - public NbtCompound writeNbt( NbtCompound tag ) + public NbtCompound writeNbt( NbtCompound nbt ) { - tag.putInt( NBT_X, xIndex ); - tag.putInt( NBT_Y, yIndex ); - tag.putInt( NBT_WIDTH, width ); - tag.putInt( NBT_HEIGHT, height ); - return super.writeNbt( tag ); + nbt.putInt( NBT_X, xIndex ); + nbt.putInt( NBT_Y, yIndex ); + nbt.putInt( NBT_WIDTH, width ); + nbt.putInt( NBT_HEIGHT, height ); + return super.writeNbt( nbt ); } - @Override - public double getRenderDistance() - { - return ComputerCraft.monitorDistanceSq; - } + // @Override //TODO: make BlockEntityRenderer work with this, i guess. + // public double getRenderDistance() + // { + // return ComputerCraft.monitorDistanceSq; + // } // Sizing and placement stuff @@ -321,6 +328,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile public void cancelRemoval() { super.cancelRemoval(); + needsValidating = true; TickScheduler.schedule( this ); } @@ -348,7 +356,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile return serverMonitor; } - TileMonitor origin = getOrigin(); + TileMonitor origin = getOrigin().getMonitor(); if( origin == null ) { return null; @@ -375,7 +383,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile { for( int y = 0; y < height; y++ ) { - TileMonitor monitor = getNeighbour( x, y ); + TileMonitor monitor = getNeighbour( x, y ).getMonitor(); if( monitor != null ) { monitor.serverMonitor = serverMonitor; @@ -469,7 +477,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile return yIndex; } - private TileMonitor getOrigin() + private MonitorState getOrigin() { return getNeighbour( 0, 0 ); } @@ -496,7 +504,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile { for( int y = 0; y < height; y++ ) { - TileMonitor monitor = getNeighbour( x, y ); + TileMonitor monitor = getNeighbour( x, y ).getMonitor(); if( monitor != null && monitor.peripheral != null ) { needsTerminal = true; @@ -530,7 +538,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile { for( int y = 0; y < height; y++ ) { - TileMonitor monitor = getNeighbour( x, y ); + TileMonitor monitor = getNeighbour( x, y ).getMonitor(); if( monitor == null ) { continue; @@ -549,7 +557,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile private boolean mergeLeft() { - TileMonitor left = getNeighbour( -1, 0 ); + TileMonitor left = getNeighbour( -1, 0 ).getMonitor(); if( left == null || left.yIndex != 0 || left.height != height ) { return false; @@ -561,7 +569,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile return false; } - TileMonitor origin = left.getOrigin(); + TileMonitor origin = left.getOrigin().getMonitor(); if( origin != null ) { origin.resize( width, height ); @@ -572,7 +580,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile private boolean mergeRight() { - TileMonitor right = getNeighbour( width, 0 ); + TileMonitor right = getNeighbour( width, 0 ).getMonitor(); if( right == null || right.yIndex != 0 || right.height != height ) { return false; @@ -584,7 +592,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile return false; } - TileMonitor origin = getOrigin(); + TileMonitor origin = getOrigin().getMonitor(); if( origin != null ) { origin.resize( width, height ); @@ -595,7 +603,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile private boolean mergeUp() { - TileMonitor above = getNeighbour( 0, height ); + TileMonitor above = getNeighbour( 0, height ).getMonitor(); if( above == null || above.xIndex != 0 || above.width != width ) { return false; @@ -607,7 +615,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile return false; } - TileMonitor origin = getOrigin(); + TileMonitor origin = getOrigin().getMonitor(); if( origin != null ) { origin.resize( width, height ); @@ -618,7 +626,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile private boolean mergeDown() { - TileMonitor below = getNeighbour( 0, -1 ); + TileMonitor below = getNeighbour( 0, -1 ).getMonitor(); if( below == null || below.xIndex != 0 || below.width != width ) { return false; @@ -630,7 +638,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile return false; } - TileMonitor origin = below.getOrigin(); + TileMonitor origin = below.getOrigin().getMonitor(); if( origin != null ) { origin.resize( width, height ); @@ -662,7 +670,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile visiting = true; if( xIndex > 0 ) { - TileMonitor left = getNeighbour( xIndex - 1, yIndex ); + TileMonitor left = getNeighbour( xIndex - 1, yIndex ).getMonitor(); if( left != null ) { left.contract(); @@ -670,7 +678,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile } if( xIndex + 1 < width ) { - TileMonitor right = getNeighbour( xIndex + 1, yIndex ); + TileMonitor right = getNeighbour( xIndex + 1, yIndex ).getMonitor(); if( right != null ) { right.contract(); @@ -678,7 +686,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile } if( yIndex > 0 ) { - TileMonitor below = getNeighbour( xIndex, yIndex - 1 ); + TileMonitor below = getNeighbour( xIndex, yIndex - 1 ).getMonitor(); if( below != null ) { below.contract(); @@ -686,7 +694,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile } if( yIndex + 1 < height ) { - TileMonitor above = getNeighbour( xIndex, yIndex + 1 ); + TileMonitor above = getNeighbour( xIndex, yIndex + 1 ).getMonitor(); if( above != null ) { above.contract(); @@ -700,11 +708,11 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile int height = this.height; int width = this.width; - TileMonitor origin = getOrigin(); + TileMonitor origin = getOrigin().getMonitor(); if( origin == null ) { - TileMonitor right = width > 1 ? getNeighbour( 1, 0 ) : null; - TileMonitor below = height > 1 ? getNeighbour( 0, 1 ) : null; + TileMonitor right = width > 1 ? getNeighbour( 1, 0 ).getMonitor() : null; + TileMonitor below = height > 1 ? getNeighbour( 0, 1 ).getMonitor() : null; if( right != null ) { @@ -730,7 +738,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile { for( int x = 0; x < width; x++ ) { - TileMonitor monitor = origin.getNeighbour( x, y ); + TileMonitor monitor = origin.getNeighbour( x, y ).getMonitor(); if( monitor != null ) { continue; @@ -749,17 +757,17 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile } if( x > 0 ) { - left = origin.getNeighbour( 0, y ); + left = origin.getNeighbour( 0, y ).getMonitor(); left.resize( x, 1 ); } if( x + 1 < width ) { - right = origin.getNeighbour( x + 1, y ); + right = origin.getNeighbour( x + 1, y ).getMonitor(); right.resize( width - (x + 1), 1 ); } if( y + 1 < height ) { - below = origin.getNeighbour( 0, y + 1 ); + below = origin.getNeighbour( 0, y + 1 ).getMonitor(); below.resize( width, height - (y + 1) ); } @@ -786,6 +794,34 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile } // endregion + private boolean checkMonitorAt( int xIndex, int yIndex ) + { + MonitorState state = getNeighbour( xIndex, yIndex ); + if( state.isMissing() ) return false; + + TileMonitor monitor = state.getMonitor(); + if( monitor == null ) return true; + + return monitor.xIndex == xIndex && monitor.yIndex == yIndex && monitor.width == width && monitor.height == height; + } + + private void validate() + { + if( xIndex == 0 && yIndex == 0 && width == 1 || height == 1 ) return; + + if( checkMonitorAt( 0, 0 ) && checkMonitorAt( 0, height - 1 ) && + checkMonitorAt( width - 1, 0 ) && checkMonitorAt( width - 1, height - 1 ) ) + { + return; + } + + // Something in our monitor is invalid. For now, let's just reset ourselves and then try to integrate ourselves + // later. + ComputerCraft.log.warn( "Monitor is malformed, resetting to 1x1." ); + resize( 1, 1 ); + needsUpdate = true; + } + private void monitorTouched( float xPos, float yPos, float zPos ) { XYPair pair = XYPair.of( xPos, yPos, zPos, getDirection(), getOrientation() ) @@ -818,7 +854,7 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile { for( int x = 0; x < width; x++ ) { - TileMonitor monitor = getNeighbour( x, y ); + TileMonitor monitor = getNeighbour( x, y ).getMonitor(); if( monitor == null ) { continue; diff --git a/src/main/java/dan200/computercraft/shared/peripheral/printer/BlockPrinter.java b/src/main/java/dan200/computercraft/shared/peripheral/printer/BlockPrinter.java index e2c6ff778..d906fb391 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/printer/BlockPrinter.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/printer/BlockPrinter.java @@ -88,4 +88,11 @@ public class BlockPrinter extends BlockGeneric { properties.add( FACING, TOP, BOTTOM ); } + + @Nullable + @Override + public BlockEntity createBlockEntity( BlockPos pos, BlockState state ) + { + return new TilePrinter( ComputerCraftRegistry.ModTiles.PRINTER, pos, state ); + } } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/printer/TilePrinter.java b/src/main/java/dan200/computercraft/shared/peripheral/printer/TilePrinter.java index 5443d1b7f..d38eb04e1 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/printer/TilePrinter.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/printer/TilePrinter.java @@ -34,6 +34,7 @@ import net.minecraft.util.Hand; import net.minecraft.util.Nameable; import net.minecraft.util.collection.DefaultedList; import net.minecraft.util.hit.BlockHitResult; +import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; import net.minecraft.util.math.Vec3d; @@ -70,9 +71,9 @@ public final class TilePrinter extends TileGeneric implements DefaultSidedInvent private String pageTitle = ""; private boolean printing = false; - public TilePrinter( BlockEntityType type ) + public TilePrinter( BlockEntityType type, BlockPos pos, BlockState state ) { - super( type ); + super( type, pos, state ); } @Override @@ -165,9 +166,9 @@ public final class TilePrinter extends TileGeneric implements DefaultSidedInvent } @Override - public void readNbt( @Nonnull BlockState state, @Nonnull NbtCompound nbt ) + public void readNbt( @Nonnull NbtCompound nbt ) { - super.readNbt( state, nbt ); + super.readNbt( nbt ); customName = nbt.contains( NBT_NAME ) ? Text.Serializer.fromJson( nbt.getString( NBT_NAME ) ) : null; diff --git a/src/main/java/dan200/computercraft/shared/peripheral/speaker/BlockSpeaker.java b/src/main/java/dan200/computercraft/shared/peripheral/speaker/BlockSpeaker.java index 50d6c08cd..34405b92b 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/speaker/BlockSpeaker.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/speaker/BlockSpeaker.java @@ -10,11 +10,16 @@ import dan200.computercraft.shared.ComputerCraftRegistry; import dan200.computercraft.shared.common.BlockGeneric; import net.minecraft.block.Block; import net.minecraft.block.BlockState; +import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.BlockEntityTicker; +import net.minecraft.block.entity.BlockEntityType; import net.minecraft.item.ItemPlacementContext; import net.minecraft.state.StateManager; import net.minecraft.state.property.DirectionProperty; import net.minecraft.state.property.Properties; +import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; +import net.minecraft.world.World; import javax.annotation.Nullable; @@ -43,4 +48,19 @@ public class BlockSpeaker extends BlockGeneric { properties.add( FACING ); } + + @Nullable + @Override + public BlockEntity createBlockEntity( BlockPos pos, BlockState state ) + { + return new TileSpeaker( ComputerCraftRegistry.ModTiles.SPEAKER, pos, state ); + } + + @Nullable + @Override + public BlockEntityTicker getTicker( World world, BlockState state, BlockEntityType type ) + { + return world.isClient ? null : BlockSpeaker.checkType( type, ComputerCraftRegistry.ModTiles.SPEAKER, TileSpeaker::tick ); + } + } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPeripheral.java b/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPeripheral.java index c44653cbd..a74ea4849 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPeripheral.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPeripheral.java @@ -11,18 +11,23 @@ import dan200.computercraft.api.lua.ILuaContext; import dan200.computercraft.api.lua.LuaException; import dan200.computercraft.api.lua.LuaFunction; import dan200.computercraft.api.peripheral.IPeripheral; -import dan200.computercraft.fabric.mixin.SoundEventAccess; +import dan200.computercraft.shared.network.NetworkHandler; +import dan200.computercraft.shared.network.client.SpeakerMoveClientMessage; +import dan200.computercraft.shared.network.client.SpeakerPlayClientMessage; import net.minecraft.block.enums.Instrument; import net.minecraft.network.packet.s2c.play.PlaySoundIdS2CPacket; import net.minecraft.server.MinecraftServer; import net.minecraft.sound.SoundCategory; import net.minecraft.util.Identifier; import net.minecraft.util.InvalidIdentifierException; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; import javax.annotation.Nonnull; import java.util.Optional; +import java.util.UUID; import java.util.concurrent.atomic.AtomicInteger; import static dan200.computercraft.api.lua.LuaValues.checkFinite; @@ -34,16 +39,40 @@ import static dan200.computercraft.api.lua.LuaValues.checkFinite; */ public abstract class SpeakerPeripheral implements IPeripheral { + private static final int MIN_TICKS_BETWEEN_SOUNDS = 1; + private final AtomicInteger notesThisTick = new AtomicInteger(); private long clock = 0; private long lastPlayTime = 0; + private long lastPositionTime; + private Vec3d lastPosition; + public void update() { clock++; notesThisTick.set( 0 ); + + // Push position updates to any speakers which have ever played a note, + // have moved by a non-trivial amount and haven't had a position update + // in the last second. + if( lastPlayTime > 0 && (clock - lastPositionTime) >= 20 ) + { + Vec3d position = getPosition(); + if( lastPosition == null || lastPosition.distanceTo( position ) >= 0.1 ) + { + lastPosition = position; + lastPositionTime = clock; + NetworkHandler.sendToAllTracking( + new SpeakerMoveClientMessage( getSource(), position ), + getWorld().getWorldChunk( new BlockPos( position ) ) + ); + } + } } + protected abstract UUID getSource(); + public boolean madeSound( long ticks ) { return clock - lastPlayTime <= ticks; @@ -90,16 +119,20 @@ public abstract class SpeakerPeripheral implements IPeripheral private synchronized boolean playSound( ILuaContext context, Identifier name, float volume, float pitch, boolean isNote ) throws LuaException { - if( clock - lastPlayTime < TileSpeaker.MIN_TICKS_BETWEEN_SOUNDS && (!isNote || clock - lastPlayTime != 0 || notesThisTick.get() >= ComputerCraft.maxNotesPerTick) ) + if( clock - lastPlayTime < MIN_TICKS_BETWEEN_SOUNDS ) { - // Rate limiting occurs when we've already played a sound within the last tick, or we've - // played more notes than allowable within the current tick. - return false; + // Rate limiting occurs when we've already played a sound within the last tick. + if( !isNote ) return false; + // Or we've played more notes than allowable within the current tick. + if( clock - lastPlayTime != 0 || notesThisTick.get() >= ComputerCraft.maxNotesPerTick ) return false; } World world = getWorld(); Vec3d pos = getPosition(); + float actualVolume = MathHelper.clamp( volume, 0.0f, 3.0f ); + float range = actualVolume * 16; + context.issueMainThreadTask( () -> { MinecraftServer server = world.getServer(); if( server == null ) @@ -107,15 +140,20 @@ public abstract class SpeakerPeripheral implements IPeripheral return null; } - float adjVolume = Math.min( volume, 3.0f ); - server.getPlayerManager() - .sendToAround( null, - pos.x, - pos.y, - pos.z, - adjVolume > 1.0f ? 16 * adjVolume : 16.0, - world.getRegistryKey(), - new PlaySoundIdS2CPacket( name, SoundCategory.RECORDS, pos, adjVolume, pitch ) ); + if( isNote ) + { + server.getPlayerManager().sendToAround( + null, pos.x, pos.y, pos.z, range, world.getRegistryKey(), + new PlaySoundIdS2CPacket( name, SoundCategory.RECORDS, pos, actualVolume, pitch ) + ); + } + else + { + NetworkHandler.sendToAllAround( + new SpeakerPlayClientMessage( getSource(), pos, name, actualVolume, pitch ), + world, pos, range + ); + } return null; } ); @@ -167,7 +205,7 @@ public abstract class SpeakerPeripheral implements IPeripheral // If the resource location for note block notes changes, this method call will need to be updated boolean success = playSound( context, - ((SoundEventAccess) instrument.getSound()).getId(), + instrument.getSound().getId(), volume, (float) Math.pow( 2.0, (pitch - 12.0) / 12.0 ), true ); diff --git a/src/main/java/dan200/computercraft/shared/peripheral/speaker/TileSpeaker.java b/src/main/java/dan200/computercraft/shared/peripheral/speaker/TileSpeaker.java index 08d1e8a41..2a0de631e 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/speaker/TileSpeaker.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/speaker/TileSpeaker.java @@ -9,8 +9,8 @@ package dan200.computercraft.shared.peripheral.speaker; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheralTile; import dan200.computercraft.shared.common.TileGeneric; +import net.minecraft.block.BlockState; import net.minecraft.block.entity.BlockEntityType; -import net.minecraft.util.Tickable; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; import net.minecraft.util.math.Vec3d; @@ -18,23 +18,24 @@ import net.minecraft.world.World; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.util.UUID; -public class TileSpeaker extends TileGeneric implements Tickable, IPeripheralTile +public class TileSpeaker extends TileGeneric implements IPeripheralTile { public static final int MIN_TICKS_BETWEEN_SOUNDS = 1; private final SpeakerPeripheral peripheral; + private final UUID source = UUID.randomUUID(); - public TileSpeaker( BlockEntityType type ) + public TileSpeaker( BlockEntityType type, BlockPos pos, BlockState state ) { - super( type ); + super( type, pos, state ); peripheral = new Peripheral( this ); } - @Override - public void tick() + public static void tick( World world, BlockPos pos, BlockState state, TileSpeaker tileSpeaker ) { - peripheral.update(); + tileSpeaker.peripheral.update(); } @Nonnull @@ -66,6 +67,12 @@ public class TileSpeaker extends TileGeneric implements Tickable, IPeripheralTil return new Vec3d( pos.getX(), pos.getY(), pos.getZ() ); } + @Override + protected UUID getSource() + { + return speaker.source; + } + @Override public boolean equals( @Nullable IPeripheral other ) { diff --git a/src/main/java/dan200/computercraft/shared/peripheral/speaker/UpgradeSpeakerPeripheral.java b/src/main/java/dan200/computercraft/shared/peripheral/speaker/UpgradeSpeakerPeripheral.java new file mode 100644 index 000000000..cbe318845 --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/peripheral/speaker/UpgradeSpeakerPeripheral.java @@ -0,0 +1,33 @@ +/* + * 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.speaker; + +import dan200.computercraft.api.peripheral.IComputerAccess; +import dan200.computercraft.shared.network.NetworkHandler; +import dan200.computercraft.shared.network.client.SpeakerStopClientMessage; + +import javax.annotation.Nonnull; +import java.util.UUID; + +/** + * A speaker peripheral which is used on an upgrade, and so is only attached to one computer. + */ +public abstract class UpgradeSpeakerPeripheral extends SpeakerPeripheral +{ + private final UUID source = UUID.randomUUID(); + + @Override + protected final UUID getSource() + { + return source; + } + + @Override + public void detach( @Nonnull IComputerAccess computer ) + { + NetworkHandler.sendToAllPlayers( new SpeakerStopClientMessage( source ) ); + } +} diff --git a/src/main/java/dan200/computercraft/shared/pocket/apis/PocketAPI.java b/src/main/java/dan200/computercraft/shared/pocket/apis/PocketAPI.java index d14c9b4ec..fec1ce9ef 100644 --- a/src/main/java/dan200/computercraft/shared/pocket/apis/PocketAPI.java +++ b/src/main/java/dan200/computercraft/shared/pocket/apis/PocketAPI.java @@ -68,7 +68,7 @@ public class PocketAPI implements ILuaAPI return new Object[] { false, "Cannot find player" }; } PlayerEntity player = (PlayerEntity) entity; - PlayerInventory inventory = player.inventory; + PlayerInventory inventory = player.getInventory(); IPocketUpgrade previousUpgrade = computer.getUpgrade(); // Attempt to find the upgrade, starting in the main segment, and then looking in the opposite @@ -143,7 +143,7 @@ public class PocketAPI implements ILuaAPI return new Object[] { false, "Cannot find player" }; } PlayerEntity player = (PlayerEntity) entity; - PlayerInventory inventory = player.inventory; + PlayerInventory inventory = player.getInventory(); IPocketUpgrade previousUpgrade = computer.getUpgrade(); if( previousUpgrade == null ) diff --git a/src/main/java/dan200/computercraft/shared/pocket/core/PocketServerComputer.java b/src/main/java/dan200/computercraft/shared/pocket/core/PocketServerComputer.java index af643dfc4..56b2e615a 100644 --- a/src/main/java/dan200/computercraft/shared/pocket/core/PocketServerComputer.java +++ b/src/main/java/dan200/computercraft/shared/pocket/core/PocketServerComputer.java @@ -57,7 +57,7 @@ public class PocketServerComputer extends ServerComputer implements IPocketAcces if( entity instanceof PlayerEntity ) { - PlayerInventory inventory = ((PlayerEntity) entity).inventory; + PlayerInventory inventory = ((PlayerEntity) entity).getInventory(); return inventory.main.contains( stack ) || inventory.offHand.contains( stack ) ? entity : null; } else if( entity instanceof LivingEntity ) @@ -122,7 +122,7 @@ public class PocketServerComputer extends ServerComputer implements IPocketAcces { if( entity instanceof PlayerEntity ) { - ((PlayerEntity) entity).inventory.markDirty(); + ((PlayerEntity) entity).getInventory().markDirty(); } } diff --git a/src/main/java/dan200/computercraft/shared/pocket/inventory/ContainerPocketComputer.java b/src/main/java/dan200/computercraft/shared/pocket/inventory/ContainerPocketComputer.java deleted file mode 100644 index 8149d9199..000000000 --- a/src/main/java/dan200/computercraft/shared/pocket/inventory/ContainerPocketComputer.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * 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.pocket.inventory; - -import dan200.computercraft.shared.ComputerCraftRegistry; -import dan200.computercraft.shared.computer.core.ServerComputer; -import dan200.computercraft.shared.computer.inventory.ContainerComputerBase; -import dan200.computercraft.shared.pocket.items.ItemPocketComputer; -import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.entity.player.PlayerInventory; -import net.minecraft.item.ItemStack; -import net.minecraft.network.PacketByteBuf; -import net.minecraft.screen.ScreenHandler; -import net.minecraft.server.network.ServerPlayerEntity; -import net.minecraft.text.Text; -import net.minecraft.util.Hand; - -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -public final class ContainerPocketComputer extends ContainerComputerBase -{ - private ContainerPocketComputer( int id, ServerComputer computer, ItemPocketComputer item, Hand hand ) - { - super( ComputerCraftRegistry.ModContainers.POCKET_COMPUTER, id, p -> { - ItemStack stack = p.getStackInHand( hand ); - return stack.getItem() == item && ItemPocketComputer.getServerComputer( stack ) == computer; - }, computer, item.getFamily() ); - } - - public ContainerPocketComputer( int id, PlayerInventory player, PacketByteBuf packetByteBuf ) - { - super( ComputerCraftRegistry.ModContainers.POCKET_COMPUTER, id, player, packetByteBuf ); - } - - public static class Factory implements ExtendedScreenHandlerFactory - { - private final ServerComputer computer; - private final Text name; - private final ItemPocketComputer item; - private final Hand hand; - - public Factory( ServerComputer computer, ItemStack stack, ItemPocketComputer item, Hand hand ) - { - this.computer = computer; - name = stack.getName(); - this.item = item; - this.hand = hand; - } - - - @Nonnull - @Override - public Text getDisplayName() - { - return name; - } - - @Nullable - @Override - public ScreenHandler createMenu( int id, @Nonnull PlayerInventory inventory, @Nonnull PlayerEntity entity ) - { - return new ContainerPocketComputer( id, computer, item, hand ); - } - - @Override - public void writeScreenOpeningData( ServerPlayerEntity serverPlayerEntity, PacketByteBuf packetByteBuf ) - { - packetByteBuf.writeInt( computer.getInstanceID() ); - packetByteBuf.writeEnumConstant( computer.getFamily() ); - } - } -} diff --git a/src/main/java/dan200/computercraft/shared/pocket/inventory/PocketComputerMenuProvider.java b/src/main/java/dan200/computercraft/shared/pocket/inventory/PocketComputerMenuProvider.java new file mode 100644 index 000000000..2e1a066c2 --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/pocket/inventory/PocketComputerMenuProvider.java @@ -0,0 +1,71 @@ +/* + * 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.pocket.inventory; + +import dan200.computercraft.shared.ComputerCraftRegistry; +import dan200.computercraft.shared.computer.core.ServerComputer; +import dan200.computercraft.shared.computer.inventory.ComputerMenuWithoutInventory; +import dan200.computercraft.shared.pocket.items.ItemPocketComputer; +import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.screen.NamedScreenHandlerFactory; +import net.minecraft.screen.ScreenHandler; +import net.minecraft.server.network.ServerPlayerEntity; +import net.minecraft.text.Text; +import net.minecraft.util.Hand; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +public class PocketComputerMenuProvider implements NamedScreenHandlerFactory, ExtendedScreenHandlerFactory +{ + private final ServerComputer computer; + private final Text name; + private final ItemPocketComputer item; + private final Hand hand; + private final boolean isTypingOnly; + + public PocketComputerMenuProvider( ServerComputer computer, ItemStack stack, ItemPocketComputer item, Hand hand, boolean isTypingOnly ) + { + this.computer = computer; + name = stack.getName(); + this.item = item; + this.hand = hand; + this.isTypingOnly = isTypingOnly; + } + + + @Nonnull + @Override + public Text getDisplayName() + { + return name; + } + + @Nullable + @Override + public ScreenHandler createMenu( int id, @Nonnull PlayerInventory inventory, @Nonnull PlayerEntity entity ) + { + return new ComputerMenuWithoutInventory( + isTypingOnly ? ComputerCraftRegistry.ModContainers.POCKET_COMPUTER_NO_TERM : ComputerCraftRegistry.ModContainers.POCKET_COMPUTER, id, inventory, + p -> { + ItemStack stack = p.getStackInHand( hand ); + return stack.getItem() == item && ItemPocketComputer.getServerComputer( stack ) == computer; + }, + computer, item.getFamily() + ); + } + + @Override + public void writeScreenOpeningData( ServerPlayerEntity player, PacketByteBuf packetByteBuf ) + { + packetByteBuf.writeInt( computer.getInstanceID() ); + packetByteBuf.writeEnumConstant( computer.getFamily() ); + } +} diff --git a/src/main/java/dan200/computercraft/shared/pocket/items/ItemPocketComputer.java b/src/main/java/dan200/computercraft/shared/pocket/items/ItemPocketComputer.java index ed349658d..69d586869 100644 --- a/src/main/java/dan200/computercraft/shared/pocket/items/ItemPocketComputer.java +++ b/src/main/java/dan200/computercraft/shared/pocket/items/ItemPocketComputer.java @@ -23,7 +23,7 @@ import dan200.computercraft.shared.computer.items.IComputerItem; import dan200.computercraft.shared.network.container.ComputerContainerData; import dan200.computercraft.shared.pocket.apis.PocketAPI; import dan200.computercraft.shared.pocket.core.PocketServerComputer; -import dan200.computercraft.shared.pocket.inventory.ContainerPocketComputer; +import dan200.computercraft.shared.pocket.inventory.PocketComputerMenuProvider; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.item.TooltipContext; @@ -147,7 +147,7 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I ItemStack stack = player.getStackInHand( hand ); if( !world.isClient ) { - PocketServerComputer computer = createServerComputer( world, player.inventory, player, stack ); + PocketServerComputer computer = createServerComputer( world, player.getInventory(), player, stack ); boolean stop = false; if( computer != null ) @@ -164,8 +164,8 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I if( !stop && computer != null ) { - computer.sendTerminalState( player ); - new ComputerContainerData( computer ).open( player, new ContainerPocketComputer.Factory( computer, stack, this, hand ) ); + boolean isTypingOnly = hand == Hand.OFF_HAND; + new ComputerContainerData( computer ).open( player, new PocketComputerMenuProvider( computer, stack, this, hand, isTypingOnly ) ); } } return new TypedActionResult<>( ActionResult.SUCCESS, stack ); @@ -177,7 +177,7 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, I if( !world.isClient ) { // Server side - Inventory inventory = entity instanceof PlayerEntity ? ((PlayerEntity) entity).inventory : null; + Inventory inventory = entity instanceof PlayerEntity ? ((PlayerEntity) entity).getInventory() : null; PocketServerComputer computer = createServerComputer( world, inventory, entity, stack ); if( computer != null ) { diff --git a/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketSpeakerPeripheral.java b/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketSpeakerPeripheral.java index dcec95bb9..315abd72c 100644 --- a/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketSpeakerPeripheral.java +++ b/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketSpeakerPeripheral.java @@ -7,11 +7,11 @@ package dan200.computercraft.shared.pocket.peripherals; import dan200.computercraft.api.peripheral.IPeripheral; -import dan200.computercraft.shared.peripheral.speaker.SpeakerPeripheral; +import dan200.computercraft.shared.peripheral.speaker.UpgradeSpeakerPeripheral; import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; -public class PocketSpeakerPeripheral extends SpeakerPeripheral +public class PocketSpeakerPeripheral extends UpgradeSpeakerPeripheral { private World world = null; private Vec3d position = Vec3d.ZERO; diff --git a/src/main/java/dan200/computercraft/shared/turtle/SignInspectHandler.java b/src/main/java/dan200/computercraft/shared/turtle/SignInspectHandler.java index 83308b347..f54c6f882 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/SignInspectHandler.java +++ b/src/main/java/dan200/computercraft/shared/turtle/SignInspectHandler.java @@ -26,7 +26,7 @@ public class SignInspectHandler Map textTable = new HashMap<>(); for( int k = 0; k < 4; k++ ) { - textTable.put( k + 1, ((SignBlockEntityAccess) sbe).getText()[k].asString() ); + textTable.put( k + 1, ((SignBlockEntityAccess) sbe).getTexts()[k].asString() ); } event.getData().put( "text", textTable ); } diff --git a/src/main/java/dan200/computercraft/shared/turtle/blocks/BlockTurtle.java b/src/main/java/dan200/computercraft/shared/turtle/blocks/BlockTurtle.java index bff000580..942214057 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/blocks/BlockTurtle.java +++ b/src/main/java/dan200/computercraft/shared/turtle/blocks/BlockTurtle.java @@ -7,6 +7,7 @@ package dan200.computercraft.shared.turtle.blocks; import dan200.computercraft.api.turtle.TurtleSide; +import dan200.computercraft.shared.ComputerCraftRegistry; import dan200.computercraft.shared.computer.blocks.BlockComputerBase; import dan200.computercraft.shared.computer.blocks.TileComputerBase; import dan200.computercraft.shared.computer.core.ComputerFamily; @@ -15,6 +16,7 @@ import dan200.computercraft.shared.turtle.items.ITurtleItem; import dan200.computercraft.shared.turtle.items.TurtleItemFactory; import net.minecraft.block.*; import net.minecraft.block.entity.BlockEntity; +import net.minecraft.block.entity.BlockEntityTicker; import net.minecraft.block.entity.BlockEntityType; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.player.PlayerEntity; @@ -136,9 +138,8 @@ public class BlockTurtle extends BlockComputerBase implements Waterl super.onPlaced( world, pos, state, player, stack ); BlockEntity tile = world.getBlockEntity( pos ); - if( !world.isClient && tile instanceof TileTurtle ) + if( !world.isClient && tile instanceof TileTurtle turtle ) { - TileTurtle turtle = (TileTurtle) tile; if( player instanceof PlayerEntity ) { @@ -176,4 +177,27 @@ public class BlockTurtle extends BlockComputerBase implements Waterl } } } + + public BlockEntityType getTypeByFamily( ComputerFamily family ) + { + if ( family == ComputerFamily.ADVANCED ) + { + return ComputerCraftRegistry.ModTiles.TURTLE_ADVANCED; + } + return ComputerCraftRegistry.ModTiles.TURTLE_NORMAL; + } + + @Nullable + @Override + public BlockEntity createBlockEntity( BlockPos pos, BlockState state ) + { + return new TileTurtle( getTypeByFamily( getFamily() ), pos, state, getFamily() ); + } + + @Nullable + @Override + public BlockEntityTicker getTicker( World world, BlockState state, BlockEntityType type ) + { + return world.isClient ? BlockTurtle.checkType( type, getTypeByFamily( getFamily() ), ( world1, pos, state1, computer ) -> computer.clientTick() ) : super.getTicker( world, state, type ); + } } diff --git a/src/main/java/dan200/computercraft/shared/turtle/blocks/TileTurtle.java b/src/main/java/dan200/computercraft/shared/turtle/blocks/TileTurtle.java index 74107bed7..ab3cf97b4 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/blocks/TileTurtle.java +++ b/src/main/java/dan200/computercraft/shared/turtle/blocks/TileTurtle.java @@ -47,20 +47,23 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.Collections; -public class TileTurtle extends TileComputerBase implements ITurtleTile, DefaultInventory +public class TileTurtle extends TileComputerBase + implements ITurtleTile, DefaultInventory { public static final int INVENTORY_SIZE = 16; public static final int INVENTORY_WIDTH = 4; public static final int INVENTORY_HEIGHT = 4; - private final DefaultedList inventory = DefaultedList.ofSize( INVENTORY_SIZE, ItemStack.EMPTY ); - private final DefaultedList previousInventory = DefaultedList.ofSize( INVENTORY_SIZE, ItemStack.EMPTY ); + private final DefaultedList inventory = DefaultedList + .ofSize( INVENTORY_SIZE, ItemStack.EMPTY ); + private final DefaultedList previousInventory = DefaultedList + .ofSize( INVENTORY_SIZE, ItemStack.EMPTY ); private boolean inventoryChanged = false; private TurtleBrain brain = new TurtleBrain( this ); private MoveState moveState = MoveState.NOT_MOVED; - public TileTurtle( BlockEntityType type, ComputerFamily family ) + public TileTurtle( BlockEntityType type, BlockPos pos, BlockState state, ComputerFamily family ) { - super( type, family ); + super( type, family, pos, state ); } @Override @@ -132,7 +135,8 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default @Override public ItemStack getStack( int slot ) { - return slot >= 0 && slot < INVENTORY_SIZE ? inventory.get( slot ) : ItemStack.EMPTY; + return slot >= 0 && slot < INVENTORY_SIZE ? inventory.get( slot ) + : ItemStack.EMPTY; } @Nonnull @@ -173,7 +177,8 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default @Override public void setStack( int i, @Nonnull ItemStack stack ) { - if( i >= 0 && i < INVENTORY_SIZE && !InventoryUtil.areItemsEqual( stack, inventory.get( i ) ) ) + if( i >= 0 && i < INVENTORY_SIZE + && !InventoryUtil.areItemsEqual( stack, inventory.get( i ) ) ) { inventory.set( i, stack ); onInventoryDefinitelyChanged(); @@ -223,7 +228,8 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default } return ActionResult.SUCCESS; } - else if( currentItem.getItem() == Items.WATER_BUCKET && brain.getColour() != -1 ) + else if( currentItem.getItem() == Items.WATER_BUCKET + && brain.getColour() != -1 ) { // Water to remove turtle colour if( !getWorld().isClient ) @@ -233,8 +239,9 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default brain.setColour( -1 ); if( !player.isCreative() ) { - player.setStackInHand( hand, new ItemStack( Items.BUCKET ) ); - player.inventory.markDirty(); + player.setStackInHand( hand, + new ItemStack( Items.BUCKET ) ); + player.getInventory().markDirty(); } } } @@ -265,11 +272,11 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default } @Override - public void tick() + public void serverTick( ) { - super.tick(); + super.serverTick(); brain.update(); - if( !getWorld().isClient && inventoryChanged ) + if( inventoryChanged ) { ServerComputer computer = getServerComputer(); if( computer != null ) @@ -280,16 +287,19 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default inventoryChanged = false; for( int n = 0; n < size(); n++ ) { - previousInventory.set( n, - getStack( n ).copy() ); + previousInventory.set( n, getStack( n ).copy() ); } } } + protected void clientTick() + { + brain.update(); + } + @Override protected void updateBlockState( ComputerState newState ) - { - } + { } @Nonnull @Override @@ -299,13 +309,11 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default NbtList nbttaglist = new NbtList(); for( int i = 0; i < INVENTORY_SIZE; i++ ) { - if( !inventory.get( i ) - .isEmpty() ) + if( !inventory.get( i ).isEmpty() ) { NbtCompound tag = new NbtCompound(); tag.putByte( "Slot", (byte) i ); - inventory.get( i ) - .writeNbt( tag ); + inventory.get( i ).writeNbt( tag ); nbttaglist.add( tag ); } } @@ -320,9 +328,9 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default // IDirectionalTile @Override - public void readNbt( @Nonnull BlockState state, @Nonnull NbtCompound nbt ) + public void readNbt( @Nonnull NbtCompound nbt ) { - super.readNbt( state, nbt ); + super.readNbt( nbt ); // Read inventory NbtList nbttaglist = nbt.getList( "Items", NBTUtil.TAG_COMPOUND ); @@ -335,8 +343,7 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default if( slot < size() ) { inventory.set( slot, ItemStack.fromNbt( tag ) ); - previousInventory.set( slot, inventory.get( slot ) - .copy() ); + previousInventory.set( slot, inventory.get( slot ).copy() ); } } @@ -361,11 +368,9 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default @Override protected ServerComputer createComputer( int instanceID, int id ) { - ServerComputer computer = new ServerComputer( getWorld(), - id, label, - instanceID, getFamily(), - ComputerCraft.turtleTermWidth, - ComputerCraft.turtleTermHeight ); + ServerComputer computer = new ServerComputer( getWorld(), id, label, instanceID, + getFamily(), ComputerCraft.turtleTermWidth, + ComputerCraft.turtleTermHeight ); computer.setPosition( getPos() ); computer.addAPI( new TurtleAPI( computer.getAPIEnvironment(), getAccess() ) ); brain.setupComputer( computer ); @@ -398,8 +403,7 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default { dir = Direction.NORTH; } - world.setBlockState( pos, - getCachedState().with( BlockTurtle.FACING, dir ) ); + world.setBlockState( pos, getCachedState().with( BlockTurtle.FACING, dir ) ); updateOutput(); updateInput(); onTileEntityChange(); @@ -413,7 +417,7 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default private boolean hasPeripheralUpgradeOnSide( ComputerSide side ) { ITurtleUpgrade upgrade; - switch( side ) + switch ( side ) { case RIGHT: upgrade = getUpgrade( TurtleSide.RIGHT ); @@ -424,8 +428,7 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default default: return false; } - return upgrade != null && upgrade.getType() - .isPeripheral(); + return upgrade != null && upgrade.getType().isPeripheral(); } // IInventory @@ -526,8 +529,7 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default boolean changed = false; for( int i = 0; i < INVENTORY_SIZE; i++ ) { - if( !inventory.get( i ) - .isEmpty() ) + if( !inventory.get( i ).isEmpty() ) { inventory.set( i, ItemStack.EMPTY ); changed = true; @@ -557,7 +559,8 @@ public class TileTurtle extends TileComputerBase implements ITurtleTile, Default @Nullable @Override - public ScreenHandler createMenu( int id, @Nonnull PlayerInventory inventory, @Nonnull PlayerEntity player ) + public ScreenHandler createMenu( int id, @Nonnull PlayerInventory inventory, + @Nonnull PlayerEntity player ) { return new ContainerTurtle( id, inventory, brain ); } diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java index bae0575a9..391ea0f3e 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java @@ -252,11 +252,10 @@ public class TurtleBrain implements ITurtleAccess if( block == oldBlock.getBlock() ) { BlockEntity newTile = world.getBlockEntity( pos ); - if( newTile instanceof TileTurtle ) + if( newTile instanceof TileTurtle newTurtle ) { // Copy the old turtle state into the new turtle - TileTurtle newTurtle = (TileTurtle) newTile; - newTurtle.setLocation( world, pos ); + newTurtle.setWorld( world ); newTurtle.transferStateFrom( oldOwner ); newTurtle.createServerComputer() .setWorld( world ); @@ -459,6 +458,7 @@ public class TurtleBrain implements ITurtleAccess { throw new UnsupportedOperationException( "Cannot run commands on the client" ); } + if( commandQueue.size() > 16 ) return MethodResult.of( false, "Too many ongoing turtle commands" ); // Issue command int commandID = issueCommand( command ); @@ -995,15 +995,12 @@ public class TurtleBrain implements ITurtleAccess @Override public MethodResult resume( Object[] response ) { - if( response.length < 3 || !(response[1] instanceof Number) || !(response[2] instanceof Boolean) ) + if( response.length < 3 || !(response[1] instanceof Number id) || !(response[2] instanceof Boolean) ) { return pull; } - if( ((Number) response[1]).intValue() != command ) - { - return pull; - } + if( id.intValue() != command ) return pull; return MethodResult.of( Arrays.copyOfRange( response, 2, response.length ) ); } diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleMoveCommand.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleMoveCommand.java index 14bd79b2e..60e58e7dd 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleMoveCommand.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleMoveCommand.java @@ -131,11 +131,11 @@ public class TurtleMoveCommand implements ITurtleCommand private static TurtleCommandResult canEnter( TurtlePlayer turtlePlayer, World world, BlockPos position ) { - if( World.isOutOfBuildLimitVertically( position ) ) + if( world.isOutOfHeightLimit( position ) ) { return TurtleCommandResult.failure( position.getY() < 0 ? "Too low to move" : "Too high to move" ); } - if( !World.isInBuildLimit( position ) ) + if( !world.isInBuildLimit( position ) ) { return TurtleCommandResult.failure( "Cannot leave the world" ); } 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 bc909ea91..ea6c02a5a 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlaceCommand.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlaceCommand.java @@ -193,24 +193,24 @@ public class TurtlePlaceCommand implements ITurtleCommand if( direction.getAxis() != Direction.Axis.Y ) { - turtlePlayer.yaw = direction.asRotation(); - turtlePlayer.pitch = 0.0f; + turtlePlayer.setYaw( direction.asRotation() ); + turtlePlayer.setPitch( 0.0f ); } else { - turtlePlayer.yaw = turtle.getDirection() - .asRotation(); - turtlePlayer.pitch = DirectionUtil.toPitchAngle( direction ); + turtlePlayer.setYaw( turtle.getDirection() + .asRotation() ); + turtlePlayer.setPitch( DirectionUtil.toPitchAngle( direction ) ); } turtlePlayer.setPos( posX, posY, posZ ); turtlePlayer.prevX = posX; turtlePlayer.prevY = posY; turtlePlayer.prevZ = posZ; - turtlePlayer.prevPitch = turtlePlayer.pitch; - turtlePlayer.prevYaw = turtlePlayer.yaw; + turtlePlayer.prevPitch = turtlePlayer.getPitch(); + turtlePlayer.prevYaw = turtlePlayer.getYaw(); - turtlePlayer.headYaw = turtlePlayer.yaw; + turtlePlayer.headYaw = turtlePlayer.getYaw(); turtlePlayer.prevHeadYaw = turtlePlayer.headYaw; } @@ -403,7 +403,7 @@ public class TurtlePlaceCommand implements ITurtleCommand Direction side, boolean allowReplaceable, String[] outErrorMessage ) { World world = turtle.getWorld(); - if( !World.isInBuildLimit( position ) || world.isAir( position ) || (context.getStack() + if( !world.isInBuildLimit( position ) || world.isAir( position ) || (context.getStack() .getItem() instanceof BlockItem && WorldUtil.isLiquidBlock( world, position )) ) { diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java index 683cb189b..896ade7a6 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java @@ -26,7 +26,6 @@ import net.minecraft.item.ItemStack; import net.minecraft.screen.NamedScreenHandlerFactory; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ServerWorld; -import net.minecraft.util.Hand; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Direction; import net.minecraft.util.math.Vec3d; @@ -84,11 +83,11 @@ public final class TurtlePlayer extends FakePlayer BlockPos position = turtle.getPosition(); setPos( position.getX() + 0.5, position.getY() + 0.5, position.getZ() + 0.5 ); - yaw = turtle.getDirection() - .asRotation(); - pitch = 0.0f; + setYaw( turtle.getDirection() + .asRotation() ); + setPitch( 0.0f ); - inventory.clear(); + getInventory().clear(); } public static TurtlePlayer get( ITurtleAccess access ) @@ -112,23 +111,23 @@ public final class TurtlePlayer extends FakePlayer public void loadInventory( @Nonnull ItemStack currentStack ) { // Load up the fake inventory - inventory.selectedSlot = 0; - inventory.setStack( 0, currentStack ); + getInventory().selectedSlot = 0; + getInventory().setStack( 0, currentStack ); } public ItemStack unloadInventory( ITurtleAccess turtle ) { // Get the item we placed with - ItemStack results = inventory.getStack( 0 ); - inventory.setStack( 0, ItemStack.EMPTY ); + ItemStack results = getInventory().getStack( 0 ); + getInventory().setStack( 0, ItemStack.EMPTY ); // Store (or drop) anything else we found BlockPos dropPosition = turtle.getPosition(); Direction dropDirection = turtle.getDirection() .getOpposite(); - for( int i = 0; i < inventory.size(); i++ ) + for( int i = 0; i < getInventory().size(); i++ ) { - ItemStack stack = inventory.getStack( i ); + ItemStack stack = getInventory().getStack( i ); if( !stack.isEmpty() ) { ItemStack remainder = InventoryUtil.storeItems( stack, turtle.getItemHandler(), turtle.getSelectedSlot() ); @@ -136,10 +135,10 @@ public final class TurtlePlayer extends FakePlayer { WorldUtil.dropItemStack( remainder, turtle.getWorld(), dropPosition, dropDirection ); } - inventory.setStack( i, ItemStack.EMPTY ); + getInventory().setStack( i, ItemStack.EMPTY ); } } - inventory.markDirty(); + getInventory().markDirty(); return results; } @@ -207,31 +206,11 @@ public final class TurtlePlayer extends FakePlayer { } - @Override - public void useBook( @Nonnull ItemStack stack, @Nonnull Hand hand ) - { - } - @Override public void closeHandledScreen() { } - @Override - public void updateCursorStack() - { - } - - @Override - protected void onStatusEffectApplied( @Nonnull StatusEffectInstance id ) - { - } - - @Override - protected void onStatusEffectUpgraded( @Nonnull StatusEffectInstance id, boolean apply ) - { - } - @Override protected void onStatusEffectRemoved( @Nonnull StatusEffectInstance effect ) { diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleSuckCommand.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleSuckCommand.java index cd807af15..f8aa7ffe8 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleSuckCommand.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleSuckCommand.java @@ -135,7 +135,7 @@ public class TurtleSuckCommand implements ITurtleCommand { if( remainder.isEmpty() && leaveStack.isEmpty() ) { - entity.remove(); + entity.discard(); } else if( remainder.isEmpty() ) { diff --git a/src/main/java/dan200/computercraft/shared/turtle/inventory/ContainerTurtle.java b/src/main/java/dan200/computercraft/shared/turtle/inventory/ContainerTurtle.java index f090722b8..17a06de7c 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/inventory/ContainerTurtle.java +++ b/src/main/java/dan200/computercraft/shared/turtle/inventory/ContainerTurtle.java @@ -6,6 +6,7 @@ package dan200.computercraft.shared.turtle.inventory; +import dan200.computercraft.client.gui.widgets.ComputerSidebar; import dan200.computercraft.shared.ComputerCraftRegistry; import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.IComputer; @@ -29,8 +30,10 @@ import java.util.function.Predicate; public class ContainerTurtle extends ContainerComputerBase { + public static final int BORDER = 8; public static final int PLAYER_START_Y = 134; - public static final int TURTLE_START_X = 175; + public static final int TURTLE_START_X = ComputerSidebar.WIDTH + 175; + public static final int PLAYER_START_X = ComputerSidebar.WIDTH + BORDER; private final PropertyDelegate properties; @@ -69,14 +72,14 @@ public class ContainerTurtle extends ContainerComputerBase { for( int x = 0; x < 9; x++ ) { - addSlot( new Slot( playerInventory, x + y * 9 + 9, 8 + x * 18, PLAYER_START_Y + 1 + y * 18 ) ); + addSlot( new Slot( playerInventory, x + y * 9 + 9, PLAYER_START_X + x * 18, PLAYER_START_Y + 1 + y * 18 ) ); } } // Player hotbar for( int x = 0; x < 9; x++ ) { - addSlot( new Slot( playerInventory, x, 8 + x * 18, PLAYER_START_Y + 3 * 18 + 5 ) ); + addSlot( new Slot( playerInventory, x, PLAYER_START_X + x * 18, PLAYER_START_Y + 3 * 18 + 5 ) ); } } diff --git a/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleSpeaker.java b/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleSpeaker.java index 3fbc845ca..fe87ef4a3 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleSpeaker.java +++ b/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleSpeaker.java @@ -12,7 +12,7 @@ import dan200.computercraft.api.turtle.ITurtleAccess; import dan200.computercraft.api.turtle.TurtleSide; import dan200.computercraft.api.turtle.TurtleUpgradeType; import dan200.computercraft.shared.ComputerCraftRegistry; -import dan200.computercraft.shared.peripheral.speaker.SpeakerPeripheral; +import dan200.computercraft.shared.peripheral.speaker.UpgradeSpeakerPeripheral; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.util.ModelIdentifier; @@ -71,7 +71,7 @@ public class TurtleSpeaker extends AbstractTurtleUpgrade } } - private static class Peripheral extends SpeakerPeripheral + private static class Peripheral extends UpgradeSpeakerPeripheral { ITurtleAccess turtle; diff --git a/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleTool.java b/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleTool.java index 91fc88b3b..7bc6281ed 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleTool.java +++ b/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleTool.java @@ -12,6 +12,7 @@ import dan200.computercraft.api.turtle.*; import dan200.computercraft.api.turtle.event.TurtleAttackEvent; import dan200.computercraft.api.turtle.event.TurtleBlockEvent; import dan200.computercraft.api.turtle.event.TurtleEvent; +import dan200.computercraft.fabric.mixininterface.IMatrix4f; import dan200.computercraft.shared.TurtlePermissions; import dan200.computercraft.shared.turtle.core.TurtleBrain; import dan200.computercraft.shared.turtle.core.TurtlePlaceCommand; @@ -36,11 +37,7 @@ import net.minecraft.nbt.NbtCompound; import net.minecraft.util.ActionResult; import net.minecraft.util.Hand; import net.minecraft.util.Identifier; -import net.minecraft.util.math.AffineTransformation; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.Direction; -import net.minecraft.util.math.Vec3d; -import net.minecraft.util.math.Vec3f; +import net.minecraft.util.math.*; import net.minecraft.world.World; import org.apache.commons.lang3.tuple.Pair; @@ -106,8 +103,7 @@ public class TurtleTool extends AbstractTurtleUpgrade @Environment( EnvType.CLIENT ) public TransformedModel getModel( ITurtleAccess turtle, @Nonnull TurtleSide side ) { - float xOffset = side == TurtleSide.LEFT ? -0.40625f : 0.40625f; - return TransformedModel.of( getCraftingItem(), new AffineTransformation( new Vec3f( xOffset + 1, 0, 1 ), Vec3f.POSITIVE_Y.getDegreesQuaternion( 270 ), new Vec3f( 1, 1, 1 ), Vec3f.POSITIVE_Z.getDegreesQuaternion( 90 ) ) ); + return TransformedModel.of( getCraftingItem(), side == TurtleSide.LEFT ? Transforms.leftTransform : Transforms.rightTransform ); } private TurtleCommandResult attack( ITurtleAccess turtle, Direction direction, TurtleSide side ) @@ -296,4 +292,22 @@ public class TurtleTool extends AbstractTurtleUpgrade Block block = state.getBlock(); return !state.isAir() && block != Blocks.BEDROCK && state.calcBlockBreakingDelta( player, world, pos ) > 0; } + + private static class Transforms + { + static final AffineTransformation leftTransform = getMatrixFor( -0.40625f ); + static final AffineTransformation rightTransform = getMatrixFor( 0.40625f ); + + private static AffineTransformation getMatrixFor( float offset ) + { + Matrix4f matrix = new Matrix4f(); + ((IMatrix4f) (Object) matrix).setFloatArray( new float[] { + 0.0f, 0.0f, -1.0f, 1.0f + offset, + 1.0f, 0.0f, 0.0f, 0.0f, + 0.0f, -1.0f, 0.0f, 1.0f, + 0.0f, 0.0f, 0.0f, 1.0f, + } ); + return new AffineTransformation( matrix ); + } + } } diff --git a/src/main/java/dan200/computercraft/shared/util/FakeNetHandler.java b/src/main/java/dan200/computercraft/shared/util/FakeNetHandler.java index ff41f1f0c..c955ae361 100644 --- a/src/main/java/dan200/computercraft/shared/util/FakeNetHandler.java +++ b/src/main/java/dan200/computercraft/shared/util/FakeNetHandler.java @@ -242,10 +242,10 @@ public class FakeNetHandler extends ServerPlayNetworkHandler { } - @Override - public void onConfirmScreenAction( ConfirmScreenActionC2SPacket packet ) - { - } + // @Override + // public void onConfirmScreenAction( ConfirmScreenActionC2SPacket packet ) + // { + // } @Override public void onSignUpdate( @Nonnull UpdateSignC2SPacket packet ) @@ -367,10 +367,5 @@ public class FakeNetHandler extends ServerPlayNetworkHandler public void disableAutoRead() { } - - @Override - public void setCompressionThreshold( int threshold ) - { - } } } diff --git a/src/main/java/dan200/computercraft/shared/util/FixedPointTileEntityType.java b/src/main/java/dan200/computercraft/shared/util/FixedPointTileEntityType.java deleted file mode 100644 index 2890f3540..000000000 --- a/src/main/java/dan200/computercraft/shared/util/FixedPointTileEntityType.java +++ /dev/null @@ -1,61 +0,0 @@ -/* - * This file is part of ComputerCraft - http://www.computercraft.info - * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. - * Send enquiries to dratcliffe@gmail.com - */ - -package dan200.computercraft.shared.util; - -import net.minecraft.block.Block; -import net.minecraft.block.entity.BlockEntity; -import net.minecraft.block.entity.BlockEntityType; - -import javax.annotation.Nonnull; -import java.util.Collections; -import java.util.function.Function; -import java.util.function.Supplier; - -/** - * A {@link BlockEntityType} whose supplier uses itself as an argument. - * - * @param The type of the produced tile entity. - */ -public final class FixedPointTileEntityType extends BlockEntityType -{ - private final Supplier block; - - private FixedPointTileEntityType( Supplier block, Supplier builder ) - { - super( builder, Collections.emptySet(), null ); - this.block = block; - } - - public static FixedPointTileEntityType create( Supplier block, Function, T> builder ) - { - return new FixedPointSupplier<>( block, builder ).factory; - } - - @Override - public boolean supports( @Nonnull Block block ) - { - return block == this.block.get(); - } - - private static final class FixedPointSupplier implements Supplier - { - final FixedPointTileEntityType factory; - private final Function, T> builder; - - private FixedPointSupplier( Supplier block, Function, T> builder ) - { - factory = new FixedPointTileEntityType<>( block, this ); - this.builder = builder; - } - - @Override - public T get() - { - return builder.apply( factory ); - } - } -} diff --git a/src/main/java/dan200/computercraft/shared/util/ImpostorRecipe.java b/src/main/java/dan200/computercraft/shared/util/ImpostorRecipe.java index 3422ba17e..92967f1b7 100644 --- a/src/main/java/dan200/computercraft/shared/util/ImpostorRecipe.java +++ b/src/main/java/dan200/computercraft/shared/util/ImpostorRecipe.java @@ -29,7 +29,7 @@ public final class ImpostorRecipe extends ShapedRecipe { String group = JsonHelper.getString( json, "group", "" ); ShapedRecipe recipe = RecipeSerializer.SHAPED.read( identifier, json ); - ItemStack result = ShapedRecipe.getItem( JsonHelper.getObject( json, "result" ) ); + ItemStack result = ShapedRecipe.outputFromJson( JsonHelper.getObject( json, "result" ) ); return new ImpostorRecipe( identifier, group, recipe.getWidth(), recipe.getHeight(), recipe.getIngredients(), result ); } diff --git a/src/main/java/dan200/computercraft/shared/util/ImpostorShapelessRecipe.java b/src/main/java/dan200/computercraft/shared/util/ImpostorShapelessRecipe.java index a816b3f39..c1fcf7cc9 100644 --- a/src/main/java/dan200/computercraft/shared/util/ImpostorShapelessRecipe.java +++ b/src/main/java/dan200/computercraft/shared/util/ImpostorShapelessRecipe.java @@ -42,7 +42,7 @@ public final class ImpostorShapelessRecipe extends ShapelessRecipe throw new JsonParseException( "Too many ingredients for shapeless recipe the max is 9" ); } - ItemStack itemstack = ShapedRecipe.getItem( JsonHelper.getObject( json, "result" ) ); + ItemStack itemstack = ShapedRecipe.outputFromJson( JsonHelper.getObject( json, "result" ) ); return new ImpostorShapelessRecipe( id, s, itemstack, ingredients ); } diff --git a/src/main/java/dan200/computercraft/shared/util/InventoryUtil.java b/src/main/java/dan200/computercraft/shared/util/InventoryUtil.java index 8f8ec71eb..9d836718b 100644 --- a/src/main/java/dan200/computercraft/shared/util/InventoryUtil.java +++ b/src/main/java/dan200/computercraft/shared/util/InventoryUtil.java @@ -49,7 +49,7 @@ public final class InventoryUtil return ((InventoryProvider) block).getInventory( blockState, world, pos ); } // Check if block is BlockEntity w/ Inventory - if( block.hasBlockEntity() ) + if( blockState.hasBlockEntity() ) { BlockEntity tileEntity = world.getBlockEntity( pos ); diff --git a/src/main/java/dan200/computercraft/shared/util/InvisibleSlot.java b/src/main/java/dan200/computercraft/shared/util/InvisibleSlot.java new file mode 100644 index 000000000..1e36de8ca --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/util/InvisibleSlot.java @@ -0,0 +1,39 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.shared.util; + +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.inventory.Inventory; +import net.minecraft.item.ItemStack; +import net.minecraft.screen.slot.Slot; + +import javax.annotation.Nonnull; + +public class InvisibleSlot extends Slot +{ + public InvisibleSlot( Inventory container, int slot ) + { + super( container, slot, 0, 0 ); + } + + @Override + public boolean canInsert( @Nonnull ItemStack stack ) + { + return false; + } + + @Override + public boolean canTakeItems( @Nonnull PlayerEntity player ) + { + return false; + } + + @Override + public boolean isEnabled() + { + return false; + } +} diff --git a/src/main/java/dan200/computercraft/shared/util/NonNullSupplier.java b/src/main/java/dan200/computercraft/shared/util/NonNullSupplier.java new file mode 100644 index 000000000..44979fbe9 --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/util/NonNullSupplier.java @@ -0,0 +1,15 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.shared.util; + +import javax.annotation.Nonnull; + +@FunctionalInterface +public interface NonNullSupplier +{ + @Nonnull + T get(); +} diff --git a/src/main/java/dan200/computercraft/shared/util/WorldUtil.java b/src/main/java/dan200/computercraft/shared/util/WorldUtil.java index bb896d146..33c153401 100644 --- a/src/main/java/dan200/computercraft/shared/util/WorldUtil.java +++ b/src/main/java/dan200/computercraft/shared/util/WorldUtil.java @@ -36,7 +36,7 @@ public final class WorldUtil public static boolean isLiquidBlock( World world, BlockPos pos ) { - if( !World.isInBuildLimit( pos ) ) + if( !world.isInBuildLimit( pos ) ) { return false; } diff --git a/src/main/resources/assets/computercraft/lang/de_de.json b/src/main/resources/assets/computercraft/lang/de_de.json index b9ea46bed..ab3205ce0 100644 --- a/src/main/resources/assets/computercraft/lang/de_de.json +++ b/src/main/resources/assets/computercraft/lang/de_de.json @@ -34,14 +34,15 @@ "upgrade.minecraft.diamond_axe.adjective": "Holzfällen", "upgrade.minecraft.diamond_hoe.adjective": "Ackerbau", "upgrade.minecraft.crafting_table.adjective": "Handwerk", - "upgrade.computercraft.wireless_modem_normal.adjective": "Ender", - "upgrade.computercraft.wireless_modem_advanced.adjective": "Kabellos", + "upgrade.computercraft.wireless_modem_normal.adjective": "Kabellos", + "upgrade.computercraft.wireless_modem_advanced.adjective": "Ender", "upgrade.computercraft.speaker.adjective": "Laut", "chat.computercraft.wired_modem.peripheral_connected": "Peripheriegerät \"%s\" mit dem Netzwerk verbunden", "chat.computercraft.wired_modem.peripheral_disconnected": "Peripheriegerät \"%s\" vom Netzwerk getrennt", "commands.computercraft.synopsis": "Verschiedene Befehle um Computer zu kontrollieren.", "commands.computercraft.desc": "Der /computercraft Befehl enthält verschiedene Werkzeuge um Computer zu debuggen, kontrollieren oder mit ihnen zu interagieren.", "commands.computercraft.help.synopsis": "Zeigt die Hilfe für den angegebenen Befehl", + "commands.computercraft.help.desc": "Zeigt diese Hilfe", "commands.computercraft.help.no_children": "%s hat keine Unterbefehle", "commands.computercraft.help.no_command": "Unbekannter Befehl '%s'", "commands.computercraft.dump.synopsis": "Zeigt den Status eines Computers.", @@ -89,6 +90,7 @@ "argument.computercraft.computer.no_matching": "Kein Computer passt auf '%s'", "argument.computercraft.computer.many_matching": "Mehrere Computer passen auf '%s' (Instanzen %s)", "argument.computercraft.tracking_field.no_field": "Unbekanntes Feld '%s'", + "argument.computercraft.argument_expected": "Parameter erwartet", "tracking_field.computercraft.tasks.name": "Aufgaben", "tracking_field.computercraft.total.name": "Gesamtzeit", "tracking_field.computercraft.average.name": "Durchschnittliche Zeit", @@ -107,7 +109,5 @@ "tracking_field.computercraft.coroutines_dead.name": "Koroutinen gelöscht", "gui.computercraft.tooltip.copy": "In die Zwischenablage kopieren", "gui.computercraft.tooltip.computer_id": "Computer ID: %s", - "gui.computercraft.tooltip.disk_id": "Disketten ID: %s", - "argument.computercraft.argument_expected": "Parameter erwartet", - "commands.computercraft.help.desc": "Zeigt diese Hilfe" + "gui.computercraft.tooltip.disk_id": "Disketten ID: %s" } diff --git a/src/main/resources/assets/computercraft/lang/en_us.json b/src/main/resources/assets/computercraft/lang/en_us.json index f64be4083..9653b1347 100644 --- a/src/main/resources/assets/computercraft/lang/en_us.json +++ b/src/main/resources/assets/computercraft/lang/en_us.json @@ -31,7 +31,6 @@ "upgrade.minecraft.diamond_sword.adjective": "Melee", "upgrade.minecraft.diamond_shovel.adjective": "Digging", "upgrade.minecraft.diamond_pickaxe.adjective": "Mining", - "upgrade.minecraft.netherite_pickaxe.adjective": "Netherite Mining", "upgrade.minecraft.diamond_axe.adjective": "Felling", "upgrade.minecraft.diamond_hoe.adjective": "Farming", "upgrade.minecraft.crafting_table.adjective": "Crafty", @@ -111,5 +110,26 @@ "tracking_field.computercraft.coroutines_dead.name": "Coroutines disposed", "gui.computercraft.tooltip.copy": "Copy to clipboard", "gui.computercraft.tooltip.computer_id": "Computer ID: %s", - "gui.computercraft.tooltip.disk_id": "Disk ID: %s" + "gui.computercraft.tooltip.disk_id": "Disk ID: %s", + "gui.computercraft.tooltip.turn_on": "Turn this computer on", + "gui.computercraft.tooltip.turn_on.key": "Hold Ctrl+R", + "gui.computercraft.tooltip.turn_off": "Turn this computer off", + "gui.computercraft.tooltip.turn_off.key": "Hold Ctrl+S", + "gui.computercraft.tooltip.terminate": "Stop the currently running code", + "gui.computercraft.tooltip.terminate.key": "Hold Ctrl+T", + "gui.computercraft.upload.success": "Upload Succeeded", + "gui.computercraft.upload.success.msg": "%d files uploaded.", + "gui.computercraft.upload.failed": "Upload Failed", + "gui.computercraft.upload.failed.out_of_space": "Not enough space on the computer for these files.", + "gui.computercraft.upload.failed.computer_off": "You must turn the computer on before uploading files.", + "gui.computercraft.upload.failed.too_much": "Your files are too large to be uploaded.", + "gui.computercraft.upload.failed.name_too_long": "File names are too long to be uploaded.", + "gui.computercraft.upload.failed.too_many_files": "Cannot upload this many files.", + "gui.computercraft.upload.failed.overwrite_dir": "Cannot upload %s, as there is already a directory with the same name.", + "gui.computercraft.upload.failed.generic": "Uploading files failed (%s)", + "gui.computercraft.upload.failed.corrupted": "Files corrupted when uploading. Please try again.", + "gui.computercraft.upload.overwrite": "Files would be overwritten", + "gui.computercraft.upload.overwrite.detail": "The following files will be overwritten when uploading. Continue?%s", + "gui.computercraft.upload.overwrite_button": "Overwrite", + "gui.computercraft.pocket_computer_overlay": "Pocket computer open. Press ESC to close." } diff --git a/src/main/resources/assets/computercraft/lang/fr_fr.json b/src/main/resources/assets/computercraft/lang/fr_fr.json index b97fa5b4c..f42efd5dd 100644 --- a/src/main/resources/assets/computercraft/lang/fr_fr.json +++ b/src/main/resources/assets/computercraft/lang/fr_fr.json @@ -1,4 +1,5 @@ { + "itemGroup.computercraft.main": "ComputerCraft", "block.computercraft.computer_normal": "Ordinateur", "block.computercraft.computer_advanced": "Ordinateur avancé", "block.computercraft.computer_command": "Ordinateur de commande", @@ -17,7 +18,7 @@ "block.computercraft.turtle_normal.upgraded_twice": "Tortue %s et %s", "block.computercraft.turtle_advanced": "Tortue avancée", "block.computercraft.turtle_advanced.upgraded": "Tortue %s avancée", - "block.computercraft.turtle_advanced.upgraded_twice": "Tortue %s %s avancée", + "block.computercraft.turtle_advanced.upgraded_twice": "Tortue %s et %s avancée", "item.computercraft.disk": "Disquette", "item.computercraft.treasure_disk": "Disquette", "item.computercraft.printed_page": "Page imprimée", @@ -26,56 +27,65 @@ "item.computercraft.pocket_computer_normal": "Ordinateur de poche", "item.computercraft.pocket_computer_normal.upgraded": "Ordinateur de poche %s", "item.computercraft.pocket_computer_advanced": "Ordinateur de poche avancé", - "item.computercraft.pocket_computer_advanced.upgraded": "Ordinateur de poche %s avancé", - "upgrade.minecraft.diamond_sword.adjective": "combattante", - "upgrade.minecraft.diamond_shovel.adjective": "excavatrice", - "upgrade.minecraft.diamond_pickaxe.adjective": "minière", - "upgrade.minecraft.diamond_axe.adjective": "forestière", - "upgrade.minecraft.diamond_hoe.adjective": "agricole", - "upgrade.minecraft.crafting_table.adjective": "ouvrière", - "upgrade.computercraft.wireless_modem_normal.adjective": "sans fil", + "item.computercraft.pocket_computer_advanced.upgraded": "Ordinateur de poche avancé %s", + "upgrade.minecraft.diamond_sword.adjective": "De Combat", + "upgrade.minecraft.diamond_shovel.adjective": "Excavatrice", + "upgrade.minecraft.diamond_pickaxe.adjective": "Mineuse", + "upgrade.minecraft.diamond_axe.adjective": "Bûcheronne", + "upgrade.minecraft.diamond_hoe.adjective": "Fermière", + "upgrade.minecraft.crafting_table.adjective": "Ouvrière", + "upgrade.computercraft.wireless_modem_normal.adjective": "Sans Fil", "upgrade.computercraft.wireless_modem_advanced.adjective": "de l'End", "upgrade.computercraft.speaker.adjective": "Bruyante", "chat.computercraft.wired_modem.peripheral_connected": "Le périphérique \"%s\" est connecté au réseau", "chat.computercraft.wired_modem.peripheral_disconnected": "Le périphérique \"%s\" est déconnecté du réseau", + "commands.computercraft.synopsis": "Commandes diverses pour contrôler les ordinateurs.", + "commands.computercraft.desc": "La commande /computercraft fournit des outils d'administration et de débogage variés pour contrôler et interagir avec les ordinateurs.", + "commands.computercraft.help.synopsis": "Fournit de l'aide pour une commande spécifique", "commands.computercraft.help.desc": "Affiche ce message d'aide", - "itemGroup.computercraft": "ComputerCraft", "commands.computercraft.help.no_children": "%s n'a pas de sous-commandes", + "commands.computercraft.help.no_command": "La commande '%s' n'existe pas", "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.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é", "commands.computercraft.turn_on.synopsis": "Démarre des ordinateurs à distance.", + "commands.computercraft.turn_on.desc": "Démarre les ordinateurs dans la 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.turn_on.done": "%s/%s ordinateurs ont été allumés", "commands.computercraft.tp.synopsis": "Se téléporter à la position de l'ordinateur spécifié.", "commands.computercraft.tp.desc": "Se téléporter à la position de l'ordinateur. Vous pouvez spécifier l'identifiant d'instance (ex. 123) ou l'identifiant d'ordinateur (ex. #123).", "commands.computercraft.tp.action": "Se téléporter vers cet ordinateur", "commands.computercraft.tp.not_player": "Impossible d'ouvrir un terminal pour un non-joueur", "commands.computercraft.tp.not_there": "Impossible de localiser cet ordinateur dans le monde", + "commands.computercraft.view.synopsis": "Visualiser le terminal de cet ordinateur.", + "commands.computercraft.view.desc": "Ouvre le terminal d'un ordinateur, autorisant le contrôle à distance. Ceci ne permet pas d'accéder à l'inventaire des tortues. Vous pouvez spécifier l'identifiant d'instance (ex. 123) ou l'identifiant d'ordinateur (ex. #123).", "commands.computercraft.view.action": "Visualiser cet ordinateur", "commands.computercraft.view.not_player": "Impossible d'ouvrir un terminal pour un non-joueur", "commands.computercraft.track.synopsis": "Surveille les temps d'exécution des ordinateurs.", "commands.computercraft.track.desc": "Surveillez combien de temps prend une exécutions sur les ordinateurs, ainsi que le nombre d’événements capturés. Les informations sont affichées d'une manière similaire à la commande /forge track, utile pour diagnostiquer les sources de latence.", "commands.computercraft.track.start.synopsis": "Démarrer la surveillance de tous les ordinateurs", + "commands.computercraft.track.start.desc": "Démarre la surveillance de tous les temps d'exécution et du nombre d'événements gérés. Ceci effacera les résultats de la dernière exécution.", "commands.computercraft.track.start.stop": "Exécutez %s pour arrêter la surveillance et visualiser les résultats", "commands.computercraft.track.stop.synopsis": "Arrêter la surveillance de tous les ordinateurs", "commands.computercraft.track.stop.desc": "Arrêter la surveillance des événements et des temps d'exécution", "commands.computercraft.track.stop.action": "Cliquez pour arrêter la surveillance", - "commands.computercraft.help.no_command": "Commande '%s' non reconnue", - "commands.computercraft.generic.no": "N", - "commands.computercraft.generic.exception": "Exception non gérée (%s)", - "gui.computercraft.tooltip.disk_id": "ID de disque : %s", "commands.computercraft.track.stop.not_enabled": "Surveillance actuellement désactivée", "commands.computercraft.track.dump.synopsis": "Effacer les dernières informations de surveillance", "commands.computercraft.track.dump.desc": "Efface les derniers résultats de surveillance des ordinateurs.", + "commands.computercraft.track.dump.no_timings": "Temps non disponibles", "commands.computercraft.track.dump.computer": "Ordinateur", "commands.computercraft.reload.synopsis": "Actualiser le fichier de configuration de ComputerCraft", "commands.computercraft.reload.desc": "Actualise le fichier de configuration de ComputerCraft", "commands.computercraft.reload.done": "Configuration actualisée", "commands.computercraft.queue.synopsis": "Envoyer un événement computer_command à un ordinateur de commande", + "commands.computercraft.queue.desc": "Envoie un événement computer_command à un ordinateur de commande, en passant les éventuels arguments additionnels. Ceci est principalement conçu pour les map makers, imitant la commande /trigger, pour une utilisation avec les ordinateurs. N'importe quel joueur peut exécuter cette commande, qui sera exécutée le plus souvent avec un événement de clic sur du texte.", "commands.computercraft.generic.no_position": "", "commands.computercraft.generic.position": "%s, %s, %s", "commands.computercraft.generic.yes": "O", + "commands.computercraft.generic.no": "N", + "commands.computercraft.generic.exception": "Exception non gérée (%s)", "commands.computercraft.generic.additional_rows": "%d lignes supplémentaires…", "argument.computercraft.computer.no_matching": "Pas d'ordinateurs correspondant à '%s'", "argument.computercraft.computer.many_matching": "Plusieurs ordinateurs correspondent à '%s' (instances %s)", @@ -91,23 +101,13 @@ "tracking_field.computercraft.fs.name": "Opérations sur le système de fichiers", "tracking_field.computercraft.turtle.name": "Opérations sur les tortues", "tracking_field.computercraft.http.name": "Requêtes HTTP", + "tracking_field.computercraft.http_upload.name": "Publication HTTP", + "tracking_field.computercraft.http_download.name": "Téléchargement HTTP", "tracking_field.computercraft.websocket_incoming.name": "Websocket entrant", "tracking_field.computercraft.websocket_outgoing.name": "Websocket sortant", "tracking_field.computercraft.coroutines_created.name": "Coroutines créées", "tracking_field.computercraft.coroutines_dead.name": "Coroutines mortes", "gui.computercraft.tooltip.copy": "Copier dans le Presse-Papiers", "gui.computercraft.tooltip.computer_id": "ID d'ordinateur : %s", - "commands.computercraft.track.dump.no_timings": "Temps non disponibles", - "tracking_field.computercraft.http_upload.name": "Publication HTTP", - "commands.computercraft.desc": "La commande /computercraft fournit des outils d'administration et de débogage variés pour contrôler et interagir avec les 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.turn_on.desc": "Démarre les ordinateurs dans la 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.view.synopsis": "Visualiser le terminal de cet ordinateur.", - "commands.computercraft.view.desc": "Ouvre le terminal d'un ordinateur, autorisant le contrôle à distance. Ceci ne permet pas d'accéder à l'inventaire des tortues. Vous pouvez spécifier l'identifiant d'instance (ex. 123) ou l'identifiant d'ordinateur (ex. #123).", - "commands.computercraft.track.start.desc": "Démarre la surveillance de tous les temps d'exécution et du nombre d'événements gérés. Ceci effacera les résultats de la dernière exécution.", - "commands.computercraft.queue.desc": "Envoie un événement computer_command à un ordinateur de commande, en passant les éventuels arguments additionnels. Ceci est principalement conçu pour les map makers, imitant la commande /trigger, pour une utilisation avec les ordinateurs. N'importe quel joueur peut exécuter cette commande, qui sera exécutée le plus souvent avec un événement de clic sur du texte.", - "commands.computercraft.help.synopsis": "Fournit de l'aide pour une commande spécifique", - "tracking_field.computercraft.http_download.name": "Téléchargement HTTP", - "commands.computercraft.synopsis": "Commandes diverses pour contrôler les ordinateurs.", - "commands.computercraft.shutdown.done": "%s/%s ordinateurs arrêté" + "gui.computercraft.tooltip.disk_id": "ID de disque : %s" } diff --git a/src/main/resources/assets/computercraft/lang/it_it.json b/src/main/resources/assets/computercraft/lang/it_it.json index 765604081..bf45acadc 100644 --- a/src/main/resources/assets/computercraft/lang/it_it.json +++ b/src/main/resources/assets/computercraft/lang/it_it.json @@ -1,5 +1,5 @@ { - "itemGroup.computercraft": "ComputerCraft", + "itemGroup.computercraft.main": "ComputerCraft", "block.computercraft.computer_normal": "Computer", "block.computercraft.computer_advanced": "Computer Avanzato", "block.computercraft.computer_command": "Computer Comando", @@ -85,29 +85,29 @@ "commands.computercraft.generic.position": "%s, %s, %s", "commands.computercraft.generic.yes": "S", "commands.computercraft.generic.no": "N", + "commands.computercraft.generic.exception": "Eccezione non gestita (%s)", "commands.computercraft.generic.additional_rows": "%d colonne aggiuntive…", "argument.computercraft.computer.no_matching": "Nessun computer che combacia con '%s'", + "argument.computercraft.computer.many_matching": "Molteplici computer che combaciano con '%s' (istanze %s)", + "argument.computercraft.tracking_field.no_field": "Campo sconosciuto '%s'", + "argument.computercraft.argument_expected": "È previsto un argomento", + "tracking_field.computercraft.tasks.name": "Compiti", + "tracking_field.computercraft.total.name": "Tempo totale", "tracking_field.computercraft.average.name": "Tempo medio", "tracking_field.computercraft.max.name": "Tempo massimo", + "tracking_field.computercraft.server_count.name": "Numero di compiti del server", + "tracking_field.computercraft.server_time.name": "Tempo di compito del server", "tracking_field.computercraft.peripheral.name": "Chiamate alle periferiche", "tracking_field.computercraft.fs.name": "Operazioni filesystem", "tracking_field.computercraft.turtle.name": "Operazioni tartarughe", "tracking_field.computercraft.http.name": "Richieste HTTP", "tracking_field.computercraft.http_upload.name": "Upload HTTP", "tracking_field.computercraft.http_download.name": "Download HTTP", - "tracking_field.computercraft.coroutines_created.name": "Coroutine create", - "gui.computercraft.tooltip.copy": "Copia negli appunti", - "gui.computercraft.tooltip.computer_id": "ID Computer: %s", - "gui.computercraft.tooltip.disk_id": "ID Disco: %s", - "commands.computercraft.generic.exception": "Eccezione non gestita (%s)", - "argument.computercraft.computer.many_matching": "Molteplici computer che combaciano con '%s' (istanze %s)", - "argument.computercraft.tracking_field.no_field": "Campo sconosciuto '%s'", - "argument.computercraft.argument_expected": "È previsto un argomento", - "tracking_field.computercraft.tasks.name": "Compiti", - "tracking_field.computercraft.total.name": "Tempo totale", - "tracking_field.computercraft.server_count.name": "Numero di compiti del server", - "tracking_field.computercraft.server_time.name": "Tempo di compito del server", "tracking_field.computercraft.websocket_incoming.name": "Websocket in arrivo", "tracking_field.computercraft.websocket_outgoing.name": "Websocket in uscita", - "tracking_field.computercraft.coroutines_dead.name": "Coroutine cancellate" + "tracking_field.computercraft.coroutines_created.name": "Coroutine create", + "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" } diff --git a/src/main/resources/assets/computercraft/lang/ja_jp.json b/src/main/resources/assets/computercraft/lang/ja_jp.json new file mode 100644 index 000000000..8d43b5b1d --- /dev/null +++ b/src/main/resources/assets/computercraft/lang/ja_jp.json @@ -0,0 +1,131 @@ +{ + "itemGroup.computercraft.main": "ComputerCraft", + "block.computercraft.computer_normal": "コンピューター", + "block.computercraft.computer_advanced": "高度なコンピューター", + "block.computercraft.computer_command": "コマンドコンピューター", + "block.computercraft.disk_drive": "ディスクドライブ", + "block.computercraft.printer": "プリンター", + "block.computercraft.speaker": "スピーカー", + "block.computercraft.monitor_normal": "モニター", + "block.computercraft.monitor_advanced": "高度なモニター", + "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タートル", + "block.computercraft.turtle_advanced": "高度なタートル", + "block.computercraft.turtle_advanced.upgraded": "高度な%sタートル", + "item.computercraft.disk": "フロッピーディスク", + "item.computercraft.treasure_disk": "フロッピーディスク", + "item.computercraft.printed_page": "印刷された紙", + "item.computercraft.printed_pages": "印刷された紙束", + "item.computercraft.printed_book": "印刷された本", + "item.computercraft.pocket_computer_normal": "ポケットコンピュータ", + "item.computercraft.pocket_computer_normal.upgraded": "%sポケットコンピュータ", + "item.computercraft.pocket_computer_advanced.upgraded": "高度な%sポケットコンピュータ", + "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_advanced.adjective": "エンダー", + "upgrade.computercraft.speaker.adjective": "騒音", + "chat.computercraft.wired_modem.peripheral_connected": "周辺の\"%s\"のネットワークに接続されました", + "commands.computercraft.synopsis": "コンピュータを制御するためのさまざまなコマンド。", + "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.action": "このコンピュータの詳細を表示します", + "commands.computercraft.dump.open_path": "このコンピュータのファイルを表示します", + "commands.computercraft.shutdown.synopsis": "コンピュータをリモートでシャットダウンする。", + "commands.computercraft.shutdown.done": "%s/%s コンピューターをシャットダウンしました", + "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.action": "このコンピューターへテレポートします", + "commands.computercraft.tp.not_player": "非プレイヤー用のターミナルを開くことができません", + "commands.computercraft.tp.not_there": "世界でコンピュータを見つけることができませんでした", + "commands.computercraft.view.synopsis": "コンピュータのターミナルを表示します。", + "commands.computercraft.turn_on.synopsis": "コンピューターをリモートで起動します。", + "commands.computercraft.view.action": "このコンピュータを見ます", + "commands.computercraft.view.not_player": "非プレイヤー用のターミナルを開くことができません", + "commands.computercraft.track.synopsis": "コンピュータの実行時間を追跡します。", + "commands.computercraft.track.start.synopsis": "すべてのコンピュータの追跡を開始します", + "commands.computercraft.track.start.desc": "すべてのコンピュータの実行時間とイベント数の追跡を開始します。 これにより、以前の実行結果が破棄されます。", + "commands.computercraft.track.stop.synopsis": "すべてのコンピュータの追跡を停止します", + "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.reload.synopsis": "コンピュータークラフトのコンフィグファイルを再読み込みします", + "commands.computercraft.reload.done": "コンフィグを再読み込みしました", + "commands.computercraft.queue.synopsis": "computer_command インベントをコマンドコンピューターに送信します", + "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'に一致するコンピュータはありません", + "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": "平均時間", + "tracking_field.computercraft.max.name": "最大時間", + "tracking_field.computercraft.server_count.name": "サーバータスク数", + "tracking_field.computercraft.server_time.name": "サーバータスク時間", + "tracking_field.computercraft.fs.name": "ファイルシステム演算", + "tracking_field.computercraft.turtle.name": "タートル演算", + "tracking_field.computercraft.http.name": "HTTPリクエスト", + "tracking_field.computercraft.http_upload.name": "HTTPアップロード", + "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_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.key": "Ctrl+S 長押し", + "gui.computercraft.tooltip.terminate": "現在実行中のコードを停止する", + "gui.computercraft.tooltip.terminate.key": "Ctrl+T 長押し", + "gui.computercraft.upload.success": "アップロードは成功しました", + "gui.computercraft.upload.failed": "アップロードに失敗しました", + "gui.computercraft.upload.failed.out_of_space": "これらのファイルに必要なスペースがコンピュータ上にありません。", + "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_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" +} diff --git a/src/main/resources/assets/computercraft/lang/ko_kr.json b/src/main/resources/assets/computercraft/lang/ko_kr.json index 919d7e56d..425586925 100644 --- a/src/main/resources/assets/computercraft/lang/ko_kr.json +++ b/src/main/resources/assets/computercraft/lang/ko_kr.json @@ -1,5 +1,5 @@ { - "itemGroup.computercraft": "컴퓨터크래프트", + "itemGroup.computercraft.main": "컴퓨터크래프트", "block.computercraft.computer_normal": "컴퓨터", "block.computercraft.computer_advanced": "고급 컴퓨터", "block.computercraft.computer_command": "명령 컴퓨터", diff --git a/src/main/resources/assets/computercraft/lang/nl_nl.json b/src/main/resources/assets/computercraft/lang/nl_nl.json index 98a174583..6a6ee310d 100644 --- a/src/main/resources/assets/computercraft/lang/nl_nl.json +++ b/src/main/resources/assets/computercraft/lang/nl_nl.json @@ -1,5 +1,5 @@ { - "itemGroup.computercraft": "ComputerCraft", + "itemGroup.computercraft.main": "ComputerCraft", "block.computercraft.computer_normal": "Computer", "block.computercraft.computer_advanced": "Geavanceerde Computer", "block.computercraft.computer_command": "Commandocomputer", @@ -51,9 +51,6 @@ "commands.computercraft.shutdown.synopsis": "Sluit computers af op afstand.", "commands.computercraft.shutdown.desc": "Sluit alle genoemde computers af, of geen enkele wanneer niet gespecificeerd. Je kunt een instance-id (bijv. 123), computer-id (bijv. #123) of computer-label (bijv. \\\"@Mijn Computer\\\") opgeven.", "commands.computercraft.shutdown.done": "%s/%s computers afgesloten", - "commands.computercraft.track.stop.not_enabled": "Er worden op dit moment geen computers bijgehouden", - "commands.computercraft.track.dump.desc": "Dump de laatste resultaten van het bijhouden van computers.", - "commands.computercraft.queue.desc": "Verzend een computer_commando event naar een commandocomputer. Additionele argumenten worden doorgegeven. Dit is vooral bedoeld voor mapmakers. Het doet dienst als een computer-vriendelijke versie van /trigger. Elke speler kan het commando uitvoeren, wat meestal gedaan zal worden door een text-component klik-event.", "commands.computercraft.turn_on.synopsis": "Zet computers aan op afstand.", "commands.computercraft.turn_on.desc": "Zet de genoemde computers aan op afstand. Je kunt een instance-id (bijv. 123), computer-id (bijv. #123) of computer-label (bijv. \"@Mijn Computer\") opgeven.", "commands.computercraft.turn_on.done": "%s/%s computers aangezet", @@ -74,12 +71,17 @@ "commands.computercraft.track.stop.synopsis": "Stop het bijhouden van alle computers", "commands.computercraft.track.stop.desc": "Stop het bijhouden van uitvoertijd en aantal behandelde events van alle computers", "commands.computercraft.track.stop.action": "Klik om bijhouden te stoppen", + "commands.computercraft.track.stop.not_enabled": "Er worden op dit moment geen computers bijgehouden", + "commands.computercraft.track.dump.synopsis": "Dump de laatste resultaten van het bijhouden van computers", + "commands.computercraft.track.dump.desc": "Dump de laatste resultaten van het bijhouden van computers.", "commands.computercraft.track.dump.no_timings": "Geen tijden beschikbaar", "commands.computercraft.track.dump.computer": "Computer", "commands.computercraft.reload.synopsis": "Herlaad het ComputerCraft configuratiebestand", "commands.computercraft.reload.desc": "Herlaad het ComputerCraft configuratiebestand", "commands.computercraft.reload.done": "Configuratie herladen", "commands.computercraft.queue.synopsis": "Verzend een computer_command event naar een commandocomputer", + "commands.computercraft.queue.desc": "Verzend een computer_commando event naar een commandocomputer. Additionele argumenten worden doorgegeven. Dit is vooral bedoeld voor mapmakers. Het doet dienst als een computer-vriendelijke versie van /trigger. Elke speler kan het commando uitvoeren, wat meestal gedaan zal worden door een text-component klik-event.", + "commands.computercraft.generic.no_position": "", "commands.computercraft.generic.position": "%s, %s, %s", "commands.computercraft.generic.yes": "J", "commands.computercraft.generic.no": "N", @@ -87,6 +89,7 @@ "commands.computercraft.generic.additional_rows": "%d additionele rijen…", "argument.computercraft.computer.no_matching": "Geen computer matcht '%s'", "argument.computercraft.computer.many_matching": "Meerdere computers matchen '%s' (instanties %s)", + "argument.computercraft.tracking_field.no_field": "Onbekend veld '%s'", "argument.computercraft.argument_expected": "Argument verwacht", "tracking_field.computercraft.tasks.name": "Taken", "tracking_field.computercraft.total.name": "Totale tijd", @@ -95,19 +98,16 @@ "tracking_field.computercraft.server_count.name": "Aantal server-taken", "tracking_field.computercraft.server_time.name": "Server-taak tijd", "tracking_field.computercraft.peripheral.name": "Randapparatuur aanroepen", + "tracking_field.computercraft.fs.name": "Restandssysteem operaties", "tracking_field.computercraft.turtle.name": "Turtle operaties", "tracking_field.computercraft.http.name": "HTTP verzoeken", "tracking_field.computercraft.http_upload.name": "HTTP upload", + "tracking_field.computercraft.http_download.name": "HTTP download", "tracking_field.computercraft.websocket_incoming.name": "Websocket inkomend", "tracking_field.computercraft.websocket_outgoing.name": "Websocket uitgaand", "tracking_field.computercraft.coroutines_created.name": "Coroutines gecreëerd", + "tracking_field.computercraft.coroutines_dead.name": "Coroutines verwijderd", "gui.computercraft.tooltip.copy": "Kopiëren naar klembord", "gui.computercraft.tooltip.computer_id": "Computer ID: %s", - "gui.computercraft.tooltip.disk_id": "Diskette ID: %s", - "tracking_field.computercraft.http_download.name": "HTTP download", - "commands.computercraft.generic.no_position": "", - "commands.computercraft.track.dump.synopsis": "Dump de laatste resultaten van het bijhouden van computers", - "tracking_field.computercraft.fs.name": "Restandssysteem operaties", - "tracking_field.computercraft.coroutines_dead.name": "Coroutines verwijderd", - "argument.computercraft.tracking_field.no_field": "Onbekend veld '%s'" + "gui.computercraft.tooltip.disk_id": "Diskette ID: %s" } diff --git a/src/main/resources/assets/computercraft/lang/pl_pl.json b/src/main/resources/assets/computercraft/lang/pl_pl.json index 027fc006e..d2db1c9a7 100644 --- a/src/main/resources/assets/computercraft/lang/pl_pl.json +++ b/src/main/resources/assets/computercraft/lang/pl_pl.json @@ -1,5 +1,5 @@ { - "itemGroup.computercraft": "ComputerCraft", + "itemGroup.computercraft.main": "ComputerCraft", "block.computercraft.computer_normal": "Komputer", "block.computercraft.computer_advanced": "Zaawansowany Komputer", "block.computercraft.computer_command": "Komputer Komendowy", diff --git a/src/main/resources/assets/computercraft/lang/pt_br.json b/src/main/resources/assets/computercraft/lang/pt_br.json index f5ec8017a..6f67e0cbc 100644 --- a/src/main/resources/assets/computercraft/lang/pt_br.json +++ b/src/main/resources/assets/computercraft/lang/pt_br.json @@ -38,21 +38,21 @@ "upgrade.computercraft.speaker.adjective": "(Alto-Falante)", "chat.computercraft.wired_modem.peripheral_connected": "Periférico \"%s\" conectado à rede", "chat.computercraft.wired_modem.peripheral_disconnected": "Periférico \"%s\" desconectado da rede", - "gui.computercraft.tooltip.copy": "Copiar para a área de transferência", - "commands.computercraft.tp.synopsis": "Teleprota para um computador específico.", - "commands.computercraft.turn_on.done": "Ligou %s/%s computadores", - "commands.computercraft.turn_on.desc": "Liga os computadores em escuta. Você pode especificar o id de instância do computador (ex.: 123), id do computador (ex.: #123) ou o rótulo (ex.: \"@MeuComputador\").", - "commands.computercraft.turn_on.synopsis": "Liga computadores remotamente.", - "commands.computercraft.shutdown.done": "Desliga %s/%s computadores", - "commands.computercraft.shutdown.desc": "Desliga os computadores em escuta ou todos caso não tenha sido especificado. Você pode especificar o id de instância do computador (ex.: 123), id do computador (ex.: #123) ou o rótulo (ex.: \"@MeuComputador\").", - "commands.computercraft.shutdown.synopsis": "Desliga computadores remotamente.", - "commands.computercraft.dump.action": "Ver mais informação sobre este computador", - "commands.computercraft.dump.desc": "Mostra o status de todos os computadores ou uma informação específica sobre um computador. Você pode especificar o id de instância do computador (ex.: 123), id do computador (ex.: #123) ou o rótulo (ex.: \"@MeuComputador\").", - "commands.computercraft.dump.synopsis": "Mostra status de computadores.", - "commands.computercraft.help.no_command": "Comando '%s' não existe", - "commands.computercraft.help.no_children": "%s não tem sub-comandos", - "commands.computercraft.help.desc": "Mostra essa mensagem de ajuda", - "commands.computercraft.help.synopsis": "Providencia ajuda para um comando específico", + "commands.computercraft.synopsis": "Vários comandos para controlar computadores.", "commands.computercraft.desc": "O comando /computercraft providencia várias ferramentas de depuração e administração para controle e interação com computadores.", - "commands.computercraft.synopsis": "Vários comandos para controlar computadores." + "commands.computercraft.help.synopsis": "Providencia ajuda para um comando específico", + "commands.computercraft.help.desc": "Mostra essa mensagem de ajuda", + "commands.computercraft.help.no_children": "%s não tem sub-comandos", + "commands.computercraft.help.no_command": "Comando '%s' não existe", + "commands.computercraft.dump.synopsis": "Mostra status de computadores.", + "commands.computercraft.dump.desc": "Mostra o status de todos os computadores ou uma informação específica sobre um computador. Você pode especificar o id de instância do computador (ex.: 123), id do computador (ex.: #123) ou o rótulo (ex.: \"@MeuComputador\").", + "commands.computercraft.dump.action": "Ver mais informação sobre este computador", + "commands.computercraft.shutdown.synopsis": "Desliga computadores remotamente.", + "commands.computercraft.shutdown.desc": "Desliga os computadores em escuta ou todos caso não tenha sido especificado. Você pode especificar o id de instância do computador (ex.: 123), id do computador (ex.: #123) ou o rótulo (ex.: \"@MeuComputador\").", + "commands.computercraft.shutdown.done": "Desliga %s/%s computadores", + "commands.computercraft.turn_on.synopsis": "Liga computadores remotamente.", + "commands.computercraft.turn_on.desc": "Liga os computadores em escuta. Você pode especificar o id de instância do computador (ex.: 123), id do computador (ex.: #123) ou o rótulo (ex.: \"@MeuComputador\").", + "commands.computercraft.turn_on.done": "Ligou %s/%s computadores", + "commands.computercraft.tp.synopsis": "Teleprota para um computador específico.", + "gui.computercraft.tooltip.copy": "Copiar para a área de transferência" } diff --git a/src/main/resources/assets/computercraft/lang/ru.json b/src/main/resources/assets/computercraft/lang/ru.json deleted file mode 100644 index 210b77091..000000000 --- a/src/main/resources/assets/computercraft/lang/ru.json +++ /dev/null @@ -1,113 +0,0 @@ -{ - "block.computercraft.computer_normal": "Компьютер", - "block.computercraft.computer_advanced": "Улучшенный компьютер", - "block.computercraft.computer_command": "Командный компьютер", - "block.computercraft.disk_drive": "Дисковод", - "block.computercraft.printer": "Принтер", - "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": "Сетевой кабель", - "block.computercraft.turtle_normal.upgraded": "%s черепашка", - "block.computercraft.turtle_advanced": "Улучшенная черепашка", - "block.computercraft.turtle_advanced.upgraded": "Улучшенная %s черепашка", - "block.computercraft.turtle_advanced.upgraded_twice": "Улучшенная %s %s черепашка", - "item.computercraft.treasure_disk": "Дискета", - "item.computercraft.printed_page": "Напечатанная страница", - "item.computercraft.printed_book": "Напечатанная книга", - "item.computercraft.pocket_computer_normal": "Карманный компьютер", - "item.computercraft.pocket_computer_normal.upgraded": "%s карманный компьютер", - "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_advanced.adjective": "Эндер", - "upgrade.computercraft.speaker.adjective": "Шумящий", - "chat.computercraft.wired_modem.peripheral_connected": "Устройство \"%s\" подключено к сети", - "commands.computercraft.synopsis": "Команды для управления компьютерами.", - "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.action": "Показать больше информации об этом компьютере", - "commands.computercraft.shutdown.synopsis": "Удалённо выключить компьютер.", - "commands.computercraft.shutdown.done": "%s/%s компьютеров выключено", - "commands.computercraft.turn_on.synopsis": "Удалённо включить компьютеры.", - "commands.computercraft.turn_on.done": "%s/%s компьютеров включено", - "commands.computercraft.tp.synopsis": "Телепортироваться к компьютеру.", - "commands.computercraft.tp.desc": "Телепортироваться к месту установки компьютера. Можно указать номер инстанса (например 123) или идентификатор компьютера (#123).", - "commands.computercraft.tp.action": "Телепортироваться к этому компьютеру", - "commands.computercraft.tp.not_there": "Невозможно найти компьютер в мире", - "commands.computercraft.view.synopsis": "Показать терминал компьютера.", - "commands.computercraft.view.not_player": "Нельзя открыть терминал для не-игрока", - "commands.computercraft.view.action": "Открыть этот компьютер", - "commands.computercraft.track.synopsis": "Отслеживание метрик компьютеров.", - "commands.computercraft.track.start.synopsis": "Начать собирать метрики со всех компьютеров", - "commands.computercraft.track.start.desc": "Начать собирать метрики со всех компьютеров: сколько времени выполняется код и сколько событий обрабатывается. Это сотрёт результаты предыдущего сбора.", - "commands.computercraft.track.start.stop": "Запустите %s чтобы остановить сбор метрик и посмотреть результаты", - "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": "Перезагрузить конфигурацию ComputerCraft", - "commands.computercraft.reload.done": "Конфигурация перезагружена", - "commands.computercraft.queue.desc": "Отправить событие computer_command командному компьютеру. Это нужно для создателей карт, более дружественная версия /trigger. Любой игрок сможет запустить команду, которая скорее всего будет выполнена с помощью click event текстового компонента.", - "commands.computercraft.generic.no_position": "<нет позиции>", - "commands.computercraft.generic.position": "%s, %s, %s", - "commands.computercraft.generic.yes": "Д", - "commands.computercraft.generic.no": "Н", - "commands.computercraft.generic.exception": "Необработанное исключение (%s)", - "commands.computercraft.generic.additional_rows": "%d дополнительных строк…", - "argument.computercraft.computer.no_matching": "Нет компьютеров совпадающих с '%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": "Среднее время", - "tracking_field.computercraft.max.name": "Максимальное время", - "tracking_field.computercraft.server_count.name": "Число серверных задач", - "tracking_field.computercraft.server_time.name": "Время серверных задач", - "tracking_field.computercraft.fs.name": "Операций с файлами", - "tracking_field.computercraft.turtle.name": "Действий черепашек", - "tracking_field.computercraft.http.name": "HTTP запросы", - "tracking_field.computercraft.http_upload.name": "HTTP загрузки", - "tracking_field.computercraft.http_download.name": "HTTP скачивания", - "tracking_field.computercraft.websocket_outgoing.name": "Исходящие вебсокеты", - "tracking_field.computercraft.coroutines_created.name": "Корутин создано", - "tracking_field.computercraft.coroutines_dead.name": "Корутин удалено", - "gui.computercraft.tooltip.copy": "Скопировать в буфер обмена", - "gui.computercraft.tooltip.disk_id": "ID дискеты: %s", - "itemGroup.computercraft": "ComputerCraft", - "block.computercraft.wired_modem_full": "Проводной модем", - "block.computercraft.turtle_normal": "Черепашка", - "block.computercraft.turtle_normal.upgraded_twice": "%s %s черепашка", - "item.computercraft.disk": "Дискета", - "item.computercraft.printed_pages": "Напечатанные страницы", - "item.computercraft.pocket_computer_advanced": "Улучшенный карманный компьютер", - "upgrade.computercraft.wireless_modem_normal.adjective": "Беспроводный", - "chat.computercraft.wired_modem.peripheral_disconnected": "Устройство \"%s\" отключено от сети", - "commands.computercraft.desc": "Команда /computercraft предоставляет отладочные и административные утилиты для управления компьютерами.", - "commands.computercraft.dump.desc": "Показать статус всех компьютеров или информацию об одном из компьютеров. Можно указать номер инстанса (например 123), идентификатор компьютера (#123) или метку (\"@My Computer\").", - "commands.computercraft.shutdown.desc": "Выключить указанные компьютеры или все. Можно указать номер инстанса (например 123), идентификатор компьютера (#123) или метку (\"@My Computer\").", - "commands.computercraft.turn_on.desc": "Включить указанные компьютеры. Можно указать номер инстанса (например 123), идентификатор компьютера (#123) или метку (\"@My Computer\").", - "commands.computercraft.tp.not_player": "Нельзя открыть терминал для не-игрока", - "commands.computercraft.view.desc": "Открыть терминал и получить контроль над компьютером. Это не показывает инвентарь черепашек. Можно указать номер инстанса (например 123) или идентификатор компьютера (#123).", - "commands.computercraft.track.desc": "Отслеживание как долго компьютеры выполняют код, как много событий они обрабатывают. Это показывает информацию аналогично /forge track и может быть полезно в поиске причин лагов.", - "commands.computercraft.track.stop.synopsis": "Остановить сбор метрик со всех компьютеров", - "commands.computercraft.track.stop.desc": "Остановить сбор метрик со всех компьютеров: сколько времени выполняется код и сколько событий обрабатывается", - "commands.computercraft.reload.desc": "Перезагрузить конфигурацию ComputerCraft", - "commands.computercraft.queue.synopsis": "Отправить событие computer_command командному компьютеру", - "argument.computercraft.computer.many_matching": "Несколько компьютеров совпадают с '%s' (инстансы %s)", - "tracking_field.computercraft.peripheral.name": "Вызовы периферийных устройств", - "tracking_field.computercraft.websocket_incoming.name": "Входящие вебсокеты", - "gui.computercraft.tooltip.computer_id": "ID компьютера: %s" -} diff --git a/src/main/resources/assets/computercraft/lang/ru_ru.json b/src/main/resources/assets/computercraft/lang/ru_ru.json new file mode 100644 index 000000000..df70622b8 --- /dev/null +++ b/src/main/resources/assets/computercraft/lang/ru_ru.json @@ -0,0 +1,131 @@ +{ + "itemGroup.computercraft.main": "ComputerCraft", + "block.computercraft.computer_normal": "Компьютер", + "block.computercraft.computer_advanced": "Продвинутый компьютер", + "block.computercraft.computer_command": "Командный компьютер", + "block.computercraft.disk_drive": "Дисковод", + "block.computercraft.printer": "Принтер", + "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": "Сетевой кабель", + "block.computercraft.wired_modem_full": "Проводной модем", + "block.computercraft.turtle_normal": "Черепашка", + "block.computercraft.turtle_normal.upgraded": "%s черепашка", + "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": "Напечатанная страница", + "item.computercraft.printed_pages": "Напечатанные страницы", + "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": "Отображает состояние всех компьютеров или конкретной информации об одном компьютере. Ты можешь указать идентификатор экземпляра компьютера (например 123), идентификатор компьютера (например #123) или метку (например \"@Мой компьютер\").", + "commands.computercraft.dump.action": "Просмотреть информацию об этом компьютере", + "commands.computercraft.dump.open_path": "Просмотреть файлы этого компьютера", + "commands.computercraft.shutdown.synopsis": "Удалённо завершить работу компьютеров.", + "commands.computercraft.shutdown.desc": "Завершить работу перечисленных компьютеров или все, если ни один не указан. Ты можешь указать идентификатор экземпляра компьютера (например 123), идентификатор компьютера (например #123) или метку (например \"@Мой компьютер\").", + "commands.computercraft.shutdown.done": "У %s/%s компьютеров завершена работа", + "commands.computercraft.turn_on.synopsis": "Включить компьютеры удалённо.", + "commands.computercraft.turn_on.desc": "Включить перечисленные компьютеры. Ты можешь указать идентификатор экземпляра компьютера (например 123), идентификатор компьютера (например #123) или метку (например \"@Мой компьютер\").", + "commands.computercraft.turn_on.done": "%s/%s компьютеров включено", + "commands.computercraft.tp.synopsis": "Телепортироваться к конкретному компьютеру.", + "commands.computercraft.tp.desc": "Телепортироваться к местоположению компьютера. Ты можешь указать либо идентификатор экземпляра компьютера (например 123) либо идентификатор компьютера (например #123).", + "commands.computercraft.tp.action": "Телепортироваться к этому компьютеру", + "commands.computercraft.tp.not_player": "Нельзя открыть терминал для не-игрока", + "commands.computercraft.tp.not_there": "Нельзя определить в мире местоположение компьютер", + "commands.computercraft.view.synopsis": "Просмотреть терминал компьютера.", + "commands.computercraft.view.desc": "Открыть терминал компьютера, позволяющий удалённо управлять компьютером. Это не предоставляет доступ к инвентарям черепашек. Ты можешь указать либо идентификатор экземпляра компьютера (например 123) либо идентификатор компьютера (например #123).", + "commands.computercraft.view.action": "Просмотреть этот компьютер", + "commands.computercraft.view.not_player": "Нельзя открыть терминал для не-игрока", + "commands.computercraft.track.synopsis": "Отслеживание сред выполнения для компьютеров.", + "commands.computercraft.track.desc": "Отслеживает, как долго компьютеры исполняют, а также то, как много они обрабатывают события. Эта информация представляется аналогично к /forge track и может быть полезной для диагностики лага.", + "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": "Перезагрузить файл конфигурации ComputerCraft'a", + "commands.computercraft.reload.desc": "Перезагружает файл конфигурации ComputerCraft'a", + "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.generic.exception": "Необработанное исключение (%s)", + "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": "Среднее время", + "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 запросы", + "tracking_field.computercraft.http_upload.name": "HTTP загрузки", + "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": "Идентификатор компьютера: %s", + "gui.computercraft.tooltip.disk_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.overwrite_dir": "Нельзя загрузить %s, поскольку папка с таким же названием уже существует.", + "gui.computercraft.upload.failed.generic": "Загрузка файлов не удалась (%s)", + "gui.computercraft.upload.overwrite": "Файлы будут перезаписаны", + "gui.computercraft.upload.overwrite.detail": "При загрузке следующие файлы будут перезаписаны. Продолжить?%s", + "gui.computercraft.upload.overwrite_button": "Перезаписать" +} diff --git a/src/main/resources/assets/computercraft/lang/sv_se.json b/src/main/resources/assets/computercraft/lang/sv_se.json index 1afe76b56..76aa1b74c 100644 --- a/src/main/resources/assets/computercraft/lang/sv_se.json +++ b/src/main/resources/assets/computercraft/lang/sv_se.json @@ -1,4 +1,5 @@ { + "itemGroup.computercraft.main": "ComputerCraft", "block.computercraft.computer_normal": "Dator", "block.computercraft.computer_advanced": "Avancerad Dator", "block.computercraft.computer_command": "Kommandodator", @@ -38,76 +39,75 @@ "upgrade.computercraft.speaker.adjective": "Högljudd", "chat.computercraft.wired_modem.peripheral_connected": "Kringutrustning \"%s\" är kopplad till nätverket", "chat.computercraft.wired_modem.peripheral_disconnected": "Kringutrustning \"%s\" är frånkopplad från nätverket", - "gui.computercraft.tooltip.copy": "Kopiera till urklipp", - "gui.computercraft.tooltip.disk_id": "Diskett-ID: %s", - "gui.computercraft.tooltip.computer_id": "Dator-ID: %s", - "tracking_field.computercraft.coroutines_dead.name": "Coroutines borttagna", - "tracking_field.computercraft.coroutines_created.name": "Coroutines skapade", - "tracking_field.computercraft.websocket_outgoing.name": "Websocket utgående", - "tracking_field.computercraft.websocket_incoming.name": "Websocket ingående", - "tracking_field.computercraft.http_download.name": "HTTP-nedladdning", - "tracking_field.computercraft.http_upload.name": "HTTP-uppladdning", - "tracking_field.computercraft.http.name": "HTTP-förfrågningar", - "tracking_field.computercraft.turtle.name": "Turtle-operationer", - "tracking_field.computercraft.fs.name": "Filsystemoperationer", - "tracking_field.computercraft.peripheral.name": "Samtal till kringutrustning", - "tracking_field.computercraft.server_time.name": "Serveraktivitetstid", - "tracking_field.computercraft.server_count.name": "Antal serveruppgifter", - "tracking_field.computercraft.max.name": "Max tid", - "tracking_field.computercraft.average.name": "Genomsnittlig tid", - "tracking_field.computercraft.total.name": "Total tid", - "tracking_field.computercraft.tasks.name": "Uppgifter", - "argument.computercraft.argument_expected": "Argument förväntas", - "argument.computercraft.tracking_field.no_field": "Okänt fält '%s'", - "argument.computercraft.computer.many_matching": "Flera datorer matchar '%s' (%s träffar)", - "argument.computercraft.computer.no_matching": "Inga datorer matchar '%s'", - "commands.computercraft.generic.additional_rows": "%d ytterligare rader…", - "commands.computercraft.generic.exception": "Ohanterat felfall (%s)", - "commands.computercraft.generic.no": "N", - "commands.computercraft.generic.yes": "J", - "commands.computercraft.generic.position": "%s, %s, %s", - "commands.computercraft.generic.no_position": "", - "commands.computercraft.queue.desc": "Skicka ett computer_command event till en kommandodator, skicka vidare ytterligare argument. Detta är mestadels utformat för kartmarkörer som fungerar som en mer datorvänlig version av /trigger. Alla spelare kan köra kommandot, vilket sannolikt skulle göras genom en textkomponents klick-event.", - "commands.computercraft.queue.synopsis": "Skicka ett computer_command event till en kommandodator", - "commands.computercraft.reload.done": "Konfiguration omladdad", - "commands.computercraft.reload.desc": "Ladda om ComputerCrafts konfigurationsfil", - "commands.computercraft.reload.synopsis": "Ladda om ComputerCrafts konfigurationsfil", - "commands.computercraft.track.dump.computer": "Dator", - "commands.computercraft.track.dump.no_timings": "Inga tidtagningar tillgängliga", - "commands.computercraft.track.dump.desc": "Dumpa de senaste resultaten av datorspårning.", - "commands.computercraft.track.dump.synopsis": "Dumpa de senaste spårningsresultaten", - "commands.computercraft.track.stop.not_enabled": "Spårar för tillfället inga datorer", - "commands.computercraft.track.stop.action": "Klicka för att stoppa spårning", - "commands.computercraft.track.stop.desc": "Stoppa spårning av alla datorers körtider och eventräkningar", - "commands.computercraft.track.stop.synopsis": "Stoppa spårning för alla datorer", - "commands.computercraft.track.start.stop": "Kör %s för att stoppa spårning och visa resultaten", - "commands.computercraft.track.start.desc": "Börja spåra alla dators körtider och eventräkningar. Detta kommer återställa resultaten från tidigare körningar.", - "commands.computercraft.track.start.synopsis": "Starta spårning för alla datorer", - "commands.computercraft.track.desc": "Spåra hur länge datorer exekverar, och även hur många event de hanterar. Detta presenterar information på liknande sätt som /forge track och kan vara användbart för att undersöka lagg.", - "commands.computercraft.track.synopsis": "Spåra körningstider för denna dator.", - "commands.computercraft.view.not_player": "Kan inte öppna terminalen för en ickespelare", - "commands.computercraft.view.action": "Titta på denna dator", - "commands.computercraft.view.desc": "Öppna datorns terminal för att möjligöra fjärrstyrning. Detta ger inte tillgång till turtlens inventory. Du kan ange en dators instans-id (t.ex. 123) eller dator-id (t.ex. #123).", - "commands.computercraft.view.synopsis": "Titta på datorns terminal.", - "commands.computercraft.tp.not_there": "Kan inte hitta datorn i världen", - "commands.computercraft.tp.not_player": "Kan inte öppna terminalen för en ickespelare", - "commands.computercraft.tp.action": "Teleportera till den här datorn", - "commands.computercraft.tp.desc": "Teleportera till datorns position. Du kan ange en dators instans-id (t.ex. 123), dator-id (t.ex. #123) eller etikett (t.ex. \"@Min dator\").", - "commands.computercraft.tp.synopsis": "Teleportera till en specifik dator.", - "commands.computercraft.turn_on.done": "Startade %s/%s datorer", - "commands.computercraft.turn_on.desc": "Starta de listade datorerna eller alla om ingen anges. Du kan ange en dators instans-id (t.ex. 123), dator-id (t.ex. #123) eller etikett (t.ex. \"@Min dator\").", - "commands.computercraft.turn_on.synopsis": "Starta på datorer på distans.", - "commands.computercraft.shutdown.done": "Stängde av %s/%s datorer", - "commands.computercraft.dump.desc": "Visa status för alla datorer eller specifik information för en dator. Du kan ange en dators instans-id (t.ex. 123), dator-id (t.ex. #123) eller etikett (t.ex. \"@Min dator\").", - "commands.computercraft.shutdown.desc": "Stäng av de listade datorerna eller alla om ingen anges. Du kan ange en dators instans-id (t.ex. 123), dator-id (t.ex. #123) eller etikett (t.ex. \"@Min dator\").", - "commands.computercraft.shutdown.synopsis": "Stäng av datorer på distans.", - "commands.computercraft.dump.action": "Visa mer information om den här datorn", - "commands.computercraft.dump.synopsis": "Visa status för datorer.", - "commands.computercraft.help.no_command": "Inget sådant kommando '%s'", - "commands.computercraft.help.no_children": "%s har inget underkommando", - "commands.computercraft.help.desc": "Visa detta hjälpmeddelande", - "commands.computercraft.help.synopsis": "Tillhandahåll hjälp för ett specifikt kommando", - "commands.computercraft.desc": "/computercraft kommandot tillhandahåller olika debugging- och administrationsverktyg för att kontrollera och interagera med datorer.", "commands.computercraft.synopsis": "Olika kommandon för att kontrollera datorer.", - "itemGroup.computercraft": "ComputerCraft" + "commands.computercraft.desc": "/computercraft kommandot tillhandahåller olika debugging- och administrationsverktyg för att kontrollera och interagera med datorer.", + "commands.computercraft.help.synopsis": "Tillhandahåll hjälp för ett specifikt kommando", + "commands.computercraft.help.desc": "Visa detta hjälpmeddelande", + "commands.computercraft.help.no_children": "%s har inget underkommando", + "commands.computercraft.help.no_command": "Inget sådant kommando '%s'", + "commands.computercraft.dump.synopsis": "Visa status för datorer.", + "commands.computercraft.dump.desc": "Visa status för alla datorer eller specifik information för en dator. Du kan ange en dators instans-id (t.ex. 123), dator-id (t.ex. #123) eller etikett (t.ex. \"@Min dator\").", + "commands.computercraft.dump.action": "Visa mer information om den här datorn", + "commands.computercraft.shutdown.synopsis": "Stäng av datorer på distans.", + "commands.computercraft.shutdown.desc": "Stäng av de listade datorerna eller alla om ingen anges. Du kan ange en dators instans-id (t.ex. 123), dator-id (t.ex. #123) eller etikett (t.ex. \"@Min dator\").", + "commands.computercraft.shutdown.done": "Stängde av %s/%s datorer", + "commands.computercraft.turn_on.synopsis": "Starta på datorer på distans.", + "commands.computercraft.turn_on.desc": "Starta de listade datorerna eller alla om ingen anges. Du kan ange en dators instans-id (t.ex. 123), dator-id (t.ex. #123) eller etikett (t.ex. \"@Min dator\").", + "commands.computercraft.turn_on.done": "Startade %s/%s datorer", + "commands.computercraft.tp.synopsis": "Teleportera till en specifik dator.", + "commands.computercraft.tp.desc": "Teleportera till datorns position. Du kan ange en dators instans-id (t.ex. 123), dator-id (t.ex. #123) eller etikett (t.ex. \"@Min dator\").", + "commands.computercraft.tp.action": "Teleportera till den här datorn", + "commands.computercraft.tp.not_player": "Kan inte öppna terminalen för en ickespelare", + "commands.computercraft.tp.not_there": "Kan inte hitta datorn i världen", + "commands.computercraft.view.synopsis": "Titta på datorns terminal.", + "commands.computercraft.view.desc": "Öppna datorns terminal för att möjligöra fjärrstyrning. Detta ger inte tillgång till turtlens inventory. Du kan ange en dators instans-id (t.ex. 123) eller dator-id (t.ex. #123).", + "commands.computercraft.view.action": "Titta på denna dator", + "commands.computercraft.view.not_player": "Kan inte öppna terminalen för en ickespelare", + "commands.computercraft.track.synopsis": "Spåra körningstider för denna dator.", + "commands.computercraft.track.desc": "Spåra hur länge datorer exekverar, och även hur många event de hanterar. Detta presenterar information på liknande sätt som /forge track och kan vara användbart för att undersöka lagg.", + "commands.computercraft.track.start.synopsis": "Starta spårning för alla datorer", + "commands.computercraft.track.start.desc": "Börja spåra alla dators körtider och eventräkningar. Detta kommer återställa resultaten från tidigare körningar.", + "commands.computercraft.track.start.stop": "Kör %s för att stoppa spårning och visa resultaten", + "commands.computercraft.track.stop.synopsis": "Stoppa spårning för alla datorer", + "commands.computercraft.track.stop.desc": "Stoppa spårning av alla datorers körtider och eventräkningar", + "commands.computercraft.track.stop.action": "Klicka för att stoppa spårning", + "commands.computercraft.track.stop.not_enabled": "Spårar för tillfället inga datorer", + "commands.computercraft.track.dump.synopsis": "Dumpa de senaste spårningsresultaten", + "commands.computercraft.track.dump.desc": "Dumpa de senaste resultaten av datorspårning.", + "commands.computercraft.track.dump.no_timings": "Inga tidtagningar tillgängliga", + "commands.computercraft.track.dump.computer": "Dator", + "commands.computercraft.reload.synopsis": "Ladda om ComputerCrafts konfigurationsfil", + "commands.computercraft.reload.desc": "Ladda om ComputerCrafts konfigurationsfil", + "commands.computercraft.reload.done": "Konfiguration omladdad", + "commands.computercraft.queue.synopsis": "Skicka ett computer_command event till en kommandodator", + "commands.computercraft.queue.desc": "Skicka ett computer_command event till en kommandodator, skicka vidare ytterligare argument. Detta är mestadels utformat för kartmarkörer som fungerar som en mer datorvänlig version av /trigger. Alla spelare kan köra kommandot, vilket sannolikt skulle göras genom en textkomponents klick-event.", + "commands.computercraft.generic.no_position": "", + "commands.computercraft.generic.position": "%s, %s, %s", + "commands.computercraft.generic.yes": "J", + "commands.computercraft.generic.no": "N", + "commands.computercraft.generic.exception": "Ohanterat felfall (%s)", + "commands.computercraft.generic.additional_rows": "%d ytterligare rader…", + "argument.computercraft.computer.no_matching": "Inga datorer matchar '%s'", + "argument.computercraft.computer.many_matching": "Flera datorer matchar '%s' (%s träffar)", + "argument.computercraft.tracking_field.no_field": "Okänt fält '%s'", + "argument.computercraft.argument_expected": "Argument förväntas", + "tracking_field.computercraft.tasks.name": "Uppgifter", + "tracking_field.computercraft.total.name": "Total tid", + "tracking_field.computercraft.average.name": "Genomsnittlig tid", + "tracking_field.computercraft.max.name": "Max tid", + "tracking_field.computercraft.server_count.name": "Antal serveruppgifter", + "tracking_field.computercraft.server_time.name": "Serveraktivitetstid", + "tracking_field.computercraft.peripheral.name": "Samtal till kringutrustning", + "tracking_field.computercraft.fs.name": "Filsystemoperationer", + "tracking_field.computercraft.turtle.name": "Turtle-operationer", + "tracking_field.computercraft.http.name": "HTTP-förfrågningar", + "tracking_field.computercraft.http_upload.name": "HTTP-uppladdning", + "tracking_field.computercraft.http_download.name": "HTTP-nedladdning", + "tracking_field.computercraft.websocket_incoming.name": "Websocket ingående", + "tracking_field.computercraft.websocket_outgoing.name": "Websocket utgående", + "tracking_field.computercraft.coroutines_created.name": "Coroutines skapade", + "tracking_field.computercraft.coroutines_dead.name": "Coroutines borttagna", + "gui.computercraft.tooltip.copy": "Kopiera till urklipp", + "gui.computercraft.tooltip.computer_id": "Dator-ID: %s", + "gui.computercraft.tooltip.disk_id": "Diskett-ID: %s" } diff --git a/src/main/resources/assets/computercraft/lang/vi_vn.json b/src/main/resources/assets/computercraft/lang/vi_vn.json new file mode 100644 index 000000000..c7b1de3c8 --- /dev/null +++ b/src/main/resources/assets/computercraft/lang/vi_vn.json @@ -0,0 +1,48 @@ +{ + "itemGroup.computercraft.main": "ComputerCraft", + "block.computercraft.computer_normal": "Máy tính", + "block.computercraft.computer_advanced": "Máy tính tiên tiến", + "block.computercraft.computer_command": "Máy tính điều khiển", + "block.computercraft.disk_drive": "Ỗ đĩa", + "block.computercraft.printer": "Máy in", + "block.computercraft.speaker": "Loa", + "block.computercraft.monitor_normal": "Màn hình", + "block.computercraft.monitor_advanced": "Màn hình tiên tiếng", + "block.computercraft.wireless_modem_normal": "Modem không dây", + "block.computercraft.wireless_modem_advanced": "Modem Ender", + "block.computercraft.wired_modem": "Modem có dây", + "block.computercraft.cable": "Dây cáp mạng", + "block.computercraft.wired_modem_full": "Modem có dây", + "block.computercraft.turtle_normal": "Rùa", + "block.computercraft.turtle_normal.upgraded": "Rùa %s", + "block.computercraft.turtle_normal.upgraded_twice": "Rùa %s %s", + "block.computercraft.turtle_advanced": "Rùa tiên tiến", + "block.computercraft.turtle_advanced.upgraded": "Rùa tiên tiến %s", + "block.computercraft.turtle_advanced.upgraded_twice": "Rùa tiên tiến %s %s", + "item.computercraft.disk": "Đĩa mềm", + "item.computercraft.treasure_disk": "Đĩa mềm", + "item.computercraft.printed_page": "Trang in", + "item.computercraft.printed_book": "Sách in", + "item.computercraft.pocket_computer_normal": "Máy tính bỏ túi", + "item.computercraft.pocket_computer_normal.upgraded": "Máy tính bỏ túi %s", + "item.computercraft.pocket_computer_advanced": "Máy tính bỏ túi tiên tiến", + "item.computercraft.pocket_computer_advanced.upgraded": "Máy tính bỏ túi tiên tiến %s", + "upgrade.minecraft.diamond_shovel.adjective": "Đào", + "upgrade.minecraft.diamond_pickaxe.adjective": "Khai thác", + "upgrade.minecraft.diamond_axe.adjective": "Đốn", + "upgrade.minecraft.diamond_hoe.adjective": "Trồng trọt", + "upgrade.minecraft.crafting_table.adjective": "Chế tạo", + "upgrade.computercraft.wireless_modem_normal.adjective": "Không dây", + "upgrade.computercraft.wireless_modem_advanced.adjective": "Ender", + "upgrade.computercraft.speaker.adjective": "Ồn ào", + "tracking_field.computercraft.http.name": "Yêu cầu HTTP", + "tracking_field.computercraft.http_upload.name": "HTTP tải lên", + "tracking_field.computercraft.http_download.name": "HTTP tải xuống", + "tracking_field.computercraft.websocket_incoming.name": "Websocket đến", + "tracking_field.computercraft.websocket_outgoing.name": "Websocket đi", + "tracking_field.computercraft.coroutines_created.name": "Coroutine đã tạo", + "tracking_field.computercraft.coroutines_dead.name": "Coroutine bỏ đi", + "gui.computercraft.tooltip.copy": "Sao chép vào clipboard", + "gui.computercraft.tooltip.computer_id": "ID của máy tính: %s", + "gui.computercraft.tooltip.disk_id": "ID của đĩa: %s" +} diff --git a/src/main/resources/assets/computercraft/lang/zh_cn.json b/src/main/resources/assets/computercraft/lang/zh_cn.json index 83b373f69..679569222 100644 --- a/src/main/resources/assets/computercraft/lang/zh_cn.json +++ b/src/main/resources/assets/computercraft/lang/zh_cn.json @@ -1,5 +1,5 @@ { - "itemGroup.computercraft": "ComputerCraft", + "itemGroup.computercraft.main": "ComputerCraft", "block.computercraft.computer_normal": "计算机", "block.computercraft.computer_advanced": "高级计算机", "block.computercraft.computer_command": "命令电脑", diff --git a/src/main/resources/assets/computercraft/textures/gui/blank_screen.png b/src/main/resources/assets/computercraft/textures/gui/blank_screen.png new file mode 100644 index 000000000..6b3288e28 Binary files /dev/null and b/src/main/resources/assets/computercraft/textures/gui/blank_screen.png differ diff --git a/src/main/resources/assets/computercraft/textures/gui/buttons.png b/src/main/resources/assets/computercraft/textures/gui/buttons.png new file mode 100644 index 000000000..69ef550ee Binary files /dev/null and b/src/main/resources/assets/computercraft/textures/gui/buttons.png differ diff --git a/src/main/resources/assets/computercraft/textures/gui/corners_advanced.png b/src/main/resources/assets/computercraft/textures/gui/corners_advanced.png index f4e8032a1..4da80a9bc 100644 Binary files a/src/main/resources/assets/computercraft/textures/gui/corners_advanced.png and b/src/main/resources/assets/computercraft/textures/gui/corners_advanced.png differ diff --git a/src/main/resources/assets/computercraft/textures/gui/corners_command.png b/src/main/resources/assets/computercraft/textures/gui/corners_command.png index 1bd3c311a..0a96d54ff 100644 Binary files a/src/main/resources/assets/computercraft/textures/gui/corners_command.png and b/src/main/resources/assets/computercraft/textures/gui/corners_command.png differ diff --git a/src/main/resources/assets/computercraft/textures/gui/corners_normal.png b/src/main/resources/assets/computercraft/textures/gui/corners_normal.png index 8b1e9f676..3627eaf3e 100644 Binary files a/src/main/resources/assets/computercraft/textures/gui/corners_normal.png and b/src/main/resources/assets/computercraft/textures/gui/corners_normal.png differ diff --git a/src/main/resources/assets/computercraft/textures/gui/term_font.png b/src/main/resources/assets/computercraft/textures/gui/term_font.png index 7bf23be2e..975fcc05b 100644 Binary files a/src/main/resources/assets/computercraft/textures/gui/term_font.png and b/src/main/resources/assets/computercraft/textures/gui/term_font.png differ diff --git a/src/main/resources/assets/computercraft/shaders/monitor.frag b/src/main/resources/assets/minecraft/shaders/core/monitor.frag similarity index 100% rename from src/main/resources/assets/computercraft/shaders/monitor.frag rename to src/main/resources/assets/minecraft/shaders/core/monitor.frag diff --git a/src/main/resources/assets/computercraft/shaders/monitor.vert b/src/main/resources/assets/minecraft/shaders/core/monitor.vert similarity index 100% rename from src/main/resources/assets/computercraft/shaders/monitor.vert rename to src/main/resources/assets/minecraft/shaders/core/monitor.vert diff --git a/src/main/resources/assets/minecraft/shaders/core/monitor_tbo.fsh b/src/main/resources/assets/minecraft/shaders/core/monitor_tbo.fsh new file mode 100644 index 000000000..f89635ca5 --- /dev/null +++ b/src/main/resources/assets/minecraft/shaders/core/monitor_tbo.fsh @@ -0,0 +1,40 @@ +#version 150 + +#define FONT_WIDTH 6.0 +#define FONT_HEIGHT 9.0 + +uniform sampler2D Sampler0; // Font +uniform int Width; +uniform int Height; +uniform usamplerBuffer Tbo; +uniform vec3 Palette[16]; + +in vec2 f_pos; + +out vec4 colour; + +vec2 texture_corner(int index) { + float x = 1.0 + float(index % 16) * (FONT_WIDTH + 2.0); + float y = 1.0 + float(index / 16) * (FONT_HEIGHT + 2.0); + return vec2(x, y); +} + +void main() { + vec2 term_pos = vec2(f_pos.x / FONT_WIDTH, f_pos.y / FONT_HEIGHT); + vec2 corner = floor(term_pos); + + ivec2 cell = ivec2(corner); + int index = 3 * (clamp(cell.x, 0, Width - 1) + clamp(cell.y, 0, Height - 1) * Width); + + // 1 if 0 <= x, y < Width, Height, 0 otherwise + vec2 outside = step(vec2(0.0, 0.0), vec2(cell)) * step(vec2(cell), vec2(float(Width) - 1.0, float(Height) - 1.0)); + float mult = outside.x * outside.y; + + int character = int(texelFetch(Tbo, index).r); + int fg = int(texelFetch(Tbo, index + 1).r); + int bg = int(texelFetch(Tbo, index + 2).r); + + vec2 pos = (term_pos - corner) * vec2(FONT_WIDTH, FONT_HEIGHT); + vec4 img = texture(Sampler0, (texture_corner(character) + pos) / 256.0); + colour = vec4(mix(Palette[bg], img.rgb * Palette[fg], img.a * mult), 1.0); +} diff --git a/src/main/resources/assets/minecraft/shaders/core/monitor_tbo.json b/src/main/resources/assets/minecraft/shaders/core/monitor_tbo.json new file mode 100644 index 000000000..9196160ee --- /dev/null +++ b/src/main/resources/assets/minecraft/shaders/core/monitor_tbo.json @@ -0,0 +1,13 @@ +{ + "vertex": "monitor_tbo", + "fragment": "monitor_tbo", + "attributes": [ "Position" ], + "samplers": [ { "name": "Sampler0" } ], + "uniforms": [ + { "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, + { "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, + { "name": "Width", "type": "int", "count": 1, "values": [ 1 ] }, + { "name": "Height", "type": "int", "count": 1, "values": [ 1 ] }, + { "name": "Tbo", "type": "int", "count": 1, "values": [ 3 ] } + ] +} diff --git a/src/main/resources/assets/minecraft/shaders/core/monitor_tbo.vsh b/src/main/resources/assets/minecraft/shaders/core/monitor_tbo.vsh new file mode 100644 index 000000000..8b673710b --- /dev/null +++ b/src/main/resources/assets/minecraft/shaders/core/monitor_tbo.vsh @@ -0,0 +1,14 @@ +#version 150 + +in vec3 Position; +in vec2 UV0; + +uniform mat4 ModelViewMat; +uniform mat4 ProjMat; + +out vec2 f_pos; + +void main() { + gl_Position = ProjMat * ModelViewMat * vec4(Position, 1); + f_pos = UV0; +} diff --git a/src/main/resources/assets/minecraft/shaders/core/terminal.json b/src/main/resources/assets/minecraft/shaders/core/terminal.json new file mode 100644 index 000000000..2cd53c182 --- /dev/null +++ b/src/main/resources/assets/minecraft/shaders/core/terminal.json @@ -0,0 +1,11 @@ +{ + "vertex": "position_color_tex", + "fragment": "position_color_tex", + "attributes": [ "Position", "Color", "UV0" ], + "samplers": [ { "name": "Sampler0" } ], + "uniforms": [ + { "name": "ModelViewMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, + { "name": "ProjMat", "type": "matrix4x4", "count": 16, "values": [ 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 ] }, + { "name": "ColorModulator", "type": "float", "count": 4, "values": [ 1.0, 1.0, 1.0, 1.0 ] } + ] +} diff --git a/src/main/resources/cc.accesswidener b/src/main/resources/cc.accesswidener new file mode 100644 index 000000000..cb9b4b774 --- /dev/null +++ b/src/main/resources/cc.accesswidener @@ -0,0 +1,8 @@ +accessWidener v1 named + +accessible class net/minecraft/client/render/RenderLayer$MultiPhase +accessible class net/minecraft/client/render/RenderLayer$MultiPhaseParameters +accessible method net/minecraft/client/render/RenderLayer of (Ljava/lang/String;Lnet/minecraft/client/render/VertexFormat;Lnet/minecraft/client/render/VertexFormat$DrawMode;IZZLnet/minecraft/client/render/RenderLayer$MultiPhaseParameters;)Lnet/minecraft/client/render/RenderLayer$MultiPhase; + +accessible class net/minecraft/client/render/RenderPhase$Shader +accessible class net/minecraft/client/render/RenderPhase$Texture diff --git a/src/main/resources/computercraft.mixins.json b/src/main/resources/computercraft.mixins.json index cdc81c628..482f7cd98 100644 --- a/src/main/resources/computercraft.mixins.json +++ b/src/main/resources/computercraft.mixins.json @@ -1,7 +1,7 @@ { "required": true, "package": "dan200.computercraft.fabric.mixin", - "compatibilityLevel": "JAVA_8", + "compatibilityLevel": "JAVA_16", "mixins": [ "MinecraftServerAccess", "MixinBlock", @@ -9,20 +9,18 @@ "MixinServerWorld", "MixinWorld", "SignBlockEntityAccess", - "SoundEventAccess", "WorldSavePathAccess", "MixinServerPlayerInteractionManager", - "MusicDiscItemAccessor" + "MixinMatrix4f" ], "client": [ - "AffineTransformationAccess", - "BakedQuadAccess", "ChatHudAccess", "HeldItemRendererAccess", "MixinHeldItemRenderer", "MixinItemFrameEntityRenderer", "MixinMinecraftClient", "MixinScreen", + "MixinGameRenderer", "MixinWorldRenderer" ], "server": [ diff --git a/src/main/resources/data/computercraft/lua/bios.lua b/src/main/resources/data/computercraft/lua/bios.lua index 895418c39..d31e20335 100644 --- a/src/main/resources/data/computercraft/lua/bios.lua +++ b/src/main/resources/data/computercraft/lua/bios.lua @@ -496,7 +496,7 @@ function os.run(_tEnv, _sPath, ...) -- throws an error with this protection enabled. Thus we set it here first. tEnv._ENV = tEnv getmetatable(tEnv).__newindex = function(_, name) - error("Attempt to create global " .. tostring(name), 2) + error("Attempt to create global " .. tostring(name), 2) end end @@ -681,6 +681,7 @@ if http then local nativeCheckURL = http.checkURL http.checkURLAsync = nativeCheckURL http.checkURL = function(_url) + expect(1, _url, "string") local ok, err = nativeCheckURL(_url) if not ok then return ok, err end diff --git a/src/main/resources/data/computercraft/lua/rom/apis/colors.lua b/src/main/resources/data/computercraft/lua/rom/apis/colors.lua index 1add04d6a..7b607757d 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/colors.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/colors.lua @@ -204,6 +204,7 @@ black = 0x8000 -- -- @tparam number ... The colors to combine. -- @treturn number The union of the color sets given in `...` +-- @since 1.2 -- @usage -- ```lua -- colors.combine(colors.white, colors.magenta, colours.lightBlue) @@ -229,6 +230,7 @@ end -- @tparam number colors The color from which to subtract. -- @tparam number ... The colors to subtract. -- @treturn number The resulting color. +-- @since 1.2 -- @usage -- ```lua -- colours.subtract(colours.lime, colours.orange, colours.white) @@ -251,6 +253,7 @@ end -- @tparam number colors A color, or color set -- @tparam number color A color or set of colors that `colors` should contain. -- @treturn boolean If `colors` contains all colors within `color`. +-- @since 1.2 -- @usage -- ```lua -- colors.test(colors.combine(colors.white, colors.magenta, colours.lightBlue), colors.lightBlue) @@ -270,15 +273,16 @@ end -- @treturn number The combined hexadecimal colour. -- @usage -- ```lua --- colors.unpackRGB(0.7, 0.2, 0.6) +-- colors.packRGB(0.7, 0.2, 0.6) -- -- => 0xb23399 -- ``` +-- @since 1.81.0 function packRGB(r, g, b) expect(1, r, "number") expect(2, g, "number") expect(3, b, "number") return - bit32.band(r * 255, 0xFF) * 2 ^ 16 + + bit32.band(r * 255, 0xFF) * 2 ^ 16 + bit32.band(g * 255, 0xFF) * 2 ^ 8 + bit32.band(b * 255, 0xFF) end @@ -295,12 +299,13 @@ end -- -- => 0.7, 0.2, 0.6 -- ``` -- @see colors.packRGB +-- @since 1.81.0 function unpackRGB(rgb) expect(1, rgb, "number") return - bit32.band(bit32.rshift(rgb, 16), 0xFF) / 255, - bit32.band(bit32.rshift(rgb, 8), 0xFF) / 255, - bit32.band(rgb, 0xFF) / 255 + bit32.band(bit32.rshift(rgb, 16), 0xFF) / 255, + bit32.band(bit32.rshift(rgb, 8), 0xFF) / 255, + bit32.band(rgb, 0xFF) / 255 end --- Either calls @{colors.packRGB} or @{colors.unpackRGB}, depending on how many @@ -317,14 +322,16 @@ end -- @deprecated Use @{packRGB} or @{unpackRGB} directly. -- @usage -- ```lua --- colors.unpackRGB(0xb23399) +-- colors.rgb8(0xb23399) -- -- => 0.7, 0.2, 0.6 -- ``` -- @usage -- ```lua --- colors.unpackRGB(0.7, 0.2, 0.6) +-- colors.rgb8(0.7, 0.2, 0.6) -- -- => 0xb23399 -- ``` +-- @since 1.80pr1 +-- @changed 1.81.0 Deprecated in favor of colors.(un)packRGB. function rgb8(r, g, b) if g == nil and b == nil then return unpackRGB(r) @@ -345,6 +352,7 @@ end -- -- @tparam number color The color to convert. -- @treturn string The blit hex code of the color. +-- @since 1.94.0 function toBlit(color) expect(1, color, "number") return color_hex_lookup[color] or diff --git a/src/main/resources/data/computercraft/lua/rom/apis/colours.lua b/src/main/resources/data/computercraft/lua/rom/apis/colours.lua index 74f048df7..287d73de9 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/colours.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/colours.lua @@ -2,10 +2,11 @@ -- -- @see colors -- @module colours +-- @since 1.2 local colours = _ENV for k, v in pairs(colors) do - colours[k] = v + colours[k] = v end --- Grey. Written as `7` in paint files and @{term.blit}, has a default diff --git a/src/main/resources/data/computercraft/lua/rom/apis/disk.lua b/src/main/resources/data/computercraft/lua/rom/apis/disk.lua index ca5ac21d2..00d691ccc 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/disk.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/disk.lua @@ -10,6 +10,7 @@ -- like a disk. -- -- @module disk +-- @since 1.2 local function isDrive(name) if type(name) ~= "string" then @@ -163,6 +164,7 @@ end -- -- @tparam string name The name of the disk drive. -- @treturn string|nil The disk ID, or `nil` if the drive does not contain a floppy disk. +-- @since 1.4 function getID(name) if isDrive(name) then return peripheral.call(name, "getDiskID") diff --git a/src/main/resources/data/computercraft/lua/rom/apis/gps.lua b/src/main/resources/data/computercraft/lua/rom/apis/gps.lua index 8cbc63a78..f653166ca 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/gps.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/gps.lua @@ -21,6 +21,7 @@ -- [1]: http://www.computercraft.info/forums2/index.php?/topic/3088-how-to-guide-gps-global-position-system/ -- -- @module gps +-- @since 1.31 local expect = dofile("rom/modules/main/cc/expect.lua").expect diff --git a/src/main/resources/data/computercraft/lua/rom/apis/help.lua b/src/main/resources/data/computercraft/lua/rom/apis/help.lua index 43b3d6b17..503555de2 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/help.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/help.lua @@ -1,6 +1,7 @@ --- Provides an API to read help files. -- -- @module help +-- @since 1.2 local expect = dofile("rom/modules/main/cc/expect.lua").expect @@ -27,21 +28,26 @@ function setPath(_sPath) sPath = _sPath end +local extensions = { "", ".md", ".txt" } + --- Returns the location of the help file for the given topic. -- -- @tparam string topic The topic to find -- @treturn string|nil The path to the given topic's help file, or `nil` if it -- cannot be found. -- @usage help.lookup("disk") -function lookup(_sTopic) - expect(1, _sTopic, "string") +-- @changed 1.80pr1 Now supports finding .txt files. +-- @changed 1.97.0 Now supports finding Markdown files. +function lookup(topic) + expect(1, topic, "string") -- Look on the path variable - for sPath in string.gmatch(sPath, "[^:]+") do - sPath = fs.combine(sPath, _sTopic) - if fs.exists(sPath) and not fs.isDir(sPath) then - return sPath - elseif fs.exists(sPath .. ".txt") and not fs.isDir(sPath .. ".txt") then - return sPath .. ".txt" + for path in string.gmatch(sPath, "[^:]+") do + path = fs.combine(path, topic) + for _, extension in ipairs(extensions) do + local file = path .. extension + if fs.exists(file) and not fs.isDir(file) then + return file + end end end @@ -52,6 +58,7 @@ end --- Returns a list of topics that can be looked up and/or displayed. -- -- @treturn table A list of topics in alphabetical order. +-- @usage help.topics() function topics() -- Add index local tItems = { @@ -65,8 +72,11 @@ function topics() for _, sFile in pairs(tList) do if string.sub(sFile, 1, 1) ~= "." then if not fs.isDir(fs.combine(sPath, sFile)) then - if #sFile > 4 and sFile:sub(-4) == ".txt" then - sFile = sFile:sub(1, -5) + for i = 2, #extensions do + local extension = extensions[i] + if #sFile > #extension and sFile:sub(-#extension) == extension then + sFile = sFile:sub(1, -#extension - 1) + end end tItems[sFile] = true end @@ -89,6 +99,7 @@ end -- -- @tparam string prefix The prefix to match -- @treturn table A list of matching topics. +-- @since 1.74 function completeTopic(sText) expect(1, sText, "string") local tTopics = topics() diff --git a/src/main/resources/data/computercraft/lua/rom/apis/io.lua b/src/main/resources/data/computercraft/lua/rom/apis/io.lua index 4898df308..50ee341e0 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/io.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/io.lua @@ -65,6 +65,30 @@ handleMetatable = { return true end, + --[[- Returns an iterator that, each time it is called, returns a new + line from the file. + + This can be used in a for loop to iterate over all lines of a file + + Once the end of the file has been reached, @{nil} will be returned. The file is + *not* automatically closed. + + @param ... The argument to pass to @{Handle:read} for each line. + @treturn function():string|nil The line iterator. + @throws If the file cannot be opened for reading + @since 1.3 + + @see io.lines + @usage Iterate over every line in a file and print it out. + + ```lua + local file = io.open("/rom/help/intro.txt") + for line in file:lines() do + print(line) + end + file:close() + ``` + ]] lines = function(self, ...) if type_of(self) ~= "table" or getmetatable(self) ~= handleMetatable then error("bad argument #1 (FILE expected, got " .. type_of(self) .. ")", 2) @@ -81,6 +105,23 @@ handleMetatable = { end end, + --[[- Reads data from the file, using the specified formats. For each + format provided, the function returns either the data read, or `nil` if + no data could be read. + + The following formats are available: + - `l`: Returns the next line (without a newline on the end). + - `L`: Returns the next line (with a newline on the end). + - `a`: Returns the entire rest of the file. + - ~~`n`: Returns a number~~ (not implemented in CC). + + These formats can be preceded by a `*` to make it compatible with Lua 5.1. + + If no format is provided, `l` is assumed. + + @param ... The formats to use. + @treturn (string|nil)... The data read from the file. + ]] read = function(self, ...) if type_of(self) ~= "table" or getmetatable(self) ~= handleMetatable then error("bad argument #1 (FILE expected, got " .. type_of(self) .. ")", 2) @@ -124,6 +165,23 @@ handleMetatable = { return table.unpack(output, 1, n) end, + --[[- Seeks the file cursor to the specified position, and returns the + new position. + + `whence` controls where the seek operation starts, and is a string that + may be one of these three values: + - `set`: base position is 0 (beginning of the file) + - `cur`: base is current position + - `end`: base is end of file + + The default value of `whence` is `cur`, and the default value of `offset` + is 0. This means that `file:seek()` without arguments returns the current + position without moving. + + @tparam[opt] string whence The place to set the cursor from. + @tparam[opt] number offset The offset from the start to move to. + @treturn number The new location of the file cursor. + ]] seek = function(self, whence, offset) if type_of(self) ~= "table" or getmetatable(self) ~= handleMetatable then error("bad argument #1 (FILE expected, got " .. type_of(self) .. ")", 2) @@ -154,6 +212,7 @@ handleMetatable = { -- @treturn[1] Handle The current file, allowing chained calls. -- @treturn[2] nil If the file could not be written to. -- @treturn[2] string The error message which occurred while writing. + -- @changed 1.81.0 Multiple arguments are now allowed. write = function(self, ...) if type_of(self) ~= "table" or getmetatable(self) ~= handleMetatable then error("bad argument #1 (FILE expected, got " .. type_of(self) .. ")", 2) @@ -173,27 +232,25 @@ handleMetatable = { }, } -local defaultInput = setmetatable({ - _handle = { readLine = _G.read }, -}, handleMetatable) +local function make_file(handle) + return setmetatable({ _handle = handle }, handleMetatable) +end -local defaultOutput = setmetatable({ - _handle = { write = _G.write }, -}, handleMetatable) +local defaultInput = make_file({ readLine = _G.read }) -local defaultError = setmetatable({ - _handle = { - write = function(...) - local oldColour - if term.isColour() then - oldColour = term.getTextColour() - term.setTextColour(colors.red) - end - _G.write(...) - if term.isColour() then term.setTextColour(oldColour) end - end, - }, -}, handleMetatable) +local defaultOutput = make_file({ write = _G.write }) + +local defaultError = make_file({ + write = function(...) + local oldColour + if term.isColour() then + oldColour = term.getTextColour() + term.setTextColour(colors.red) + end + _G.write(...) + if term.isColour() then term.setTextColour(oldColour) end + end, +}) local currentInput = defaultInput local currentOutput = defaultOutput @@ -219,6 +276,7 @@ stderr = defaultError -- -- @see Handle:close -- @see io.output +-- @since 1.55 function close(file) if file == nil then return currentOutput:close() end @@ -232,6 +290,7 @@ end -- -- @see Handle:flush -- @see io.output +-- @since 1.55 function flush() return currentOutput:flush() end @@ -241,6 +300,7 @@ end -- @tparam[opt] Handle|string file The new input file, either as a file path or pre-existing handle. -- @treturn Handle The current input file. -- @throws If the provided filename cannot be opened for reading. +-- @since 1.55 function input(file) if type_of(file) == "string" then local res, err = open(file, "rb") @@ -255,28 +315,33 @@ function input(file) return currentInput end ---- Opens the given file name in read mode and returns an iterator that, --- each time it is called, returns a new line from the file. --- --- This can be used in a for loop to iterate over all lines of a file: --- --- ```lua --- for line in io.lines(filename) do print(line) end --- ``` --- --- Once the end of the file has been reached, @{nil} will be --- returned. The file is automatically closed. --- --- If no file name is given, the @{io.input|current input} will be used --- instead. In this case, the handle is not used. --- --- @tparam[opt] string filename The name of the file to extract lines from --- @param ... The argument to pass to @{Handle:read} for each line. --- @treturn function():string|nil The line iterator. --- @throws If the file cannot be opened for reading --- --- @see Handle:lines --- @see io.input +--[[- Opens the given file name in read mode and returns an iterator that, +each time it is called, returns a new line from the file. + +This can be used in a for loop to iterate over all lines of a file + +Once the end of the file has been reached, @{nil} will be returned. The file is +automatically closed. + +If no file name is given, the @{io.input|current input} will be used instead. +In this case, the handle is not used. + +@tparam[opt] string filename The name of the file to extract lines from +@param ... The argument to pass to @{Handle:read} for each line. +@treturn function():string|nil The line iterator. +@throws If the file cannot be opened for reading + +@see Handle:lines +@see io.input +@since 1.55 +@usage Iterate over every line in a file and print it out. + +```lua +for line in io.lines("/rom/help/intro.txt") do + print(line) +end +``` +]] function lines(filename, ...) expect(1, filename, "string", "nil") if filename then @@ -316,7 +381,7 @@ function open(filename, mode) local file, err = fs.open(filename, sMode) if not file then return nil, err end - return setmetatable({ _handle = file }, handleMetatable) + return make_file(file) end --- Get or set the current output file. @@ -324,6 +389,7 @@ end -- @tparam[opt] Handle|string file The new output file, either as a file path or pre-existing handle. -- @treturn Handle The current output file. -- @throws If the provided filename cannot be opened for writing. +-- @since 1.55 function output(file) if type_of(file) == "string" then local res, err = open(file, "wb") @@ -372,6 +438,7 @@ end -- documentation} there for full details. -- -- @tparam string ... The strings to write +-- @changed 1.81.0 Multiple arguments are now allowed. function write(...) return currentOutput:write(...) end diff --git a/src/main/resources/data/computercraft/lua/rom/apis/keys.lua b/src/main/resources/data/computercraft/lua/rom/apis/keys.lua index 206646c21..aeac84b7a 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/keys.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/keys.lua @@ -6,6 +6,7 @@ -- the underlying numerical values. -- -- @module keys +-- @since 1.4 local expect = dofile("rom/modules/main/cc/expect.lua").expect @@ -76,7 +77,7 @@ tKeys[269] = 'end' tKeys[280] = 'capsLock' tKeys[281] = 'scrollLock' tKeys[282] = 'numLock' --- tKeys[283] = 'printScreen' +tKeys[283] = 'printScreen' tKeys[284] = 'pause' tKeys[290] = 'f1' tKeys[291] = 'f2' @@ -115,7 +116,7 @@ tKeys[328] = 'numPad8' tKeys[329] = 'numPad9' tKeys[330] = 'numPadDecimal' tKeys[331] = 'numPadDivide' --- tKeys[332] = 'numPadMultiply' +tKeys[332] = 'numPadMultiply' tKeys[333] = 'numPadSubtract' tKeys[334] = 'numPadAdd' tKeys[335] = 'numPadEnter' @@ -123,12 +124,12 @@ tKeys[336] = 'numPadEqual' tKeys[340] = 'leftShift' tKeys[341] = 'leftCtrl' tKeys[342] = 'leftAlt' --- tKeys[343] = 'leftSuper' +tKeys[343] = 'leftSuper' tKeys[344] = 'rightShift' tKeys[345] = 'rightCtrl' tKeys[346] = 'rightAlt' -- tKeys[347] = 'rightSuper' --- tKeys[348] = 'menu' +tKeys[348] = 'menu' local keys = _ENV for nKey, sKey in pairs(tKeys) do diff --git a/src/main/resources/data/computercraft/lua/rom/apis/paintutils.lua b/src/main/resources/data/computercraft/lua/rom/apis/paintutils.lua index f6f1efaac..c3a6ce314 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/paintutils.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/paintutils.lua @@ -2,6 +2,7 @@ -- image files. You can use the `colors` API for easier color manipulation. -- -- @module paintutils +-- @since 1.45 local expect = dofile("rom/modules/main/cc/expect.lua").expect @@ -47,6 +48,7 @@ end -- @tparam string image The string containing the raw-image data. -- @treturn table The parsed image data, suitable for use with -- @{paintutils.drawImage}. +-- @since 1.80pr1 function parseImage(image) expect(1, image, "string") local tImage = {} @@ -276,7 +278,7 @@ end -- -- @tparam table image The parsed image data. -- @tparam number xPos The x position to start drawing at. --- @tparam number xPos The y position to start drawing at. +-- @tparam number yPos The y position to start drawing at. function drawImage(image, xPos, yPos) expect(1, image, "table") expect(2, xPos, "number") diff --git a/src/main/resources/data/computercraft/lua/rom/apis/parallel.lua b/src/main/resources/data/computercraft/lua/rom/apis/parallel.lua index e826ed28e..78e5abdd8 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/parallel.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/parallel.lua @@ -13,6 +13,7 @@ etc) can safely be used in one without affecting the event queue accessed by the other. @module parallel +@since 1.2 ]] local function create(...) @@ -32,6 +33,7 @@ end local function runUntilLimit(_routines, _limit) local count = #_routines + if count < 1 then return 0 end local living = count local tFilters = {} diff --git a/src/main/resources/data/computercraft/lua/rom/apis/peripheral.lua b/src/main/resources/data/computercraft/lua/rom/apis/peripheral.lua index 0f3478046..5266678e7 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/peripheral.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/peripheral.lua @@ -11,6 +11,8 @@ -- Peripheral functions are called *methods*, a term borrowed from Java. -- -- @module peripheral +-- @since 1.3 +-- @changed 1.51 Add support for wired modems. local expect = dofile("rom/modules/main/cc/expect.lua").expect @@ -24,6 +26,7 @@ local sides = rs.getSides() -- Modem, then it'll be reported according to its name on the wired network. -- -- @treturn { string... } A list of the names of all attached peripherals. +-- @since 1.51 function getNames() local results = {} for n = 1, #sides do @@ -69,6 +72,7 @@ end -- @tparam string|table peripheral The name of the peripheral to find, or a -- wrapped peripheral instance. -- @treturn string|nil The peripheral's type, or `nil` if it is not present. +-- @changed 1.88.0 Accepts a wrapped peripheral as an argument. function getType(peripheral) expect(1, peripheral, "string", "table") if type(peripheral) == "string" then -- Peripheral name passed @@ -118,6 +122,7 @@ end -- -- @tparam table peripheral The peripheral to get the name of. -- @treturn string The name of the given peripheral. +-- @since 1.88.0 function getName(peripheral) expect(1, peripheral, "table") local mt = getmetatable(peripheral) @@ -200,9 +205,16 @@ and returns if it should be included in the result. monitor.write("Hello") end +@usage Find all wireless modems connected to this computer. + + local modems = { peripheral.find("modem", function(name, modem) + return modem.isWireless() -- Check this modem is wireless. + end) } + @usage This abuses the `filter` argument to call @{rednet.open} on every modem. peripheral.find("modem", rednet.open) +@since 1.6 ]] function find(ty, filter) expect(1, ty, "string") 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 3a8f3a5c0..3f42b6510 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/rednet.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/rednet.lua @@ -15,6 +15,7 @@ -- determine where given messages should be sent in the first place. -- -- @module rednet +-- @since 1.2 local expect = dofile("rom/modules/main/cc/expect.lua").expect @@ -28,15 +29,19 @@ local tReceivedMessages = {} local tReceivedMessageTimeouts = {} local tHostnames = {} ---- Opens a modem with the given @{peripheral} name, allowing it to send and --- receive messages over rednet. --- --- This will open the modem on two channels: one which has the same --- @{os.getComputerID|ID} as the computer, and another on --- @{CHANNEL_BROADCAST|the broadcast channel}. --- --- @tparam string modem The name of the modem to open. --- @throws If there is no such modem with the given name +--[[- Opens a modem with the given @{peripheral} name, allowing it to send and +receive messages over rednet. + +This will open the modem on two channels: one which has the same +@{os.getComputerID|ID} as the computer, and another on +@{CHANNEL_BROADCAST|the broadcast channel}. + +@tparam string modem The name of the modem to open. +@throws If there is no such modem with the given name +@usage Open a wireless modem on the back of the computer. + + rednet.open("back") +]] function open(modem) expect(1, modem, "string") if peripheral.getType(modem) ~= "modem" then @@ -76,6 +81,7 @@ end -- @tparam[opt] string modem Which modem to check. If not given, all connected -- modems will be checked. -- @treturn boolean If the given modem is open. +-- @since 1.31 function isOpen(modem) expect(1, modem, "string", "nil") if modem then @@ -94,23 +100,29 @@ function isOpen(modem) return false end ---- Allows a computer or turtle with an attached modem to send a message --- intended for a system with a specific ID. At least one such modem must first --- be @{rednet.open|opened} before sending is possible. --- --- Assuming the target was in range and also had a correctly opened modem, it --- may then use @{rednet.receive} to collect the message. --- --- @tparam number nRecipient The ID of the receiving computer. --- @param message The message to send. This should not contain coroutines or --- functions, as they will be converted to @{nil}. --- @tparam[opt] string sProtocol The "protocol" to send this message under. When --- using @{rednet.receive} one can filter to only receive messages sent under a --- particular protocol. --- @treturn boolean If this message was successfully sent (i.e. if rednet is --- currently @{rednet.open|open}). Note, this does not guarantee the message was --- actually _received_. --- @see rednet.receive +--[[- Allows a computer or turtle with an attached modem to send a message +intended for a system with a specific ID. At least one such modem must first +be @{rednet.open|opened} before sending is possible. + +Assuming the target was in range and also had a correctly opened modem, it +may then use @{rednet.receive} to collect the message. + +@tparam number nRecipient The ID of the receiving computer. +@param message The message to send. This should not contain coroutines or +functions, as they will be converted to @{nil}. +@tparam[opt] string sProtocol The "protocol" to send this message under. When +using @{rednet.receive} one can filter to only receive messages sent under a +particular protocol. +@treturn boolean If this message was successfully sent (i.e. if rednet is +currently @{rednet.open|open}). Note, this does not guarantee the message was +actually _received_. +@changed 1.6 Added protocol parameter. +@changed 1.82.0 Now returns whether the message was successfully sent. +@see rednet.receive +@usage Send a message to computer #2. + + rednet.send(2, "Hello from rednet!") +]] function send(nRecipient, message, sProtocol) expect(1, nRecipient, "number") expect(3, sProtocol, "string", "nil") @@ -158,25 +170,50 @@ end -- using @{rednet.receive} one can filter to only receive messages sent under a -- particular protocol. -- @see rednet.receive +-- @changed 1.6 Added protocol parameter. function broadcast(message, sProtocol) expect(2, sProtocol, "string", "nil") send(CHANNEL_BROADCAST, message, sProtocol) end ---- Wait for a rednet message to be received, or until `nTimeout` seconds have --- elapsed. --- --- @tparam[opt] string sProtocolFilter The protocol the received message must be --- sent with. If specified, any messages not sent under this protocol will be --- discarded. --- @tparam[opt] number nTimeout The number of seconds to wait if no message is --- received. --- @treturn[1] number The computer which sent this message --- @return[1] The received message --- @treturn[1] string|nil The protocol this message was sent under. --- @treturn[2] nil If the timeout elapsed and no message was received. --- @see rednet.broadcast --- @see rednet.send +--[[- Wait for a rednet message to be received, or until `nTimeout` seconds have +elapsed. + +@tparam[opt] string sProtocolFilter The protocol the received message must be +sent with. If specified, any messages not sent under this protocol will be +discarded. +@tparam[opt] number nTimeout The number of seconds to wait if no message is +received. +@treturn[1] number The computer which sent this message +@return[1] The received message +@treturn[1] string|nil The protocol this message was sent under. +@treturn[2] nil If the timeout elapsed and no message was received. +@see rednet.broadcast +@see rednet.send +@changed 1.6 Added protocol filter parameter. +@usage Receive a rednet message. + + local id, message = rednet.receive() + print(("Computer %d sent message %s"):format(id, message)) + +@usage Receive a message, stopping after 5 seconds if no message was received. + + local id, message = rednet.receive(nil, 5) + if not id then + printError("No message received") + else + print(("Computer %d sent message %s"):format(id, message)) + end + +@usage Receive a message from computer #2. + + local id, message + repeat + id, message = rednet.receive() + until id == 2 + + print(message) +]] function receive(sProtocolFilter, nTimeout) -- The parameters used to be ( nTimeout ), detect this case for backwards compatibility if type(sProtocolFilter) == "number" and nTimeout == nil then @@ -231,6 +268,7 @@ end -- @throws If trying to register a hostname which is reserved, or currently in use. -- @see rednet.unhost -- @see rednet.lookup +-- @since 1.6 function host(sProtocol, sHostname) expect(1, sProtocol, "string") expect(2, sHostname, "string") @@ -249,6 +287,7 @@ end -- respond to @{rednet.lookup} requests. -- -- @tparam string sProtocol The protocol to unregister your self from. +-- @since 1.6 function unhost(sProtocol) expect(1, sProtocol, "string") tHostnames[sProtocol] = nil @@ -268,6 +307,7 @@ end -- protocol, or @{nil} if none exist. -- @treturn[2] number|nil The computer ID with the provided hostname and protocol, -- or @{nil} if none exists. +-- @since 1.6 function lookup(sProtocol, sHostname) expect(1, sProtocol, "string") expect(2, sHostname, "string", "nil") @@ -351,12 +391,12 @@ function run() -- 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 == os.getComputerID() or nChannel == CHANNEL_BROADCAST) then - if type(tMessage) == "table" and tMessage.nMessageID then - if not tReceivedMessages[tMessage.nMessageID] then - tReceivedMessages[tMessage.nMessageID] = true - tReceivedMessageTimeouts[os.startTimer(30)] = tMessage.nMessageID - os.queueEvent("rednet_message", nReplyChannel, tMessage.message, tMessage.sProtocol) - end + if type(tMessage) == "table" and type(tMessage.nMessageID) == "number" + and tMessage.nMessageID == tMessage.nMessageID and not tReceivedMessages[tMessage.nMessageID] + then + tReceivedMessages[tMessage.nMessageID] = true + tReceivedMessageTimeouts[os.startTimer(30)] = tMessage.nMessageID + os.queueEvent("rednet_message", nReplyChannel, tMessage.message, tMessage.sProtocol) end end diff --git a/src/main/resources/data/computercraft/lua/rom/apis/settings.lua b/src/main/resources/data/computercraft/lua/rom/apis/settings.lua index f86044128..6ef015178 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/settings.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/settings.lua @@ -5,6 +5,7 @@ -- `/.settings` file. One can then use @{settings.save} to update the file. -- -- @module settings +-- @since 1.78 local expect = dofile("rom/modules/main/cc/expect.lua") local type, expect, field = type, expect.expect, expect.field @@ -40,9 +41,10 @@ for _, v in ipairs(valid_types) do valid_types[v] = true end -- setting has not been changed. -- - `type`: Require values to be of this type. @{set|Setting} the value to another type -- will error. +-- @since 1.87.0 function define(name, options) expect(1, name, "string") - expect(2, options, "table", nil) + expect(2, options, "table", "nil") if options then options = { @@ -67,6 +69,7 @@ end -- for that. -- -- @tparam string name The name of this option +-- @since 1.87.0 function undefine(name) expect(1, name, "string") details[name] = nil @@ -111,6 +114,7 @@ end -- this setting. If not given, it will use the setting's default value if given, -- or `nil` otherwise. -- @return The setting's, or the default if the setting has not been changed. +-- @changed 1.87.0 Now respects default value if pre-defined and `default` is unset. function get(name, default) expect(1, name, "string") local result = values[name] @@ -130,6 +134,7 @@ end -- @treturn { description? = string, default? = any, type? = string, value? = any } -- Information about this setting. This includes all information from @{settings.define}, -- as well as this setting's value. +-- @since 1.87.0 function getDetails(name) expect(1, name, "string") local deets = copy(details[name]) or {} @@ -189,6 +194,7 @@ end -- corrupted. -- -- @see settings.save +-- @changed 1.87.0 `sPath` is now optional. function load(sPath) expect(1, sPath, "string", "nil") local file = fs.open(sPath or ".settings", "r") @@ -226,6 +232,7 @@ end -- @treturn boolean If the settings were successfully saved. -- -- @see settings.load +-- @changed 1.87.0 `sPath` is now optional. function save(sPath) expect(1, sPath, "string", "nil") local file = fs.open(sPath or ".settings", "w") diff --git a/src/main/resources/data/computercraft/lua/rom/apis/term.lua b/src/main/resources/data/computercraft/lua/rom/apis/term.lua index 7461a137c..2def0eb50 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/term.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/term.lua @@ -31,6 +31,7 @@ local term = _ENV -- @tparam Redirect target The terminal redirect the @{term} API will draw to. -- @treturn Redirect The previous redirect object, as returned by -- @{term.current}. +-- @since 1.31 -- @usage -- Redirect to a monitor on the right of the computer. -- term.redirect(peripheral.wrap("right")) @@ -56,6 +57,7 @@ end --- Returns the current terminal object of the computer. -- -- @treturn Redirect The current terminal redirect +-- @since 1.6 -- @usage -- Create a new @{window} which draws to the current redirect target -- window.create(term.current(), 1, 1, 10, 10) @@ -70,6 +72,7 @@ end -- terminal object, and so drawing may interfere with other programs. -- -- @treturn Redirect The native terminal redirect. +-- @since 1.6 term.native = function() return native end diff --git a/src/main/resources/data/computercraft/lua/rom/apis/textutils.lua b/src/main/resources/data/computercraft/lua/rom/apis/textutils.lua index d6b4c5705..aac7d1822 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/textutils.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/textutils.lua @@ -2,38 +2,37 @@ -- manipulating strings. -- -- @module textutils +-- @since 1.2 local expect = dofile("rom/modules/main/cc/expect.lua") local expect, field = expect.expect, expect.field +local wrap = dofile("rom/modules/main/cc/strings.lua").wrap --- Slowly writes string text at current cursor position, -- character-by-character. -- -- Like @{_G.write}, this does not insert a newline at the end. -- --- @tparam string sText The the text to write to the screen --- @tparam[opt] number nRate The number of characters to write each second, +-- @tparam string text The the text to write to the screen +-- @tparam[opt] number rate The number of characters to write each second, -- Defaults to 20. -- @usage textutils.slowWrite("Hello, world!") -- @usage textutils.slowWrite("Hello, world!", 5) -function slowWrite(sText, nRate) - expect(2, nRate, "number", "nil") - nRate = nRate or 20 - if nRate < 0 then +-- @since 1.3 +function slowWrite(text, rate) + expect(2, rate, "number", "nil") + rate = rate or 20 + if rate < 0 then error("Rate must be positive", 2) end - local nSleep = 1 / nRate + local to_sleep = 1 / rate - sText = tostring(sText) - local x, y = term.getCursorPos() - local len = #sText + local wrapped_lines = wrap(tostring(text), (term.getSize())) + local wrapped_str = table.concat(wrapped_lines, "\n") - for n = 1, len do - term.setCursorPos(x, y) - sleep(nSleep) - local nLines = write(string.sub(sText, 1, n)) - local _, newY = term.getCursorPos() - y = newY - nLines + for n = 1, #wrapped_str do + sleep(to_sleep) + write(wrapped_str:sub(n, n)) end end @@ -58,7 +57,12 @@ end -- @tparam[opt] boolean bTwentyFourHour Whether to format this as a 24-hour -- clock (`18:30`) rather than a 12-hour one (`6:30 AM`) -- @treturn string The formatted time --- @usage textutils.formatTime(os.time()) +-- @usage Print the current in-game time as a 12-hour clock. +-- +-- textutils.formatTime(os.time()) +-- @usage Print the local time as a 24-hour clock. +-- +-- textutils.formatTime(os.time("local"), true) function formatTime(nTime, bTwentyFourHour) expect(1, nTime, "number") expect(2, bTwentyFourHour, "boolean", "nil") @@ -220,6 +224,7 @@ end -- -- @tparam {string...}|number ... The rows and text colors to display. -- @usage textutils.tabulate(colors.orange, { "1", "2", "3" }, colors.lightBlue, { "A", "B", "C" }) +-- @since 1.3 function tabulate(...) return tabulateCommon(false, ...) end @@ -234,6 +239,7 @@ end -- @usage textutils.tabulate(colors.orange, { "1", "2", "3" }, colors.lightBlue, { "A", "B", "C" }) -- @see textutils.tabulate -- @see textutils.pagedPrint +-- @since 1.3 function pagedTabulate(...) return tabulateCommon(true, ...) end @@ -262,45 +268,72 @@ local g_tLuaKeywords = { ["while"] = true, } -local function serializeImpl(t, tTracking, sIndent) +local serialize_infinity = math.huge +local function serialize_impl(t, tracking, indent, opts) local sType = type(t) if sType == "table" then - if tTracking[t] ~= nil then - error("Cannot serialize table with recursive entries", 0) + if tracking[t] ~= nil then + if tracking[t] == false then + error("Cannot serialize table with repeated entries", 0) + else + error("Cannot serialize table with recursive entries", 0) + end end - tTracking[t] = true + tracking[t] = true + local result if next(t) == nil then -- Empty tables are simple - return "{}" + result = "{}" else -- Other tables take more work - local sResult = "{\n" - local sSubIndent = sIndent .. " " - local tSeen = {} + local open, sub_indent, open_key, close_key, equal, comma = "{\n", indent .. " ", "[ ", " ] = ", " = ", ",\n" + if opts.compact then + open, sub_indent, open_key, close_key, equal, comma = "{", "", "[", "]=", "=", "," + end + + result = open + local seen_keys = {} for k, v in ipairs(t) do - tSeen[k] = true - sResult = sResult .. sSubIndent .. serializeImpl(v, tTracking, sSubIndent) .. ",\n" + seen_keys[k] = true + result = result .. sub_indent .. serialize_impl(v, tracking, sub_indent, opts) .. comma end for k, v in pairs(t) do - if not tSeen[k] then + if not seen_keys[k] then local sEntry if type(k) == "string" and not g_tLuaKeywords[k] and string.match(k, "^[%a_][%a%d_]*$") then - sEntry = k .. " = " .. serializeImpl(v, tTracking, sSubIndent) .. ",\n" + sEntry = k .. equal .. serialize_impl(v, tracking, sub_indent, opts) .. comma else - sEntry = "[ " .. serializeImpl(k, tTracking, sSubIndent) .. " ] = " .. serializeImpl(v, tTracking, sSubIndent) .. ",\n" + sEntry = open_key .. serialize_impl(k, tracking, sub_indent, opts) .. close_key .. serialize_impl(v, tracking, sub_indent, opts) .. comma end - sResult = sResult .. sSubIndent .. sEntry + result = result .. sub_indent .. sEntry end end - sResult = sResult .. sIndent .. "}" - return sResult + result = result .. indent .. "}" end + if opts.allow_repetitions then + tracking[t] = nil + else + tracking[t] = false + end + return result + elseif sType == "string" then return string.format("%q", t) - elseif sType == "number" or sType == "boolean" or sType == "nil" then + elseif sType == "number" then + if t ~= t then --nan + return "0/0" + elseif t == serialize_infinity then + return "1/0" + elseif t == -serialize_infinity then + return "-1/0" + else + return tostring(t) + end + + elseif sType == "boolean" or sType == "nil" then return tostring(t) else @@ -616,6 +649,7 @@ do -- @return[1] The deserialised object -- @treturn[2] nil If the object could not be deserialised. -- @treturn string A message describing why the JSON string is invalid. + -- @since 1.87.0 unserialise_json = function(s, options) expect(1, s, "string") expect(2, options, "table", "nil") @@ -645,17 +679,45 @@ do end end ---- Convert a Lua object into a textual representation, suitable for --- saving in a file or pretty-printing. --- --- @param t The object to serialise --- @treturn string The serialised representation --- @throws If the object contains a value which cannot be --- serialised. This includes functions and tables which appear multiple --- times. -function serialize(t) +--[[- Convert a Lua object into a textual representation, suitable for +saving in a file or pretty-printing. + +@param t The object to serialise +@tparam { compact? = boolean, allow_repetitions? = boolean } opts Options for serialisation. + - `compact`: Do not emit indentation and other whitespace between terms. + - `allow_repetitions`: Relax the check for recursive tables, allowing them to appear multiple + times (as long as tables do not appear inside themselves). + +@treturn string The serialised representation +@throws If the object contains a value which cannot be +serialised. This includes functions and tables which appear multiple +times. +@see cc.pretty.pretty An alternative way to display a table, often more suitable for +pretty printing. +@since 1.3 +@changed 1.97.0 Added `opts` argument. +@usage Pretty print a basic table. + + textutils.serialise({ 1, 2, 3, a = 1, ["another key"] = { true } }) + +@usage Demonstrates some of the other options + + local tbl = { 1, 2, 3 } + print(textutils.serialize({ tbl, tbl }, { allow_repetitions = true })) + + print(textutils.serialize(tbl, { compact = true })) +]] +function serialize(t, opts) local tTracking = {} - return serializeImpl(t, tTracking, "") + expect(2, opts, "table", "nil") + + if opts then + field(opts, "compact", "boolean", "nil") + field(opts, "allow_repetitions", "boolean", "nil") + else + opts = {} + end + return serialize_impl(t, tTracking, "", opts) end serialise = serialize -- GB version @@ -667,6 +729,7 @@ serialise = serialize -- GB version -- @tparam string s The serialised string to deserialise. -- @return[1] The deserialised object -- @treturn[2] nil If the object could not be deserialised. +-- @since 1.3 function unserialize(s) expect(1, s, "string") local func = load("return " .. s, "unserialize", "t", {}) @@ -699,6 +762,7 @@ unserialise = unserialize -- GB version -- serialised. This includes functions and tables which appear multiple -- times. -- @usage textutils.serializeJSON({ values = { 1, "2", true } }) +-- @since 1.7 function serializeJSON(t, bNBTStyle) expect(1, t, "table", "string", "number", "boolean") expect(2, bNBTStyle, "boolean", "nil") @@ -716,6 +780,7 @@ unserialiseJSON = unserialise_json -- @tparam string str The string to encode -- @treturn string The encoded string. -- @usage print("https://example.com/?view=" .. textutils.urlEncode("some text&things")) +-- @since 1.31 function urlEncode(str) expect(1, str, "string") if str then @@ -728,7 +793,7 @@ function urlEncode(str) else -- Non-ASCII (encode as UTF-8) return - string.format("%%%02X", 192 + bit32.band(bit32.arshift(n, 6), 31)) .. + string.format("%%%02X", 192 + bit32.band(bit32.arshift(n, 6), 31)) .. string.format("%%%02X", 128 + bit32.band(n, 63)) end end) @@ -755,6 +820,7 @@ local tEmpty = {} -- @see shell.setCompletionFunction -- @see _G.read -- @usage textutils.complete( "pa", _ENV ) +-- @since 1.74 function complete(sSearchText, tSearchTable) expect(1, sSearchText, "string") expect(2, tSearchTable, "table", "nil") diff --git a/src/main/resources/data/computercraft/lua/rom/apis/vector.lua b/src/main/resources/data/computercraft/lua/rom/apis/vector.lua index 05dab327e..a5227c853 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/vector.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/vector.lua @@ -5,6 +5,7 @@ -- [wiki]: http://en.wikipedia.org/wiki/Euclidean_vector -- -- @module vector +-- @since 1.31 --- A 3-dimensional vector, with `x`, `y`, and `z` values. -- @@ -151,6 +152,15 @@ local vector = { tostring = function(self) return self.x .. "," .. self.y .. "," .. self.z end, + + --- Check for equality between two vectors. + -- + -- @tparam Vector self The first vector to compare. + -- @tparam Vector other The second vector to compare to. + -- @treturn boolean Whether or not the vectors are equal. + equals = function(self, other) + return self.x == other.x and self.y == other.y and self.z == other.z + end, } local vmetatable = { @@ -161,6 +171,7 @@ local vmetatable = { __div = vector.div, __unm = vector.unm, __tostring = vector.tostring, + __eq = vector.equals, } --- Construct a new @{Vector} with the given coordinates. diff --git a/src/main/resources/data/computercraft/lua/rom/apis/window.lua b/src/main/resources/data/computercraft/lua/rom/apis/window.lua index 684d54f9c..feafa00c2 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/window.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/window.lua @@ -26,6 +26,7 @@ -- terminal display as its parent, and only one of which is visible at a time. -- -- @module window +-- @since 1.6 local expect = dofile("rom/modules/main/cc/expect.lua").expect @@ -52,24 +53,40 @@ local type = type local string_rep = string.rep local string_sub = string.sub ---- Returns a terminal object that is a space within the specified parent --- terminal object. This can then be used (or even redirected to) in the same --- manner as eg a wrapped monitor. Refer to @{term|the term API} for a list of --- functions available to it. --- --- @{term} itself may not be passed as the parent, though @{term.native} is --- acceptable. Generally, @{term.current} or a wrapped monitor will be most --- suitable, though windows may even have other windows assigned as their --- parents. --- --- @tparam term.Redirect parent The parent terminal redirect to draw to. --- @tparam number nX The x coordinate this window is drawn at in the parent terminal --- @tparam number nY The y coordinate this window is drawn at in the parent terminal --- @tparam number nWidth The width of this window --- @tparam number nHeight The height of this window --- @tparam[opt] boolean bStartVisible Whether this window is visible by --- default. Defaults to `true`. --- @treturn Window The constructed window +--[[- Returns a terminal object that is a space within the specified parent +terminal object. This can then be used (or even redirected to) in the same +manner as eg a wrapped monitor. Refer to @{term|the term API} for a list of +functions available to it. + +@{term} itself may not be passed as the parent, though @{term.native} is +acceptable. Generally, @{term.current} or a wrapped monitor will be most +suitable, though windows may even have other windows assigned as their +parents. + +@tparam term.Redirect parent The parent terminal redirect to draw to. +@tparam number nX The x coordinate this window is drawn at in the parent terminal +@tparam number nY The y coordinate this window is drawn at in the parent terminal +@tparam number nWidth The width of this window +@tparam number nHeight The height of this window +@tparam[opt] boolean bStartVisible Whether this window is visible by +default. Defaults to `true`. +@treturn Window The constructed window +@since 1.6 +@usage Create a smaller window, fill it red and write some text to it. + + local my_window = window.create(term.current(), 1, 1, 20, 5) + my_window.setBackgroundColour(colours.red) + my_window.setTextColour(colours.white) + my_window.clear() + my_window.write("Testing my window!") + +@usage Create a smaller window and redirect to it. + + local my_window = window.create(term.current(), 1, 1, 25, 5) + term.redirect(my_window) + print("Writing some long text which will wrap around and show the bounds of this window.") + +]] function create(parent, nX, nY, nWidth, nHeight, bStartVisible) expect(1, parent, "table") expect(2, nX, "number") @@ -125,7 +142,7 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible) -- Helper functions local function updateCursorPos() if nCursorX >= 1 and nCursorY >= 1 and - nCursorX <= nWidth and nCursorY <= nHeight then + nCursorX <= nWidth and nCursorY <= nHeight then parent.setCursorPos(nX + nCursorX - 1, nY + nCursorY - 1) else parent.setCursorPos(0, 0) @@ -446,6 +463,7 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible) -- @treturn string The text colours of this line, suitable for use with @{term.blit}. -- @treturn string The background colours of this line, suitable for use with @{term.blit}. -- @throws If `y` is not between 1 and this window's height. + -- @since 1.84.0 function window.getLine(y) if type(y) ~= "number" then expect(1, y, "number") end @@ -479,6 +497,7 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible) -- -- @treturn boolean Whether this window is visible. -- @see Window:setVisible + -- @since 1.94.0 function window.isVisible() return bVisible end @@ -525,6 +544,7 @@ function create(parent, nX, nY, nWidth, nHeight, bStartVisible) -- @tparam number new_height The new height of this window. -- @tparam[opt] term.Redirect new_parent The new redirect object this -- window should draw to. + -- @changed 1.85.0 Add `new_parent` parameter. function window.reposition(new_x, new_y, new_width, new_height, new_parent) if type(new_x) ~= "number" then expect(1, new_x, "number") end if type(new_y) ~= "number" then expect(2, new_y, "number") end diff --git a/src/main/resources/data/computercraft/lua/rom/help/craft.txt b/src/main/resources/data/computercraft/lua/rom/help/craft.txt index de6f6eb60..9f0bd6cf5 100644 --- a/src/main/resources/data/computercraft/lua/rom/help/craft.txt +++ b/src/main/resources/data/computercraft/lua/rom/help/craft.txt @@ -1,5 +1,5 @@ craft is a program for Crafty Turtles. Craft will craft a stack of items using the current inventory. ex: -"craft" will craft as many items as possible +"craft all" will craft as many items as possible "craft 5" will craft at most 5 times diff --git a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/completion.lua b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/completion.lua index 67a83bdbc..ade5988ae 100644 --- a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/completion.lua +++ b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/completion.lua @@ -4,6 +4,7 @@ -- @module cc.completion -- @see cc.shell.completion For additional helpers to use with -- @{shell.setCompletionFunction}. +-- @since 1.85.0 local expect = require "cc.expect".expect @@ -31,8 +32,9 @@ end -- @treturn { string... } A list of suffixes of matching strings. -- @usage Call @{_G.read}, completing the names of various animals. -- +-- local completion = require "cc.completion" -- local animals = { "dog", "cat", "lion", "unicorn" } --- read(nil, nil, function(text) return choice(text, animals) end) +-- read(nil, nil, function(text) return completion.choice(text, animals) end) local function choice(text, choices, add_space) expect(1, text, "string") expect(2, choices, "table") diff --git a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/expect.lua b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/expect.lua index 877fa0476..e5c5ebcbf 100644 --- a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/expect.lua +++ b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/expect.lua @@ -2,6 +2,8 @@ function arguments are well-formed and of the correct type. @module cc.expect +@since 1.84.0 +@changed 1.96.0 The module can now be called directly as a function, which wraps around `expect.expect`. @usage Define a basic function and check it has the correct arguments. local expect = require "cc.expect" @@ -51,10 +53,8 @@ local function expect(index, value, ...) -- If we can determine the function name with a high level of confidence, try to include it. local name - if native_type(debug) == "table" and native_type(debug.getinfo) == "function" then - local ok, info = pcall(debug.getinfo, 3, "nS") - if ok and info.name and info.name ~= "" and info.what ~= "C" then name = info.name end - end + local ok, info = pcall(debug.getinfo, 3, "nS") + if ok and info.name and info.name ~= "" and info.what ~= "C" then name = info.name end local type_names = get_type_names(...) if name then @@ -94,11 +94,12 @@ end --- Expect a number to be within a specific range. -- --- @tparam number num, The value to check. +-- @tparam number num The value to check. -- @tparam number min The minimum value, if nil then `-math.huge` is used. -- @tparam number max The maximum value, if nil then `math.huge` is used. -- @return The given `value`. -- @throws If the value is outside of the allowed range. +-- @since 1.96.0 local function range(num, min, max) expect(1, num, "number") min = expect(2, min, "number", "nil") or -math.huge diff --git a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/image/nft.lua b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/image/nft.lua index 225abb6f7..410934c5d 100644 --- a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/image/nft.lua +++ b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/image/nft.lua @@ -5,6 +5,7 @@ -- text. -- -- @module cc.image.nft +-- @since 1.90.0 -- @usage Load an image from `example.nft` and draw it. -- -- local nft = require "cc.image.nft" diff --git a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/pretty.lua b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/pretty.lua index 6ac9aeeb2..f5cdeee6c 100644 --- a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/pretty.lua +++ b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/pretty.lua @@ -15,11 +15,14 @@ The structure of this module is based on [A Prettier Printer][prettier]. [prettier]: https://homepages.inf.ed.ac.uk/wadler/papers/prettier/prettier.pdf "A Prettier Printer" @module cc.pretty +@since 1.87.0 @usage Print a table to the terminal + local pretty = require "cc.pretty" pretty.print(pretty.pretty({ 1, 2, 3 })) @usage Build a custom document and display it + local pretty = require "cc.pretty" pretty.print(pretty.group(pretty.text("hello") .. pretty.space_line .. pretty.text("world"))) ]] @@ -28,8 +31,7 @@ local expect = require "cc.expect" local expect, field = expect.expect, expect.field local type, getmetatable, setmetatable, colours, str_write, tostring = type, getmetatable, setmetatable, colours, write, tostring -local debug_info = type(debug) == "table" and type(debug.getinfo) == "function" and debug.getinfo -local debug_local = type(debug) == "table" and type(debug.getlocal) == "function" and debug.getlocal +local debug_info, debug_local = debug.getinfo, debug.getlocal --- @{table.insert} alternative, but with the length stored inline. local function append(out, value) @@ -45,17 +47,19 @@ end -- @type Doc local Doc = { } +local function mk_doc(tbl) return setmetatable(tbl, Doc) end + --- An empty document. -local empty = setmetatable({ tag = "nil" }, Doc) +local empty = mk_doc({ tag = "nil" }) --- A document with a single space in it. -local space = setmetatable({ tag = "text", text = " " }, Doc) +local space = mk_doc({ tag = "text", text = " " }) --- A line break. When collapsed with @{group}, this will be replaced with @{empty}. -local line = setmetatable({ tag = "line", flat = empty }, Doc) +local line = mk_doc({ tag = "line", flat = empty }) --- A line break. When collapsed with @{group}, this will be replaced with @{space}. -local space_line = setmetatable({ tag = "line", flat = space }, Doc) +local space_line = mk_doc({ tag = "line", flat = space }) local text_cache = { [""] = empty, [" "] = space, ["\n"] = space_line } @@ -73,6 +77,7 @@ end -- colour. -- @treturn Doc The document with the provided text. -- @usage Write some blue text. +-- -- local pretty = require "cc.pretty" -- pretty.print(pretty.text("Hello!", colours.blue)) local function text(text, colour) @@ -453,7 +458,9 @@ end -- - `function_source`: Show where the function was defined, instead of -- `function: xxxxxxxx` (`false` by default). -- @treturn Doc The object formatted as a document. +-- @changed 1.88.0 Added `options` argument. -- @usage Display a table on the screen +-- -- local pretty = require "cc.pretty" -- pretty.print(pretty.pretty({ 1, 2, 3 })) local function pretty(obj, options) diff --git a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/require.lua b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/require.lua index a428ae08d..24c27b15c 100644 --- a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/require.lua +++ b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/require.lua @@ -6,12 +6,17 @@ -- custom shell or when running programs yourself. -- -- @module cc.require +-- @since 1.88.0 -- @usage Construct the package and require function, and insert them into a -- custom environment. -- -- local r = require "cc.require" -- local env = setmetatable({}, { __index = _ENV }) -- env.require, env.package = r.make(env, "/") +-- +-- -- Now we have our own require function, separate to the original. +-- local r2 = env.require "cc.require" +-- print(r, r2) local expect = require and require("cc.expect") or dofile("rom/modules/main/cc/expect.lua") local expect = expect.expect diff --git a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/shell/completion.lua b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/shell/completion.lua index 06619c7f1..4bdc24674 100644 --- a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/shell/completion.lua +++ b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/shell/completion.lua @@ -8,6 +8,7 @@ and so are not directly usable with the @{shell.setCompletionFunction}. Instead, wrap them using @{build}, or your own custom function. @module cc.shell.completion +@since 1.85.0 @see cc.completion For more general helpers, suitable for use with @{_G.read}. @see shell.setCompletionFunction @@ -29,8 +30,8 @@ local completion = require "cc.completion" --- Complete the name of a file relative to the current working directory. -- --- @tparam table shell The shell we're completing in --- @tparam { string... } choices The list of choices to complete from. +-- @tparam table shell The shell we're completing in. +-- @tparam string text Current text to complete. -- @treturn { string... } A list of suffixes of matching files. local function file(shell, text) return fs.complete(text, shell.dir(), true, false) @@ -38,8 +39,8 @@ end --- Complete the name of a directory relative to the current working directory. -- --- @tparam table shell The shell we're completing in --- @tparam { string... } choices The list of choices to complete from. +-- @tparam table shell The shell we're completing in. +-- @tparam string text Current text to complete. -- @treturn { string... } A list of suffixes of matching directories. local function dir(shell, text) return fs.complete(text, shell.dir(), false, true) @@ -48,8 +49,8 @@ end --- Complete the name of a file or directory relative to the current working -- directory. -- --- @tparam table shell The shell we're completing in --- @tparam { string... } choices The list of choices to complete from. +-- @tparam table shell The shell we're completing in. +-- @tparam string text Current text to complete. -- @tparam { string... } previous The shell arguments before this one. -- @tparam[opt] boolean add_space Whether to add a space after the completed item. -- @treturn { string... } A list of suffixes of matching files and directories. @@ -74,14 +75,47 @@ end --- Complete the name of a program. -- --- @tparam table shell The shell we're completing in --- @tparam { string... } choices The list of choices to complete from. +-- @tparam table shell The shell we're completing in. +-- @tparam string text Current text to complete. -- @treturn { string... } A list of suffixes of matching programs. -- @see shell.completeProgram local function program(shell, text) return shell.completeProgram(text) end +--- Complete arguments of a program. +-- +-- @tparam table shell The shell we're completing in. +-- @tparam string text Current text to complete. +-- @tparam { string... } previous The shell arguments before this one. +-- @tparam number starting Which argument index this program and args start at. +-- @treturn { string... } A list of suffixes of matching programs or arguments. +-- @since 1.97.0 +local function programWithArgs(shell, text, previous, starting) + if #previous + 1 == starting then + local tCompletionInfo = shell.getCompletionInfo() + if text:sub(-1) ~= "/" and tCompletionInfo[shell.resolveProgram(text)] then + return { " " } + else + local results = shell.completeProgram(text) + for n = 1, #results do + local sResult = results[n] + if sResult:sub(-1) ~= "/" and tCompletionInfo[shell.resolveProgram(text .. sResult)] then + results[n] = sResult .. " " + end + end + return results + end + else + local program = previous[starting] + local resolved = shell.resolveProgram(program) + if not resolved then return end + local tCompletion = shell.getCompletionInfo()[resolved] + if not tCompletion then return end + return tCompletion.fnComplete(shell, #previous - starting + 1, text, { program, table.unpack(previous, starting + 1, #previous) }) + end +end + --[[- A helper function for building shell completion arguments. This accepts a series of single-argument completion functions, and combines @@ -144,6 +178,7 @@ return { dir = dir, dirOrFile = dirOrFile, program = program, + programWithArgs = programWithArgs, -- Re-export various other functions help = wrap(help.completeTopic), --- Wraps @{help.completeTopic} as a @{build} compatible function. diff --git a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/strings.lua b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/strings.lua index 89d6e475c..6c9da0d9f 100644 --- a/src/main/resources/data/computercraft/lua/rom/modules/main/cc/strings.lua +++ b/src/main/resources/data/computercraft/lua/rom/modules/main/cc/strings.lua @@ -1,9 +1,10 @@ --- Various utilities for working with strings and text. -- -- @module cc.strings +-- @since 1.95.0 -- @see textutils For additional string related utilities. -local expect = require "cc.expect".expect +local expect = (require and require("cc.expect") or dofile("rom/modules/main/cc/expect.lua")).expect --[[- Wraps a block of text, so that each line fits within the given width. diff --git a/src/main/resources/data/computercraft/lua/rom/motd.txt b/src/main/resources/data/computercraft/lua/rom/motd.txt index 0c5db751b..ce6b91f4b 100644 --- a/src/main/resources/data/computercraft/lua/rom/motd.txt +++ b/src/main/resources/data/computercraft/lua/rom/motd.txt @@ -21,3 +21,4 @@ The "equip" programs let you add upgrades to a turtle without crafting. You can change the color of a disk by crafting or right clicking it with dye. You can print on a printed page again to get multiple colors. Holding the Ctrl and T keys terminates the running program. +You can drag and drop files onto an open computer to upload them. diff --git a/src/main/resources/data/computercraft/lua/rom/programs/advanced/multishell.lua b/src/main/resources/data/computercraft/lua/rom/programs/advanced/multishell.lua index 1ff73ab9f..59de7153d 100644 --- a/src/main/resources/data/computercraft/lua/rom/programs/advanced/multishell.lua +++ b/src/main/resources/data/computercraft/lua/rom/programs/advanced/multishell.lua @@ -15,6 +15,7 @@ -- not available to @{os.loadAPI|APIs}. -- -- @module[module] multishell +-- @since 1.6 local expect = dofile("rom/modules/main/cc/expect.lua").expect @@ -290,6 +291,7 @@ end -- @treturn number The index of the created process. -- @see os.run -- @usage Run the "hello" program, and set its title to "Hello!" +-- -- local id = multishell.launch({}, "/rom/programs/fun/hello.lua") -- multishell.setTitle(id, "Hello!") function multishell.launch(tProgramEnv, sProgramPath, ...) diff --git a/src/main/resources/data/computercraft/lua/rom/programs/fun/advanced/paint.lua b/src/main/resources/data/computercraft/lua/rom/programs/fun/advanced/paint.lua index 91667464b..044b12116 100644 --- a/src/main/resources/data/computercraft/lua/rom/programs/fun/advanced/paint.lua +++ b/src/main/resources/data/computercraft/lua/rom/programs/fun/advanced/paint.lua @@ -242,8 +242,8 @@ end Converts each colour in a single line of the canvas and draws it returns: nil ]] -local text, fg, bg = "", "", "" local function drawCanvasLine(y) + local text, fg, bg = "", "", "" for x = 1, w - 2 do local pixel = getCanvasPixel(x, y) if pixel then @@ -256,6 +256,7 @@ local function drawCanvasLine(y) bg = bg .. color_hex_lookup[canvasColour] end end + term.setCursorPos(1, y) term.blit(text, fg, bg) end @@ -289,6 +290,7 @@ local menu_choices = { return false end, Exit = function() + sleep(0) -- Super janky, but consumes stray "char" events from pressing Ctrl then E separately. return true end, } diff --git a/src/main/resources/data/computercraft/lua/rom/programs/help.lua b/src/main/resources/data/computercraft/lua/rom/programs/help.lua index 497a6dff7..9c7081ecc 100644 --- a/src/main/resources/data/computercraft/lua/rom/programs/help.lua +++ b/src/main/resources/data/computercraft/lua/rom/programs/help.lua @@ -14,16 +14,127 @@ if sTopic == "index" then end local strings = require "cc.strings" -local function word_wrap(text, width) - local lines = strings.wrap(text, width) + +local function min_of(a, b, default) + if not a and not b then return default end + if not a then return b end + if not b then return a end + return math.min(a, b) +end + +--[[- Parse a markdown string, extracting headings and highlighting some basic +constructs. + +The implementation of this is horrible. SquidDev shouldn't be allowed to write +parsers, especially ones they think might be "performance critical". +]] +local function parse_markdown(text) + local len = #text + local oob = len + 1 + + -- Some patterns to match headers and bullets on the start of lines. + -- The `%f[^\n\0]` is some wonderful logic to match the start of a line /or/ + -- the start of the document. + local heading = "%f[^\n\0](#+ +)([^\n]*)" + local bullet = "%f[^\n\0]( *)[.*]( +)" + local code = "`([^`]+)`" + + local new_text, fg, bg = "", "", "" + local function append(txt, fore, back) + new_text = new_text .. txt + fg = fg .. (fore or "0"):rep(#txt) + bg = bg .. (back or "f"):rep(#txt) + end + + local next_header = text:find(heading) + local next_bullet = text:find(bullet) + local next_block = min_of(next_header, next_bullet, oob) + + local next_code, next_code_end = text:find(code) + + local sections = {} + + local start = 1 + while start <= len do + if start == next_block then + if start == next_header then + local _, fin, head, content = text:find(heading, start) + sections[#new_text + 1] = content + append(head .. content, "4", "f") + start = fin + 1 + + next_header = text:find(heading, start) + else + local _, fin, space, content = text:find(bullet, start) + append(space .. "\7" .. content) + start = fin + 1 + + next_bullet = text:find(bullet, start) + end + + next_block = min_of(next_header, next_bullet, oob) + elseif next_code and next_code_end < next_block then + -- Basic inline code blocks + if start < next_code then append(text:sub(start, next_code - 1)) end + local content = text:match(code, next_code) + append(content, "0", "7") + + start = next_code_end + 1 + next_code, next_code_end = text:find(code, start) + else + -- Normal text + append(text:sub(start, next_block - 1)) + start = next_block + + -- Rescan for a new code block + if next_code then next_code, next_code_end = text:find(code, start) end + end + end + + return new_text, fg, bg, sections +end + +local function word_wrap_basic(text, width) + local lines, fg, bg = strings.wrap(text, width), {}, {} + local fg_line, bg_line = ("0"):rep(width), ("f"):rep(width) -- Normalise the strings suitable for use with blit. We could skip this and -- just use term.write, but saves us a clearLine call. for k, line in pairs(lines) do lines[k] = strings.ensure_width(line, width) + fg[k] = fg_line + bg[k] = bg_line end - return lines + return lines, fg, bg, {} +end + +local function word_wrap_markdown(text, width) + -- Add in styling for Markdown-formatted text. + local text, fg, bg, sections = parse_markdown(text) + + local lines = strings.wrap(text, width) + local fglines, bglines, section_list, section_n = {}, {}, {}, 1 + + -- Normalise the strings suitable for use with blit. We could skip this and + -- just use term.write, but saves us a clearLine call. + local start = 1 + for k, line in pairs(lines) do + -- I hate this with a burning passion, but it works! + local pos = text:find(line, start, true) + lines[k], fglines[k], bglines[k] = + strings.ensure_width(line, width), + strings.ensure_width(fg:sub(pos, pos + #line), width), + strings.ensure_width(bg:sub(pos, pos + #line), width) + + if sections[pos] then + section_list[section_n], section_n = { content = sections[pos], offset = k - 1 }, section_n + 1 + end + + start = pos + 1 + end + + return lines, fglines, bglines, section_list end local sFile = help.lookup(sTopic) @@ -33,31 +144,40 @@ if not file then return end -local contents = file:read("*a"):gsub("(\n *)[-*]( +)", "%1\7%2") +local contents = file:read("*a") file:close() +local word_wrap = sFile:sub(-3) == ".md" and word_wrap_markdown or word_wrap_basic local width, height = term.getSize() -local lines = word_wrap(contents, width) +local lines, fg, bg, sections = word_wrap(contents, width) local print_height = #lines -- If we fit within the screen, just display without pagination. if print_height <= height then - print(contents) + local _, y = term.getCursorPos() + for i = 1, print_height do + if y + i - 1 > height then + term.scroll(1) + term.setCursorPos(1, height) + else + term.setCursorPos(1, y + i - 1) + end + + term.blit(lines[i], fg[i], bg[i]) + end return end +local current_section = nil local offset = 0 -local function draw() - local fg, bg = ("0"):rep(width), ("f"):rep(width) - for y = 1, height - 1 do - term.setCursorPos(1, y) - if y + offset > print_height then - -- Should only happen if we resize the terminal to a larger one - -- than actually needed for the current text. - term.clearLine() - else - term.blit(lines[y + offset], fg, bg) +--- Find the currently visible seciton, or nil if this document has no sections. +-- +-- This could potentially be a binary search, but right now it's not worth it. +local function find_section() + for i = #sections, 1, -1 do + if sections[i].offset <= offset then + return i end end end @@ -68,7 +188,10 @@ local function draw_menu() term.clearLine() local tag = "Help: " .. sTopic - term.write("Help: " .. sTopic) + if current_section then + tag = tag .. (" (%s)"):format(sections[current_section].content) + end + term.write(tag) if width >= #tag + 16 then term.setCursorPos(width - 14, height) @@ -76,11 +199,31 @@ local function draw_menu() end end + +local function draw() + for y = 1, height - 1 do + term.setCursorPos(1, y) + if y + offset > print_height then + -- Should only happen if we resize the terminal to a larger one + -- than actually needed for the current text. + term.clearLine() + else + term.blit(lines[y + offset], fg[y + offset], bg[y + offset]) + end + end + + local new_section = find_section() + if new_section ~= current_section then + current_section = new_section + draw_menu() + end +end + draw() draw_menu() while true do - local event, param = os.pullEvent() + local event, param = os.pullEventRaw() if event == "key" then if param == keys.up and offset > 0 then offset = offset - 1 @@ -97,6 +240,12 @@ while true do elseif param == keys.home then offset = 0 draw() + elseif param == keys.left and current_section and current_section > 1 then + offset = sections[current_section - 1].offset + draw() + elseif param == keys.right and current_section and current_section < #sections then + offset = sections[current_section + 1].offset + draw() elseif param == keys["end"] then offset = print_height - height draw() @@ -116,7 +265,7 @@ while true do local new_width, new_height = term.getSize() if new_width ~= width then - lines = word_wrap(contents, new_width) + lines, fg, bg = word_wrap(contents, new_width) print_height = #lines end @@ -124,6 +273,8 @@ while true do offset = math.max(math.min(offset, print_height - height), 0) draw() draw_menu() + elseif event == "terminate" then + break end end diff --git a/src/main/resources/data/computercraft/lua/rom/programs/http/wget.lua b/src/main/resources/data/computercraft/lua/rom/programs/http/wget.lua index ac5fc4fc0..042660c02 100644 --- a/src/main/resources/data/computercraft/lua/rom/programs/http/wget.lua +++ b/src/main/resources/data/computercraft/lua/rom/programs/http/wget.lua @@ -51,7 +51,7 @@ local function get(sUrl) local sResponse = response.readAll() response.close() - return sResponse + return sResponse or "" end if run then @@ -69,7 +69,7 @@ if run then printError(err) end else - local sFile = tArgs[1] or getFilename(url) + local sFile = tArgs[1] or getFilename(url) or url local sPath = shell.resolve(sFile) if fs.exists(sPath) then print("File already exists") @@ -79,7 +79,12 @@ else local res = get(url) if not res then return end - local file = fs.open(sPath, "wb") + local file, err = fs.open(sPath, "wb") + if not file then + printError("Cannot save file: " .. err) + return + end + file.write(res) file.close() diff --git a/src/main/resources/data/computercraft/lua/rom/programs/label.lua b/src/main/resources/data/computercraft/lua/rom/programs/label.lua index 8e413e09a..68f228c60 100644 --- a/src/main/resources/data/computercraft/lua/rom/programs/label.lua +++ b/src/main/resources/data/computercraft/lua/rom/programs/label.lua @@ -9,7 +9,6 @@ local function printUsage() print(programName .. " clear ") end - local function checkDrive(sDrive) if peripheral.getType(sDrive) == "drive" then -- Check the disk exists diff --git a/src/main/resources/data/computercraft/lua/rom/programs/monitor.lua b/src/main/resources/data/computercraft/lua/rom/programs/monitor.lua index e97558357..fbf8daa04 100644 --- a/src/main/resources/data/computercraft/lua/rom/programs/monitor.lua +++ b/src/main/resources/data/computercraft/lua/rom/programs/monitor.lua @@ -1,15 +1,34 @@ local function printUsage() local programName = arg[0] or fs.getName(shell.getRunningProgram()) - print("Usage: " .. programName .. " ") + print("Usage:") + print(" " .. programName .. " ") + print(" " .. programName .. " scale ") return end local tArgs = { ... } -if #tArgs < 2 then +if #tArgs < 2 or tArgs[1] == "scale" and #tArgs < 3 then printUsage() return end +if tArgs[1] == "scale" then + local sName = tArgs[2] + if peripheral.getType(sName) ~= "monitor" then + print("No monitor named " .. sName) + return + end + + local nRes = tonumber(tArgs[3]) + if nRes == nil or nRes < 0.5 or nRes > 5 then + print("Invalid scale: " .. tArgs[3]) + return + end + + peripheral.call(sName, "setTextScale", nRes) + return +end + local sName = tArgs[1] if peripheral.getType(sName) ~= "monitor" then print("No monitor named " .. sName) diff --git a/src/main/resources/data/computercraft/lua/rom/programs/shell.lua b/src/main/resources/data/computercraft/lua/rom/programs/shell.lua index 1cc6dfe48..f24c0c4fc 100644 --- a/src/main/resources/data/computercraft/lua/rom/programs/shell.lua +++ b/src/main/resources/data/computercraft/lua/rom/programs/shell.lua @@ -56,6 +56,7 @@ end -- @tparam string command The program to execute. -- @tparam string ... Arguments to this program. -- @treturn boolean Whether the program exited successfully. +-- @since 1.88.0 -- @usage Run `paint my-image` from within your program: -- -- shell.execute("paint", "my-image") @@ -131,6 +132,8 @@ end -- -- shell.run("paint", "my-image") -- @see shell.execute Run a program directly without parsing the arguments. +-- @changed 1.80pr1 Programs now get their own environment instead of sharing the same one. +-- @changed 1.83.0 `arg` is now added to the environment. function shell.run(...) local tWords = tokenise(...) local sCommand = tWords[1] @@ -193,6 +196,7 @@ end -- for from the @{shell.dir|current directory}, rather than the computer's root. -- -- @tparam string path The new program path. +-- @since 1.2 function shell.setPath(path) expect(1, path, "string") sPath = path @@ -235,6 +239,7 @@ end -- @tparam string command The name of the program -- @treturn string|nil The absolute path to the program, or @{nil} if it could -- not be found. +-- @since 1.2 -- @usage Locate the `hello` program. -- -- shell.resolveProgram("hello") @@ -283,6 +288,7 @@ end -- start with `.`. -- @treturn { string } A list of available programs. -- @usage textutils.tabulate(shell.programs()) +-- @since 1.2 function shell.programs(include_hidden) expect(1, include_hidden, "boolean", "nil") @@ -387,6 +393,7 @@ end -- @see shell.completeProgram -- @see shell.setCompletionFunction -- @see shell.getCompletionInfo +-- @since 1.74 function shell.complete(sLine) expect(1, sLine, "string") if #sLine > 0 then @@ -462,6 +469,7 @@ end -- @see cc.shell.completion Various utilities to help with writing completion functions. -- @see shell.complete -- @see _G.read For more information about completion. +-- @since 1.74 function shell.setCompletionFunction(program, complete) expect(1, program, "string") expect(2, complete, "function") @@ -484,6 +492,7 @@ end --- Returns the path to the currently running program. -- -- @treturn string The absolute path to the running program. +-- @since 1.3 function shell.getRunningProgram() if #tProgramStack > 0 then return tProgramStack[#tProgramStack] @@ -495,6 +504,7 @@ end -- -- @tparam string command The name of the alias to add. -- @tparam string program The name or path to the program. +-- @since 1.2 -- @usage Alias `vim` to the `edit` program -- -- shell.setAlias("vim", "edit") @@ -543,6 +553,7 @@ if multishell then -- @tparam string ... The command line to run. -- @see shell.run -- @see multishell.launch + -- @since 1.6 -- @usage Launch the Lua interpreter and switch to it. -- -- local id = shell.openTab("lua") @@ -566,6 +577,7 @@ if multishell then -- -- @tparam number id The tab to switch to. -- @see multishell.setFocus + -- @since 1.6 function shell.switchTab(id) expect(1, id, "number") multishell.setFocus(id) diff --git a/src/main/resources/data/computercraft/lua/rom/programs/turtle/craft.lua b/src/main/resources/data/computercraft/lua/rom/programs/turtle/craft.lua index 34da46aea..19ab82f45 100644 --- a/src/main/resources/data/computercraft/lua/rom/programs/turtle/craft.lua +++ b/src/main/resources/data/computercraft/lua/rom/programs/turtle/craft.lua @@ -1,6 +1,5 @@ if not turtle then - local programName = arg[0] or fs.getName(shell.getRunningProgram()) - print("Usage: " .. programName .. " [number]") + printError("Requires a Turtle") return end @@ -10,19 +9,19 @@ if not turtle.craft then end local tArgs = { ... } -local nLimit = nil -if #tArgs < 1 then - print("Usage: craft [number]") +local nLimit = tonumber(tArgs[1]) + +if not nLimit and tArgs[1] ~= "all" then + local programName = arg[0] or fs.getName(shell.getRunningProgram()) + print("Usage: " .. programName .. " all|") return -else - nLimit = tonumber(tArgs[1]) end local nCrafted = 0 local nOldCount = turtle.getItemCount(turtle.getSelectedSlot()) if turtle.craft(nLimit) then local nNewCount = turtle.getItemCount(turtle.getSelectedSlot()) - if nOldCount <= nLimit then + if not nLimit or nOldCount <= nLimit then nCrafted = nNewCount else nCrafted = nOldCount - nNewCount diff --git a/src/main/resources/data/computercraft/lua/rom/startup.lua b/src/main/resources/data/computercraft/lua/rom/startup.lua index 8c347cc33..8c03b4852 100644 --- a/src/main/resources/data/computercraft/lua/rom/startup.lua +++ b/src/main/resources/data/computercraft/lua/rom/startup.lua @@ -67,10 +67,33 @@ shell.setCompletionFunction("rom/programs/label.lua", completion.build( )) shell.setCompletionFunction("rom/programs/list.lua", completion.build(completion.dir)) shell.setCompletionFunction("rom/programs/mkdir.lua", completion.build({ completion.dir, many = true })) + +local complete_monitor_extra = { "scale" } shell.setCompletionFunction("rom/programs/monitor.lua", completion.build( - { completion.peripheral, true }, - completion.program + function(shell, text, previous) + local choices = completion.peripheral(shell, text, previous, true) + for _, option in pairs(completion.choice(shell, text, previous, complete_monitor_extra, true)) do + choices[#choices + 1] = option + end + return choices + end, + function(shell, text, previous) + if previous[2] == "scale" then + return completion.peripheral(shell, text, previous, true) + else + return completion.programWithArgs(shell, text, previous, 3) + end + end, + { + function(shell, text, previous) + if previous[2] ~= "scale" then + return completion.programWithArgs(shell, text, previous, 3) + end + end, + many = true, + } )) + shell.setCompletionFunction("rom/programs/move.lua", completion.build( { completion.dirOrFile, true }, completion.dirOrFile @@ -83,11 +106,11 @@ shell.setCompletionFunction("rom/programs/rename.lua", completion.build( { completion.dirOrFile, true }, completion.dirOrFile )) -shell.setCompletionFunction("rom/programs/shell.lua", completion.build(completion.program)) +shell.setCompletionFunction("rom/programs/shell.lua", completion.build({ completion.programWithArgs, 2, many = true })) shell.setCompletionFunction("rom/programs/type.lua", completion.build(completion.dirOrFile)) shell.setCompletionFunction("rom/programs/set.lua", completion.build({ completion.setting, true })) -shell.setCompletionFunction("rom/programs/advanced/bg.lua", completion.build(completion.program)) -shell.setCompletionFunction("rom/programs/advanced/fg.lua", completion.build(completion.program)) +shell.setCompletionFunction("rom/programs/advanced/bg.lua", completion.build({ completion.programWithArgs, 2, many = true })) +shell.setCompletionFunction("rom/programs/advanced/fg.lua", completion.build({ completion.programWithArgs, 2, many = true })) shell.setCompletionFunction("rom/programs/fun/dj.lua", completion.build( { completion.choice, { "play", "play ", "stop " } }, completion.peripheral diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 32bd056ea..32343f71f 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -20,8 +20,10 @@ "Toad-Dev" ], "depends": { - "fabricloader": ">=0.4.0", - "fabric": "*" + "fabricloader": ">=0.11.3", + "fabric": "*", + "minecraft": "1.17.x", + "java": ">=16" }, "suggests": { "modmenu": "*" @@ -30,6 +32,7 @@ "canvas": "*", "iris": "*" }, + "accessWidener": "cc.accesswidener", "environment": "*", "entrypoints": { "main": [ diff --git a/src/main/resources/resourcepacks/classic/assets/computercraft/textures/gui/corners_advanced.png b/src/main/resources/resourcepacks/classic/assets/computercraft/textures/gui/corners_advanced.png index 12a080626..4da80a9bc 100644 Binary files a/src/main/resources/resourcepacks/classic/assets/computercraft/textures/gui/corners_advanced.png and b/src/main/resources/resourcepacks/classic/assets/computercraft/textures/gui/corners_advanced.png differ diff --git a/src/main/resources/resourcepacks/classic/assets/computercraft/textures/gui/corners_command.png b/src/main/resources/resourcepacks/classic/assets/computercraft/textures/gui/corners_command.png index 1bd3c311a..0a96d54ff 100644 Binary files a/src/main/resources/resourcepacks/classic/assets/computercraft/textures/gui/corners_command.png and b/src/main/resources/resourcepacks/classic/assets/computercraft/textures/gui/corners_command.png differ diff --git a/src/main/resources/resourcepacks/classic/assets/computercraft/textures/gui/corners_normal.png b/src/main/resources/resourcepacks/classic/assets/computercraft/textures/gui/corners_normal.png index be4cd974b..3627eaf3e 100644 Binary files a/src/main/resources/resourcepacks/classic/assets/computercraft/textures/gui/corners_normal.png and b/src/main/resources/resourcepacks/classic/assets/computercraft/textures/gui/corners_normal.png differ diff --git a/src/main/resources/resourcepacks/classic/pack.mcmeta b/src/main/resources/resourcepacks/classic/pack.mcmeta index 453fcc72a..0450878b5 100644 --- a/src/main/resources/resourcepacks/classic/pack.mcmeta +++ b/src/main/resources/resourcepacks/classic/pack.mcmeta @@ -1,6 +1,6 @@ { "pack": { - "pack_format": 6, + "pack_format": 7, "description": "The clasic look of ComputerCraft" } }