diff --git a/.gitpod.yml b/.gitpod.yml index d773abea4..e0500ab36 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -1,5 +1,5 @@ image: - file: .gitpod.Dockerfile + file: config/gitpod/Dockerfile ports: - port: 25565 @@ -7,12 +7,14 @@ ports: vscode: extensions: + - eamodio.gitlens + - github.vscode-pull-request-github - ms-azuretools.vscode-docker - redhat.java - richardwillis.vscode-gradle - vscjava.vscode-java-debug - vscode.github - tasks: - - init: ./gradlew + - name: Setup pre-commit hool + init: pre-commit install --config config/pre-commit/config.yml --allow-missing-config diff --git a/build.gradle b/build.gradle index fc9295d3c..8f07d44ba 100644 --- a/build.gradle +++ b/build.gradle @@ -93,7 +93,7 @@ dependencies { testImplementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.5.21' testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.5.1' - cctJavadoc 'cc.tweaked:cct-javadoc:1.4.1' + cctJavadoc 'cc.tweaked:cct-javadoc:1.4.2' } processResources { diff --git a/.gitpod.Dockerfile b/config/gitpod/Dockerfile similarity index 100% rename from .gitpod.Dockerfile rename to config/gitpod/Dockerfile diff --git a/doc/events/redstone.md b/doc/events/redstone.md index 44eda304a..0c199ee5d 100644 --- a/doc/events/redstone.md +++ b/doc/events/redstone.md @@ -2,7 +2,7 @@ module: [kind=event] redstone --- -The @{redstone} event is fired whenever any redstone inputs on the computer change. +The @{event!redstone} event is fired whenever any redstone inputs on the computer change. ## Example Prints a message when a redstone input changes: diff --git a/src/main/java/dan200/computercraft/ComputerCraft.java b/src/main/java/dan200/computercraft/ComputerCraft.java index 00f116d64..ca80ff1a9 100644 --- a/src/main/java/dan200/computercraft/ComputerCraft.java +++ b/src/main/java/dan200/computercraft/ComputerCraft.java @@ -3,12 +3,11 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft; import dan200.computercraft.core.apis.http.options.Action; import dan200.computercraft.core.apis.http.options.AddressRule; -import dan200.computercraft.shared.ComputerCraftRegistry.ModBlocks; +import dan200.computercraft.shared.Registry.ModBlocks; import dan200.computercraft.shared.common.ColourableRecipe; import dan200.computercraft.shared.computer.core.ClientComputerRegistry; import dan200.computercraft.shared.computer.core.ServerComputerRegistry; @@ -37,24 +36,20 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -import java.util.Arrays; -import java.util.Collections; import java.util.List; import java.util.concurrent.TimeUnit; -import static dan200.computercraft.shared.ComputerCraftRegistry.init; +import static dan200.computercraft.shared.Registry.init; public final class ComputerCraft implements ModInitializer { public static final String MOD_ID = "computercraft"; - // Configuration fields public static int computerSpaceLimit = 1000 * 1000; public static int floppySpaceLimit = 125 * 1000; public static int maximumFilesOpen = 128; public static boolean disableLua51Features = false; public static String defaultComputerSettings = ""; - public static boolean debugEnable = true; public static boolean logComputerErrors = true; public static boolean commandRequireCreative = true; @@ -64,10 +59,11 @@ public final class ComputerCraft implements ModInitializer public static boolean httpEnabled = true; public static boolean httpWebsocketEnabled = true; - public static List httpRules = Collections.unmodifiableList( Arrays.asList( + public static List httpRules = List.of( AddressRule.parse( "$private", null, Action.DENY.toPartial() ), AddressRule.parse( "*", null, Action.ALLOW.toPartial() ) - ) ); + ); + public static int httpMaxRequests = 16; public static int httpMaxWebsockets = 4; public static int httpDownloadBandwidth = 32 * 1024 * 1024; @@ -80,7 +76,6 @@ public final class ComputerCraft implements ModInitializer public static int modemHighAltitudeRangeDuringStorm = 384; 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; @@ -98,6 +93,7 @@ public final class ComputerCraft implements ModInitializer public static int pocketTermWidth = 26; public static int pocketTermHeight = 20; + public static int monitorWidth = 8; public static int monitorHeight = 6; diff --git a/src/main/java/dan200/computercraft/ComputerCraftAPIImpl.java b/src/main/java/dan200/computercraft/ComputerCraftAPIImpl.java index 068114705..ae3f4ebf7 100644 --- a/src/main/java/dan200/computercraft/ComputerCraftAPIImpl.java +++ b/src/main/java/dan200/computercraft/ComputerCraftAPIImpl.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft; import dan200.computercraft.api.ComputerCraftAPI.IComputerCraftAPI; @@ -35,7 +34,6 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.resources.ResourceLocation; -import net.minecraft.server.MinecraftServer; import net.minecraft.server.packs.resources.ReloadableResourceManager; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; @@ -59,36 +57,24 @@ private ComputerCraftAPIImpl() public static InputStream getResourceFile( String domain, String subPath ) { - MinecraftServer server = GameInstanceUtils.getServer(); - if( server != null ) + ReloadableResourceManager manager = (ReloadableResourceManager) ((MinecraftServerAccess) GameInstanceUtils.getServer()).callGetResourceManager(); + try { - ReloadableResourceManager manager = (ReloadableResourceManager) ((MinecraftServerAccess) server).callGetResourceManager(); - try - { - return manager.getResource( new ResourceLocation( domain, subPath ) ) - .getInputStream(); - } - catch( IOException ignored ) - { - return null; - } + return manager.getResource( new ResourceLocation( domain, subPath ) ).getInputStream(); + } + catch( IOException ignored ) + { + return null; } - return null; } @Nonnull @Override public String getInstalledVersion() { - if( version != null ) - { - return version; - } - return version = FabricLoader.getInstance() - .getModContainer( ComputerCraft.MOD_ID ) - .map( x -> x.getMetadata() - .getVersion() - .toString() ) + if( version != null ) return version; + return version = FabricLoader.getInstance().getModContainer( ComputerCraft.MOD_ID ) + .map( x -> x.getMetadata().getVersion().toString() ) .orElse( "unknown" ); } @@ -114,14 +100,9 @@ public IWritableMount createSaveDirMount( @Nonnull Level world, @Nonnull String @Override public IMount createResourceMount( @Nonnull String domain, @Nonnull String subPath ) { - MinecraftServer server = GameInstanceUtils.getServer(); - if( server != null ) - { - ReloadableResourceManager manager = (ReloadableResourceManager) ((MinecraftServerAccess) server).callGetResourceManager(); - ResourceMount mount = ResourceMount.get( domain, subPath, manager ); - return mount.exists( "" ) ? mount : null; - } - return null; + ReloadableResourceManager manager = (ReloadableResourceManager) ((MinecraftServerAccess) GameInstanceUtils.getServer()).callGetResourceManager(); + ResourceMount mount = ResourceMount.get( domain, subPath, manager ); + return mount.exists( "" ) ? mount : null; } @Override @@ -131,39 +112,39 @@ public void registerPeripheralProvider( @Nonnull IPeripheralProvider provider ) } @Override - public void registerTurtleUpgrade( @Nonnull ITurtleUpgrade upgrade ) + public void registerGenericSource( @Nonnull GenericSource source ) { - TurtleUpgrades.register( upgrade ); + GenericMethod.register( source ); } @Override - public void registerBundledRedstoneProvider( @Nonnull IBundledRedstoneProvider provider ) + public void registerTurtleUpgrade( @Nonnull ITurtleUpgrade upgrade ) { - BundledRedstone.register( provider ); + TurtleUpgrades.register( upgrade ); } @Override - public int getBundledRedstoneOutput( @Nonnull Level world, @Nonnull BlockPos pos, @Nonnull Direction side ) + public void registerPocketUpgrade( @Nonnull IPocketUpgrade upgrade ) { - return BundledRedstone.getDefaultOutput( world, pos, side ); + PocketUpgrades.register( upgrade ); } @Override - public void registerMediaProvider( @Nonnull IMediaProvider provider ) + public void registerBundledRedstoneProvider( @Nonnull IBundledRedstoneProvider provider ) { - MediaProviders.register( provider ); + BundledRedstone.register( provider ); } @Override - public void registerPocketUpgrade( @Nonnull IPocketUpgrade upgrade ) + public int getBundledRedstoneOutput( @Nonnull Level world, @Nonnull BlockPos pos, @Nonnull Direction side ) { - PocketUpgrades.register( upgrade ); + return BundledRedstone.getDefaultOutput( world, pos, side ); } @Override - public void registerGenericSource( @Nonnull GenericSource source ) + public void registerMediaProvider( @Nonnull IMediaProvider provider ) { - GenericMethod.register( source ); + MediaProviders.register( provider ); } @Nonnull diff --git a/src/main/java/dan200/computercraft/api/ComputerCraftAPI.java b/src/main/java/dan200/computercraft/api/ComputerCraftAPI.java index bef9f90d7..4f3751a12 100644 --- a/src/main/java/dan200/computercraft/api/ComputerCraftAPI.java +++ b/src/main/java/dan200/computercraft/api/ComputerCraftAPI.java @@ -36,19 +36,14 @@ */ public final class ComputerCraftAPI { + public static final String MOD_ID = "computercraft"; + @Nonnull public static String getInstalledVersion() { return getInstance().getInstalledVersion(); } - @Nonnull - @Deprecated - public static String getAPIVersion() - { - return getInstalledVersion(); - } - /** * Creates a numbered directory in a subfolder of the save directory for a given world, and returns that number. * @@ -139,9 +134,9 @@ public static void registerGenericSource( @Nonnull GenericSource source ) } /** - * Registers a new turtle turtle for use in ComputerCraft. After calling this, - * users should be able to craft Turtles with your new turtle. It is recommended to call - * this during the load() method of your mod. + * Registers a new turtle upgrade for use in ComputerCraft. After calling this, + * users should be able to craft Turtles with your upgrade's ItemStack. It is + * recommended to call this during the load() method of your mod. * * @param upgrade The turtle upgrade to register. * @see ITurtleUpgrade @@ -151,6 +146,19 @@ public static void registerTurtleUpgrade( @Nonnull ITurtleUpgrade upgrade ) getInstance().registerTurtleUpgrade( upgrade ); } + /** + * Registers a new pocket upgrade for use in ComputerCraft. After calling this, + * users should be able to craft pocket computers with your upgrade's ItemStack. It is + * recommended to call this during the load() method of your mod. + * + * @param upgrade The pocket upgrade to register. + * @see IPocketUpgrade + */ + public static void registerPocketUpgrade( @Nonnull IPocketUpgrade upgrade ) + { + getInstance().registerPocketUpgrade( upgrade ); + } + /** * Registers a bundled redstone provider to provide bundled redstone output for blocks. * @@ -188,11 +196,6 @@ public static void registerMediaProvider( @Nonnull IMediaProvider provider ) getInstance().registerMediaProvider( provider ); } - public static void registerPocketUpgrade( @Nonnull IPocketUpgrade upgrade ) - { - getInstance().registerPocketUpgrade( upgrade ); - } - /** * Attempt to get the game-wide wireless network. * @@ -273,14 +276,13 @@ public interface IComputerCraftAPI void registerTurtleUpgrade( @Nonnull ITurtleUpgrade upgrade ); + void registerPocketUpgrade( @Nonnull IPocketUpgrade upgrade ); + void registerBundledRedstoneProvider( @Nonnull IBundledRedstoneProvider provider ); int getBundledRedstoneOutput( @Nonnull Level world, @Nonnull BlockPos pos, @Nonnull Direction side ); void registerMediaProvider( @Nonnull IMediaProvider provider ); - - void registerPocketUpgrade( @Nonnull IPocketUpgrade upgrade ); - @Nonnull IPacketNetwork getWirelessNetwork(); diff --git a/src/main/java/dan200/computercraft/api/ComputerCraftTags.java b/src/main/java/dan200/computercraft/api/ComputerCraftTags.java new file mode 100644 index 000000000..4478bd034 --- /dev/null +++ b/src/main/java/dan200/computercraft/api/ComputerCraftTags.java @@ -0,0 +1,65 @@ +/* + * 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; + +import dan200.computercraft.ComputerCraft; +import net.fabricmc.fabric.api.tag.TagFactory; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.tags.Tag; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.Block; + +/** + * Tags provided by ComputerCraft. + */ +public class ComputerCraftTags +{ + public static class Items + { + public static final Tag.Named COMPUTER = make( "computer" ); + public static final Tag.Named TURTLE = make( "turtle" ); + public static final Tag.Named WIRED_MODEM = make( "wired_modem" ); + public static final Tag.Named MONITOR = make( "monitor" ); + + private static Tag.Named make( String name ) + { + return TagFactory.ITEM.create( new ResourceLocation( ComputerCraft.MOD_ID, name ) ); + } + } + + public static class Blocks + { + public static final Tag.Named COMPUTER = make( "computer" ); + public static final Tag.Named TURTLE = make( "turtle" ); + public static final Tag.Named WIRED_MODEM = make( "wired_modem" ); + public static final Tag.Named MONITOR = make( "monitor" ); + + /** + * Blocks which can be broken by any turtle tool. + */ + public static final Tag.Named TURTLE_ALWAYS_BREAKABLE = make( "turtle_always_breakable" ); + + /** + * Blocks which can be broken by the default shovel tool. + */ + public static final Tag.Named TURTLE_SHOVEL_BREAKABLE = make( "turtle_shovel_harvestable" ); + + /** + * Blocks which can be broken with the default sword tool. + */ + public static final Tag.Named TURTLE_SWORD_BREAKABLE = make( "turtle_sword_harvestable" ); + + /** + * Blocks which can be broken with the default hoe tool. + */ + public static final Tag.Named TURTLE_HOE_BREAKABLE = make( "turtle_hoe_harvestable" ); + + private static Tag.Named make( String name ) + { + return TagFactory.BLOCK.create( new ResourceLocation( ComputerCraft.MOD_ID, name ) ); + } + } +} diff --git a/src/main/java/dan200/computercraft/api/client/TransformedModel.java b/src/main/java/dan200/computercraft/api/client/TransformedModel.java index 5a439da61..da991ccbe 100644 --- a/src/main/java/dan200/computercraft/api/client/TransformedModel.java +++ b/src/main/java/dan200/computercraft/api/client/TransformedModel.java @@ -3,7 +3,6 @@ * 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.client; import com.mojang.blaze3d.vertex.PoseStack; diff --git a/src/main/java/dan200/computercraft/api/peripheral/IPeripheral.java b/src/main/java/dan200/computercraft/api/peripheral/IPeripheral.java index eddc30861..0efb46ded 100644 --- a/src/main/java/dan200/computercraft/api/peripheral/IPeripheral.java +++ b/src/main/java/dan200/computercraft/api/peripheral/IPeripheral.java @@ -9,6 +9,8 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.util.Collections; +import java.util.Set; /** * The interface that defines a peripheral. @@ -30,6 +32,18 @@ public interface IPeripheral @Nonnull String getType(); + /** + * Return additional types/traits associated with this object. + * + * @return A collection of additional object traits. + * @see PeripheralType#getAdditionalTypes() + */ + @Nonnull + default Set getAdditionalTypes() + { + return Collections.emptySet(); + } + /** * Is called when when a computer is attaching to the peripheral. * diff --git a/src/main/java/dan200/computercraft/api/peripheral/PeripheralType.java b/src/main/java/dan200/computercraft/api/peripheral/PeripheralType.java index 2780534c2..c80526eda 100644 --- a/src/main/java/dan200/computercraft/api/peripheral/PeripheralType.java +++ b/src/main/java/dan200/computercraft/api/peripheral/PeripheralType.java @@ -6,9 +6,13 @@ package dan200.computercraft.api.peripheral; import com.google.common.base.Strings; +import com.google.common.collect.ImmutableSet; import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.util.Collection; +import java.util.Collections; +import java.util.Set; /** * The type of a {@link GenericPeripheral}. @@ -18,13 +22,19 @@ */ public final class PeripheralType { - private static final PeripheralType UNTYPED = new PeripheralType( null ); + private static final PeripheralType UNTYPED = new PeripheralType( null, Collections.emptySet() ); private final String type; + private final Set additionalTypes; - public PeripheralType( String type ) + public PeripheralType( String type, Set additionalTypes ) { this.type = type; + this.additionalTypes = additionalTypes; + if( additionalTypes.contains( null ) ) + { + throw new IllegalArgumentException( "All additional types must be non-null" ); + } } /** @@ -46,7 +56,55 @@ public static PeripheralType untyped() public static PeripheralType ofType( @Nonnull String type ) { if( Strings.isNullOrEmpty( type ) ) throw new IllegalArgumentException( "type cannot be null or empty" ); - return new PeripheralType( type ); + return new PeripheralType( type, Collections.emptySet() ); + } + + /** + * Create a new non-empty peripheral type with additional traits. + * + * @param type The name of the type. + * @param additionalTypes Additional types, or "traits" of this peripheral. For instance, {@literal "inventory"}. + * @return The constructed peripheral type. + */ + public static PeripheralType ofType( @Nonnull String type, Collection additionalTypes ) + { + if( Strings.isNullOrEmpty( type ) ) throw new IllegalArgumentException( "type cannot be null or empty" ); + return new PeripheralType( type, ImmutableSet.copyOf( additionalTypes ) ); + } + + /** + * Create a new non-empty peripheral type with additional traits. + * + * @param type The name of the type. + * @param additionalTypes Additional types, or "traits" of this peripheral. For instance, {@literal "inventory"}. + * @return The constructed peripheral type. + */ + public static PeripheralType ofType( @Nonnull String type, @Nonnull String... additionalTypes ) + { + if( Strings.isNullOrEmpty( type ) ) throw new IllegalArgumentException( "type cannot be null or empty" ); + return new PeripheralType( type, ImmutableSet.copyOf( additionalTypes ) ); + } + + /** + * Create a new peripheral type with no primary type but additional traits. + * + * @param additionalTypes Additional types, or "traits" of this peripheral. For instance, {@literal "inventory"}. + * @return The constructed peripheral type. + */ + public static PeripheralType ofAdditional( Collection additionalTypes ) + { + return new PeripheralType( null, ImmutableSet.copyOf( additionalTypes ) ); + } + + /** + * Create a new peripheral type with no primary type but additional traits. + * + * @param additionalTypes Additional types, or "traits" of this peripheral. For instance, {@literal "inventory"}. + * @return The constructed peripheral type. + */ + public static PeripheralType ofAdditional( @Nonnull String... additionalTypes ) + { + return new PeripheralType( null, ImmutableSet.copyOf( additionalTypes ) ); } /** @@ -59,4 +117,15 @@ public String getPrimaryType() { return type; } + + /** + * Get any additional types or "traits" of this peripheral. These effectively act as a standard set of interfaces + * a peripheral might have. + * + * @return All additional types. + */ + public Set getAdditionalTypes() + { + return additionalTypes; + } } diff --git a/src/main/java/dan200/computercraft/api/pocket/IPocketUpgrade.java b/src/main/java/dan200/computercraft/api/pocket/IPocketUpgrade.java index 4e39f0b38..34f4b8cb5 100644 --- a/src/main/java/dan200/computercraft/api/pocket/IPocketUpgrade.java +++ b/src/main/java/dan200/computercraft/api/pocket/IPocketUpgrade.java @@ -6,7 +6,7 @@ package dan200.computercraft.api.pocket; import dan200.computercraft.api.ComputerCraftAPI; -import dan200.computercraft.api.IUpgradeBase; +import dan200.computercraft.api.upgrades.IUpgradeBase; import dan200.computercraft.api.peripheral.IPeripheral; import net.minecraft.world.level.Level; diff --git a/src/main/java/dan200/computercraft/api/turtle/AbstractTurtleUpgrade.java b/src/main/java/dan200/computercraft/api/turtle/AbstractTurtleUpgrade.java index 6d075b5fc..45f2c0886 100644 --- a/src/main/java/dan200/computercraft/api/turtle/AbstractTurtleUpgrade.java +++ b/src/main/java/dan200/computercraft/api/turtle/AbstractTurtleUpgrade.java @@ -3,18 +3,13 @@ * 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.turtle; -import dan200.computercraft.shared.util.NonNullSupplier; -import net.minecraft.Util; +import dan200.computercraft.api.upgrades.IUpgradeBase; import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.ItemLike; import javax.annotation.Nonnull; -import java.util.function.Supplier; /** * A base class for {@link ITurtleUpgrade}s. @@ -26,9 +21,9 @@ public abstract class AbstractTurtleUpgrade implements ITurtleUpgrade private final ResourceLocation id; private final TurtleUpgradeType type; private final String adjective; - private final NonNullSupplier stack; + private final ItemStack stack; - protected AbstractTurtleUpgrade( ResourceLocation id, TurtleUpgradeType type, String adjective, NonNullSupplier stack ) + protected AbstractTurtleUpgrade( ResourceLocation id, TurtleUpgradeType type, String adjective, ItemStack stack ) { this.id = id; this.type = type; @@ -36,39 +31,9 @@ protected AbstractTurtleUpgrade( ResourceLocation id, TurtleUpgradeType type, St this.stack = stack; } - protected AbstractTurtleUpgrade( ResourceLocation id, TurtleUpgradeType type, NonNullSupplier stack ) - { - this( id, type, Util.makeDescriptionId( "upgrade", id ) + ".adjective", stack ); - } - - protected AbstractTurtleUpgrade( ResourceLocation id, TurtleUpgradeType type, String adjective, ItemStack stack ) - { - this( id, type, adjective, () -> stack ); - } - protected AbstractTurtleUpgrade( ResourceLocation id, TurtleUpgradeType type, ItemStack stack ) { - this( id, type, () -> stack ); - } - - protected AbstractTurtleUpgrade( ResourceLocation id, TurtleUpgradeType type, String adjective, ItemLike item ) - { - this( id, type, adjective, new CachedStack( () -> item ) ); - } - - protected AbstractTurtleUpgrade( ResourceLocation id, TurtleUpgradeType type, ItemLike item ) - { - this( id, type, new CachedStack( () -> item ) ); - } - - protected AbstractTurtleUpgrade( ResourceLocation id, TurtleUpgradeType type, String adjective, Supplier item ) - { - this( id, type, adjective, new CachedStack( item ) ); - } - - protected AbstractTurtleUpgrade( ResourceLocation id, TurtleUpgradeType type, Supplier item ) - { - this( id, type, new CachedStack( item ) ); + this( id, type, IUpgradeBase.getDefaultAdjective( id ), stack ); } @Nonnull @@ -96,32 +61,6 @@ public final TurtleUpgradeType getType() @Override public final ItemStack getCraftingItem() { - 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 ); - } + return stack; } } diff --git a/src/main/java/dan200/computercraft/api/turtle/ITurtleUpgrade.java b/src/main/java/dan200/computercraft/api/turtle/ITurtleUpgrade.java index 52acab992..27a4b0fee 100644 --- a/src/main/java/dan200/computercraft/api/turtle/ITurtleUpgrade.java +++ b/src/main/java/dan200/computercraft/api/turtle/ITurtleUpgrade.java @@ -6,7 +6,7 @@ package dan200.computercraft.api.turtle; import dan200.computercraft.api.ComputerCraftAPI; -import dan200.computercraft.api.IUpgradeBase; +import dan200.computercraft.api.upgrades.IUpgradeBase; import dan200.computercraft.api.client.TransformedModel; import dan200.computercraft.api.peripheral.IPeripheral; import net.fabricmc.api.EnvType; diff --git a/src/main/java/dan200/computercraft/api/IUpgradeBase.java b/src/main/java/dan200/computercraft/api/upgrades/IUpgradeBase.java similarity index 86% rename from src/main/java/dan200/computercraft/api/IUpgradeBase.java rename to src/main/java/dan200/computercraft/api/upgrades/IUpgradeBase.java index d87c404bb..06a83cb1b 100644 --- a/src/main/java/dan200/computercraft/api/IUpgradeBase.java +++ b/src/main/java/dan200/computercraft/api/upgrades/IUpgradeBase.java @@ -3,10 +3,11 @@ * 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; +package dan200.computercraft.api.upgrades; import dan200.computercraft.api.pocket.IPocketUpgrade; import dan200.computercraft.api.turtle.ITurtleUpgrade; +import net.minecraft.Util; import net.minecraft.nbt.CompoundTag; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.ItemStack; @@ -81,4 +82,18 @@ default boolean isItemSuitable( @Nonnull ItemStack stack ) if( craftingShareTag == null ) return shareTag.isEmpty(); return shareTag.equals( craftingShareTag ); } + + /** + * Get a suitable default unlocalised adjective for an upgrade ID. This converts "modid:some_upgrade" to + * "upgrade.modid.some_upgrade.adjective". + * + * @param id The upgrade ID. + * @return The generated adjective. + * @see #getUnlocalisedAdjective() + */ + @Nonnull + static String getDefaultAdjective( @Nonnull ResourceLocation id ) + { + return Util.makeDescriptionId( "upgrade", id ) + ".adjective"; + } } diff --git a/src/main/java/dan200/computercraft/client/ClientRegistry.java b/src/main/java/dan200/computercraft/client/ClientRegistry.java index 2e1b3074a..657850018 100644 --- a/src/main/java/dan200/computercraft/client/ClientRegistry.java +++ b/src/main/java/dan200/computercraft/client/ClientRegistry.java @@ -3,11 +3,10 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.client; import dan200.computercraft.ComputerCraft; -import dan200.computercraft.shared.ComputerCraftRegistry; +import dan200.computercraft.shared.Registry; import dan200.computercraft.shared.common.IColouredItem; import dan200.computercraft.shared.media.items.ItemDisk; import dan200.computercraft.shared.media.items.ItemTreasureDisk; @@ -34,6 +33,7 @@ public final class ClientRegistry { private static final String[] EXTRA_MODELS = new String[] { + // Turtle upgrades "turtle_modem_normal_off_left", "turtle_modem_normal_on_left", "turtle_modem_normal_off_right", @@ -49,6 +49,7 @@ public final class ClientRegistry "turtle_speaker_upgrade_left", "turtle_speaker_upgrade_right", + // Turtle block renderer "turtle_colour", "turtle_elf_overlay", }; @@ -85,10 +86,10 @@ public static void onItemColours() { ColorProviderRegistry.ITEM.register( ( stack, layer ) -> { return layer == 1 ? ((ItemDisk) stack.getItem()).getColour( stack ) : 0xFFFFFF; - }, ComputerCraftRegistry.ModItems.DISK ); + }, Registry.ModItems.DISK ); ColorProviderRegistry.ITEM.register( ( stack, layer ) -> layer == 1 ? ItemTreasureDisk.getColour( stack ) : 0xFFFFFF, - ComputerCraftRegistry.ModItems.TREASURE_DISK ); + Registry.ModItems.TREASURE_DISK ); ColorProviderRegistry.ITEM.register( ( stack, layer ) -> { switch( layer ) @@ -102,12 +103,12 @@ public static void onItemColours() int light = ItemPocketComputer.getLightState( stack ); return light == -1 ? Colour.BLACK.getHex() : light; } - }, ComputerCraftRegistry.ModItems.POCKET_COMPUTER_NORMAL, ComputerCraftRegistry.ModItems.POCKET_COMPUTER_ADVANCED ); + }, Registry.ModItems.POCKET_COMPUTER_NORMAL, Registry.ModItems.POCKET_COMPUTER_ADVANCED ); // Setup turtle colours ColorProviderRegistry.ITEM.register( ( stack, tintIndex ) -> tintIndex == 0 ? ((IColouredItem) stack.getItem()).getColour( stack ) : 0xFFFFFF, - ComputerCraftRegistry.ModBlocks.TURTLE_NORMAL, - ComputerCraftRegistry.ModBlocks.TURTLE_ADVANCED ); + Registry.ModBlocks.TURTLE_NORMAL, + Registry.ModBlocks.TURTLE_ADVANCED ); } private static BakedModel bake( ModelBakery loader, UnbakedModel model, ResourceLocation identifier ) diff --git a/src/main/java/dan200/computercraft/client/ClientTableFormatter.java b/src/main/java/dan200/computercraft/client/ClientTableFormatter.java index d401e6566..d4a4ba62d 100644 --- a/src/main/java/dan200/computercraft/client/ClientTableFormatter.java +++ b/src/main/java/dan200/computercraft/client/ClientTableFormatter.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; import dan200.computercraft.fabric.mixin.ChatComponentAccess; @@ -25,17 +24,19 @@ public class ClientTableFormatter implements TableFormatter { public static final ClientTableFormatter INSTANCE = new ClientTableFormatter(); - private static Int2IntOpenHashMap lastHeights = new Int2IntOpenHashMap(); + private static final Int2IntOpenHashMap lastHeights = new Int2IntOpenHashMap(); + + private static Font renderer() + { + return Minecraft.getInstance().font; + } @Override @Nullable public Component getPadding( Component component, int width ) { int extraWidth = width - getWidth( component ); - if( extraWidth <= 0 ) - { - return null; - } + if( extraWidth <= 0 ) return null; Font renderer = renderer(); @@ -46,11 +47,6 @@ public Component getPadding( Component component, int width ) return ChatHelpers.coloured( StringUtils.repeat( ' ', spaces ) + StringUtils.repeat( (char) 712, extra ), ChatFormatting.GRAY ); } - private static Font renderer() - { - return Minecraft.getInstance().font; - } - @Override public int getColumnPadding() { @@ -71,7 +67,7 @@ public void writeLine( int id, Component component ) // TODO: Trim the text if it goes over the allowed length // int maxWidth = MathHelper.floor( chat.getChatWidth() / chat.getScale() ); - // List list = RenderComponentsUtil.func_238505_a_( component, maxWidth, mc.fontRenderer ); + // List list = RenderComponentsUtil.wrapComponents( component, maxWidth, mc.fontRenderer ); // if( !list.isEmpty() ) chat.printChatMessageWithOptionalDeletion( list.get( 0 ), id ); ((ChatComponentAccess) chat).callAddMessage( component, id ); } @@ -86,10 +82,7 @@ public int display( TableBuilder table ) int height = TableFormatter.super.display( table ); lastHeights.put( table.getId(), height ); - for( int i = height; i < lastHeight; i++ ) - { - ((ChatComponentAccess) chat).callRemoveById( i + table.getId() ); - } + for( int i = height; i < lastHeight; i++ ) ((ChatComponentAccess) chat).callRemoveById( i + table.getId() ); return height; } } diff --git a/src/main/java/dan200/computercraft/client/FrameInfo.java b/src/main/java/dan200/computercraft/client/FrameInfo.java index 600f44e05..ec8bb6500 100644 --- a/src/main/java/dan200/computercraft/client/FrameInfo.java +++ b/src/main/java/dan200/computercraft/client/FrameInfo.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; import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents; diff --git a/src/main/java/dan200/computercraft/client/gui/ComputerScreenBase.java b/src/main/java/dan200/computercraft/client/gui/ComputerScreenBase.java index e9c54163d..7d6d42bc6 100644 --- a/src/main/java/dan200/computercraft/client/gui/ComputerScreenBase.java +++ b/src/main/java/dan200/computercraft/client/gui/ComputerScreenBase.java @@ -36,7 +36,6 @@ public abstract class ComputerScreenBase extends AbstractContainerScreen { - private static final Component OK = new TranslatableComponent( "gui.ok" ); private static final Component CANCEL = new TranslatableComponent( "gui.cancel" ); private static final Component OVERWRITE = new TranslatableComponent( "gui.computercraft.upload.overwrite_button" ); @@ -94,6 +93,7 @@ public final boolean keyPressed( int key, int scancode, int modifiers ) return super.keyPressed( key, scancode, modifiers ); } + @Override public final void render( @Nonnull PoseStack stack, int mouseX, int mouseY, float partialTicks ) { @@ -105,9 +105,11 @@ public final void render( @Nonnull PoseStack stack, int mouseX, int mouseY, floa @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 ); + return (getFocused() != null && getFocused().mouseDragged( x, y, button, deltaX, deltaY )) + || super.mouseDragged( x, y, button, deltaX, deltaY ); } + @Override protected void renderLabels( @Nonnull PoseStack transform, int mouseX, int mouseY ) { @@ -207,7 +209,7 @@ public void uploadResult( UploadResult result, Component message ) private void continueUpload() { - if( minecraft.screen instanceof OptionScreen ) ((OptionScreen) minecraft.screen).disable(); + if( minecraft.screen instanceof OptionScreen screen ) screen.disable(); NetworkHandler.sendToServer( new ContinueUploadMessage( computer.getInstanceID(), true ) ); } @@ -224,5 +226,4 @@ private void alert( Component title, Component message ) () -> minecraft.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 d91b45ab1..f23467159 100644 --- a/src/main/java/dan200/computercraft/client/gui/FixedWidthFontRenderer.java +++ b/src/main/java/dan200/computercraft/client/gui/FixedWidthFontRenderer.java @@ -3,19 +3,17 @@ * 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.vertex.Tesselator; import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.math.Matrix4f; -import com.mojang.math.Transformation; 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.Minecraft; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.resources.ResourceLocation; @@ -24,60 +22,99 @@ import static dan200.computercraft.client.render.RenderTypes.FULL_BRIGHT_LIGHTMAP; +/** + * Handles rendering fixed width text and computer terminals. + * + * This class has several modes of usage: + *
    + *
  • {@link #drawString}: Drawing basic text without a terminal (such as for printouts). Unlike the other methods, + * this accepts a lightmap coordinate as, unlike terminals, printed pages render fullbright.
  • + *
  • {@link #drawTerminalWithoutCursor}/{@link #drawCursor}: Draw a terminal without a cursor and then draw the cursor + * separately. This is used by the monitor renderer to render the terminal to a VBO and draw the cursor dynamically. + *
  • + *
  • {@link #drawTerminal}: Draw a terminal with a cursor. This is used by the various computer GUIs to render the + * whole term.
  • + *
  • {@link #drawBlocker}: When rendering a terminal using {@link RenderTypes#TERMINAL_WITHOUT_DEPTH} you need to + * render an additional "depth blocker" on top of the monitor.
  • + *
+ */ public final class FixedWidthFontRenderer { + public static final ResourceLocation FONT = new ResourceLocation( "computercraft", "textures/gui/term_font.png" ); + public static final int FONT_HEIGHT = 9; public static final int FONT_WIDTH = 6; public static final float WIDTH = 256.0f; + public static final float BACKGROUND_START = (WIDTH - 6.0f) / WIDTH; public static final float BACKGROUND_END = (WIDTH - 4.0f) / WIDTH; - private static final Matrix4f IDENTITY = Transformation.identity() - .getMatrix(); - public static final ResourceLocation FONT = new ResourceLocation( "computercraft", "textures/gui/term_font.png" ); - private FixedWidthFontRenderer() { } - 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, int light ) + public static float toGreyscale( double[] rgb ) { - if( backgroundColour != null ) + return (float) ((rgb[0] + rgb[1] + rgb[2]) / 3); + } + + public static int getColour( char c, Colour def ) + { + return 15 - Terminal.getColour( c, def ); + } + + 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 == ' ' ) return; + + int column = index % 16; + int row = index / 16; + + int xStart = 1 + column * (FONT_WIDTH + 2); + int yStart = 1 + row * (FONT_HEIGHT + 2); + + buffer.vertex( transform, x, y, 0f ).color( r, g, b, 1.0f ).uv( xStart / WIDTH, yStart / WIDTH ).uv2( light ).endVertex(); + buffer.vertex( transform, x, y + FONT_HEIGHT, 0f ).color( r, g, b, 1.0f ).uv( xStart / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ).uv2( light ).endVertex(); + buffer.vertex( transform, x + FONT_WIDTH, y, 0f ).color( r, g, b, 1.0f ).uv( (xStart + FONT_WIDTH) / WIDTH, yStart / WIDTH ).uv2( light ).endVertex(); + buffer.vertex( transform, x + FONT_WIDTH, y, 0f ).color( r, g, b, 1.0f ).uv( (xStart + FONT_WIDTH) / WIDTH, yStart / WIDTH ).uv2( light ).endVertex(); + buffer.vertex( transform, x, y + FONT_HEIGHT, 0f ).color( r, g, b, 1.0f ).uv( xStart / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ).uv2( light ).endVertex(); + buffer.vertex( transform, x + FONT_WIDTH, y + FONT_HEIGHT, 0f ).color( r, g, b, 1.0f ).uv( (xStart + FONT_WIDTH) / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ).uv2( light ).endVertex(); + } + + private static void drawQuad( Matrix4f transform, VertexConsumer buffer, float x, float y, float width, float height, float r, float g, float b ) + { + buffer.vertex( transform, x, y, 0 ).color( r, g, b, 1.0f ).uv( BACKGROUND_START, BACKGROUND_START ).endVertex(); + buffer.vertex( transform, x, y + height, 0 ).color( r, g, b, 1.0f ).uv( BACKGROUND_START, BACKGROUND_END ).endVertex(); + buffer.vertex( transform, x + width, y, 0 ).color( r, g, b, 1.0f ).uv( BACKGROUND_END, BACKGROUND_START ).endVertex(); + buffer.vertex( transform, x + width, y, 0 ).color( r, g, b, 1.0f ).uv( BACKGROUND_END, BACKGROUND_START ).endVertex(); + buffer.vertex( transform, x, y + height, 0 ).color( r, g, b, 1.0f ).uv( BACKGROUND_START, BACKGROUND_END ).endVertex(); + buffer.vertex( transform, x + width, y + height, 0 ).color( r, g, b, 1.0f ).uv( BACKGROUND_END, BACKGROUND_END ).endVertex(); + } + + private static void drawQuad( Matrix4f transform, VertexConsumer buffer, float x, float y, float width, float height, Palette palette, boolean greyscale, char colourIndex ) + { + double[] colour = palette.getColour( getColour( colourIndex, Colour.BLACK ) ); + float r, g, b; + if( greyscale ) { - drawBackground( transform, renderer, x, y, backgroundColour, palette, greyscale, leftMarginSize, rightMarginSize, FONT_HEIGHT ); + r = g = b = toGreyscale( colour ); } - - for( int i = 0; i < text.length(); i++ ) + else { - double[] colour = palette.getColour( getColour( textColour.charAt( i ), Colour.BLACK ) ); - float r, g, b; - if( greyscale ) - { - r = g = b = toGreyscale( colour ); - } - else - { - r = (float) colour[0]; - g = (float) colour[1]; - b = (float) colour[2]; - } - - // Draw char - int index = text.charAt( i ); - if( index > 255 ) - { - index = '?'; - } - drawChar( transform, renderer, x + i * FONT_WIDTH, y, index, r, g, b, light ); + r = (float) colour[0]; + g = (float) colour[1]; + b = (float) colour[2]; } + drawQuad( transform, buffer, x, y, width, height, r, g, b ); } - private static void drawBackground( @Nonnull Matrix4f transform, @Nonnull VertexConsumer renderer, float x, float y, - @Nonnull TextBuffer backgroundColour, @Nonnull Palette palette, boolean greyscale, float leftMarginSize, - float rightMarginSize, float height ) + private static void drawBackground( + @Nonnull Matrix4f transform, @Nonnull VertexConsumer renderer, float x, float y, + @Nonnull TextBuffer backgroundColour, @Nonnull Palette palette, boolean greyscale, + float leftMarginSize, float rightMarginSize, float height + ) { if( leftMarginSize > 0 ) { @@ -86,15 +123,7 @@ private static void drawBackground( @Nonnull Matrix4f transform, @Nonnull Vertex if( rightMarginSize > 0 ) { - drawQuad( transform, - renderer, - x + backgroundColour.length() * FONT_WIDTH, - y, - rightMarginSize, - height, - palette, - greyscale, - backgroundColour.charAt( backgroundColour.length() - 1 ) ); + drawQuad( transform, renderer, x + backgroundColour.length() * FONT_WIDTH, y, rightMarginSize, height, palette, greyscale, backgroundColour.charAt( backgroundColour.length() - 1 ) ); } // Batch together runs of identical background cells. @@ -103,10 +132,7 @@ private static void drawBackground( @Nonnull Matrix4f transform, @Nonnull Vertex for( int i = 0; i < backgroundColour.length(); i++ ) { char colourIndex = backgroundColour.charAt( i ); - if( colourIndex == blockColour ) - { - continue; - } + if( colourIndex == blockColour ) continue; if( blockColour != '\0' ) { @@ -119,170 +145,81 @@ private static void drawBackground( @Nonnull Matrix4f transform, @Nonnull Vertex if( blockColour != '\0' ) { - drawQuad( transform, - renderer, - x + blockStart * FONT_WIDTH, - y, - FONT_WIDTH * (backgroundColour.length() - blockStart), - height, - palette, - greyscale, - blockColour ); + drawQuad( transform, renderer, x + blockStart * FONT_WIDTH, y, FONT_WIDTH * (backgroundColour.length() - blockStart), height, palette, greyscale, blockColour ); } } - public static int getColour( char c, Colour def ) + 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, int light + ) { - return 15 - Terminal.getColour( c, def ); - } - - public static float toGreyscale( double[] rgb ) - { - 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, int light ) - { - // Short circuit to avoid the common case - the texture should be blank here after all. - if( index == '\0' || index == ' ' ) + if( backgroundColour != null ) { - return; + drawBackground( transform, renderer, x, y, backgroundColour, palette, greyscale, leftMarginSize, rightMarginSize, FONT_HEIGHT ); } - int column = index % 16; - int row = index / 16; - - int xStart = 1 + column * (FONT_WIDTH + 2); - int yStart = 1 + row * (FONT_HEIGHT + 2); - - buffer.vertex( transform, x, y, 0f ) - .color( r, g, b, 1.0f ) - .uv( xStart / WIDTH, yStart / WIDTH ) - .uv2( light ) - .endVertex(); - buffer.vertex( transform, x, y + FONT_HEIGHT, 0f ) - .color( r, g, b, 1.0f ) - .uv( xStart / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ) - .uv2( light ) - .endVertex(); - buffer.vertex( transform, x + FONT_WIDTH, y, 0f ) - .color( r, g, b, 1.0f ) - .uv( (xStart + FONT_WIDTH) / WIDTH, yStart / WIDTH ) - .uv2( light ) - .endVertex(); - buffer.vertex( transform, x + FONT_WIDTH, y, 0f ) - .color( r, g, b, 1.0f ) - .uv( (xStart + FONT_WIDTH) / WIDTH, yStart / WIDTH ) - .uv2( light ) - .endVertex(); - buffer.vertex( transform, x, y + FONT_HEIGHT, 0f ) - .color( r, g, b, 1.0f ) - .uv( xStart / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ) - .uv2( light ) - .endVertex(); - buffer.vertex( transform, x + FONT_WIDTH, y + FONT_HEIGHT, 0f ) - .color( r, g, b, 1.0f ) - .uv( (xStart + FONT_WIDTH) / WIDTH, (yStart + FONT_HEIGHT) / WIDTH ) - .uv2( light ) - .endVertex(); - } - - private static void drawQuad( Matrix4f transform, VertexConsumer buffer, float x, float y, float width, float height, Palette palette, - boolean greyscale, char colourIndex ) - { - double[] colour = palette.getColour( getColour( colourIndex, Colour.BLACK ) ); - float r, g, b; - if( greyscale ) - { - r = g = b = toGreyscale( colour ); - } - else + for( int i = 0; i < text.length(); i++ ) { - r = (float) colour[0]; - g = (float) colour[1]; - b = (float) colour[2]; - } + double[] colour = palette.getColour( getColour( textColour.charAt( i ), Colour.BLACK ) ); + float r, g, b; + if( greyscale ) + { + r = g = b = toGreyscale( colour ); + } + else + { + r = (float) colour[0]; + g = (float) colour[1]; + b = (float) colour[2]; + } - drawQuad( transform, buffer, x, y, width, height, r, g, b ); - } + // Draw char + int index = text.charAt( i ); + if( index > 255 ) index = '?'; + drawChar( transform, renderer, x + i * FONT_WIDTH, y, index, r, g, b, light ); + } - private static void drawQuad( Matrix4f transform, VertexConsumer buffer, float x, float y, float width, float height, float r, float g, float b ) - { - buffer.vertex( transform, x, y, 0 ) - .color( r, g, b, 1.0f ) - .uv( BACKGROUND_START, BACKGROUND_START ) - .endVertex(); - buffer.vertex( transform, x, y + height, 0 ) - .color( r, g, b, 1.0f ) - .uv( BACKGROUND_START, BACKGROUND_END ) - .endVertex(); - buffer.vertex( transform, x + width, y, 0 ) - .color( r, g, b, 1.0f ) - .uv( BACKGROUND_END, BACKGROUND_START ) - .endVertex(); - buffer.vertex( transform, x + width, y, 0 ) - .color( r, g, b, 1.0f ) - .uv( BACKGROUND_END, BACKGROUND_START ) - .endVertex(); - buffer.vertex( transform, x, y + height, 0 ) - .color( r, g, b, 1.0f ) - .uv( BACKGROUND_START, BACKGROUND_END ) - .endVertex(); - buffer.vertex( transform, x + width, y + height, 0 ) - .color( r, g, b, 1.0f ) - .uv( BACKGROUND_END, BACKGROUND_END ) - .endVertex(); } - public static void drawTerminalWithoutCursor( @Nonnull Matrix4f transform, @Nonnull VertexConsumer buffer, float x, float y, - @Nonnull Terminal terminal, boolean greyscale, float topMarginSize, float bottomMarginSize, - float leftMarginSize, float rightMarginSize ) + public static void drawTerminalWithoutCursor( + @Nonnull Matrix4f transform, @Nonnull VertexConsumer buffer, float x, float y, + @Nonnull Terminal terminal, boolean greyscale, + float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize + ) { Palette palette = terminal.getPalette(); int height = terminal.getHeight(); // Top and bottom margins - drawBackground( transform, - buffer, - x, - y - topMarginSize, - terminal.getBackgroundColourLine( 0 ), - palette, - greyscale, - leftMarginSize, - rightMarginSize, - topMarginSize ); - - drawBackground( transform, - buffer, - x, - y + height * FONT_HEIGHT, - terminal.getBackgroundColourLine( height - 1 ), - palette, - greyscale, - leftMarginSize, - rightMarginSize, - bottomMarginSize ); + drawBackground( + transform, buffer, x, y - topMarginSize, + terminal.getBackgroundColourLine( 0 ), palette, greyscale, + leftMarginSize, rightMarginSize, topMarginSize + ); + + drawBackground( + transform, buffer, x, y + height * FONT_HEIGHT, + terminal.getBackgroundColourLine( height - 1 ), palette, greyscale, + leftMarginSize, rightMarginSize, bottomMarginSize + ); // The main text for( int i = 0; i < height; i++ ) { - drawString( transform, - buffer, - x, - y + FixedWidthFontRenderer.FONT_HEIGHT * i, - terminal.getLine( i ), - terminal.getTextColourLine( i ), - terminal.getBackgroundColourLine( i ), - palette, - greyscale, - leftMarginSize, - rightMarginSize, FULL_BRIGHT_LIGHTMAP ); + drawString( + transform, buffer, x, y + FixedWidthFontRenderer.FONT_HEIGHT * i, + terminal.getLine( i ), terminal.getTextColourLine( i ), terminal.getBackgroundColourLine( i ), + palette, greyscale, leftMarginSize, rightMarginSize, FULL_BRIGHT_LIGHTMAP + ); } } - public static void drawCursor( @Nonnull Matrix4f transform, @Nonnull VertexConsumer buffer, float x, float y, @Nonnull Terminal terminal, - boolean greyscale ) + public static void drawCursor( + @Nonnull Matrix4f transform, @Nonnull VertexConsumer buffer, float x, float y, + @Nonnull Terminal terminal, boolean greyscale + ) { Palette palette = terminal.getPalette(); int width = terminal.getWidth(); @@ -309,52 +246,40 @@ public static void drawCursor( @Nonnull Matrix4f transform, @Nonnull VertexConsu } } - public static void drawTerminal( @Nonnull Matrix4f transform, @Nonnull VertexConsumer buffer, float x, float y, @Nonnull Terminal terminal, - boolean greyscale, float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize ) + public static void drawTerminal( + @Nonnull Matrix4f transform, @Nonnull VertexConsumer buffer, float x, float y, + @Nonnull Terminal terminal, boolean greyscale, + float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize + ) { drawTerminalWithoutCursor( transform, buffer, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize ); drawCursor( transform, buffer, x, y, terminal, greyscale ); } - public static void drawTerminal( @Nonnull Matrix4f transform, float x, float y, @Nonnull Terminal terminal, boolean greyscale, float topMarginSize, - float bottomMarginSize, float leftMarginSize, float rightMarginSize ) + public static void drawTerminal( + @Nonnull Matrix4f transform, float x, float y, @Nonnull Terminal terminal, boolean greyscale, + float topMarginSize, float bottomMarginSize, float leftMarginSize, float rightMarginSize + ) { - MultiBufferSource.BufferSource renderer = Minecraft.getInstance() - .renderBuffers() - .bufferSource(); + MultiBufferSource.BufferSource renderer = MultiBufferSource.immediate( Tesselator.getInstance().getBuilder() ); VertexConsumer buffer = renderer.getBuffer( RenderTypes.TERMINAL_WITH_DEPTH ); drawTerminal( transform, buffer, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize ); renderer.endBatch(); } - public static void drawTerminal( float x, float y, @Nonnull Terminal terminal, boolean greyscale, float topMarginSize, float bottomMarginSize, - float leftMarginSize, float rightMarginSize ) - { - drawTerminal( IDENTITY, x, y, terminal, greyscale, topMarginSize, bottomMarginSize, leftMarginSize, rightMarginSize ); - } - - public static void drawEmptyTerminal( float x, float y, float width, float height ) + public static void drawEmptyTerminal( @Nonnull Matrix4f transform, @Nonnull MultiBufferSource renderer, float x, float y, float width, float height ) { Colour colour = Colour.BLACK; - drawEmptyTerminal( IDENTITY, x, y, width, height ); + drawQuad( transform, renderer.getBuffer( RenderTypes.TERMINAL_WITH_DEPTH ), x, y, width, height, colour.getR(), colour.getG(), colour.getB() ); } public static void drawEmptyTerminal( @Nonnull Matrix4f transform, float x, float y, float width, float height ) { - MultiBufferSource.BufferSource renderer = Minecraft.getInstance() - .renderBuffers() - .bufferSource(); + MultiBufferSource.BufferSource renderer = MultiBufferSource.immediate( Tesselator.getInstance().getBuilder() ); drawEmptyTerminal( transform, renderer, x, y, width, height ); renderer.endBatch(); } - public static void drawEmptyTerminal( @Nonnull Matrix4f transform, @Nonnull MultiBufferSource renderer, float x, float y, float width, - float height ) - { - Colour colour = Colour.BLACK; - 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 MultiBufferSource renderer, float x, float y, float width, float height ) { Colour colour = Colour.BLACK; diff --git a/src/main/java/dan200/computercraft/client/gui/GuiComputer.java b/src/main/java/dan200/computercraft/client/gui/GuiComputer.java index b938d87f9..9046c2ffa 100644 --- a/src/main/java/dan200/computercraft/client/gui/GuiComputer.java +++ b/src/main/java/dan200/computercraft/client/gui/GuiComputer.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.gui; import com.mojang.blaze3d.vertex.PoseStack; @@ -20,14 +19,15 @@ import javax.annotation.Nonnull; import static dan200.computercraft.client.render.ComputerBorderRenderer.BORDER; -import static dan200.computercraft.client.render.ComputerBorderRenderer.getTexture; public final class GuiComputer extends ComputerScreenBase { private final int termWidth; private final int termHeight; - private GuiComputer( T container, Inventory player, Component title, int termWidth, int termHeight ) + private GuiComputer( + T container, Inventory player, Component title, int termWidth, int termHeight + ) { super( container, player, title, BORDER ); this.termWidth = termWidth; @@ -37,19 +37,31 @@ private GuiComputer( T container, Inventory player, Component title, int termWid imageHeight = WidgetTerminal.getHeight( termHeight ) + BORDER * 2; } + @Nonnull public static GuiComputer create( ContainerComputerBase container, Inventory inventory, Component component ) { - return new GuiComputer<>( container, inventory, component, ComputerCraft.computerTermWidth, ComputerCraft.computerTermHeight ); + return new GuiComputer<>( + container, inventory, component, + ComputerCraft.computerTermWidth, ComputerCraft.computerTermHeight + ); } + @Nonnull public static GuiComputer createPocket( ContainerComputerBase container, Inventory inventory, Component component ) { - return new GuiComputer<>( container, inventory, component, ComputerCraft.pocketTermWidth, ComputerCraft.pocketTermHeight ); + return new GuiComputer<>( + container, inventory, component, + ComputerCraft.pocketTermWidth, ComputerCraft.pocketTermHeight + ); } + @Nonnull public static GuiComputer createView( ContainerViewComputer container, Inventory inventory, Component component ) { - return new GuiComputer<>( container, inventory, component, container.getWidth(), container.getHeight() ); + return new GuiComputer<>( + container, inventory, component, + container.getWidth(), container.getHeight() + ); } @Override @@ -63,9 +75,11 @@ protected WidgetTerminal createTerminal() @Override public void renderBg( @Nonnull PoseStack stack, float partialTicks, int mouseX, int mouseY ) { + // Draw a border around the terminal ComputerBorderRenderer.render( - getTexture( family ), terminal.x, terminal.y, getBlitOffset(), - RenderTypes.FULL_BRIGHT_LIGHTMAP, terminal.getWidth(), terminal.getHeight() ); + ComputerBorderRenderer.getTexture( family ), terminal.x, terminal.y, getBlitOffset(), + RenderTypes.FULL_BRIGHT_LIGHTMAP, terminal.getWidth(), terminal.getHeight() + ); ComputerSidebar.renderBackground( stack, leftPos, topPos + sidebarYOffset ); } } diff --git a/src/main/java/dan200/computercraft/client/gui/GuiDiskDrive.java b/src/main/java/dan200/computercraft/client/gui/GuiDiskDrive.java index 6c61b4021..d64302c79 100644 --- a/src/main/java/dan200/computercraft/client/gui/GuiDiskDrive.java +++ b/src/main/java/dan200/computercraft/client/gui/GuiDiskDrive.java @@ -3,14 +3,12 @@ * 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 com.mojang.blaze3d.vertex.PoseStack; import dan200.computercraft.shared.peripheral.diskdrive.ContainerDiskDrive; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; -import net.minecraft.client.renderer.GameRenderer; import net.minecraft.network.chat.Component; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.player.Inventory; @@ -26,20 +24,19 @@ public GuiDiskDrive( ContainerDiskDrive container, Inventory player, Component t super( container, player, title ); } - @Override - public void render( @Nonnull PoseStack transform, int mouseX, int mouseY, float partialTicks ) - { - renderBackground( transform ); - super.render( transform, mouseX, mouseY, partialTicks ); - renderTooltip( transform, mouseX, mouseY ); - } - @Override protected void renderBg( @Nonnull PoseStack transform, float partialTicks, int mouseX, int mouseY ) { - RenderSystem.setShader( GameRenderer::getPositionTexShader ); RenderSystem.setShaderColor( 1.0F, 1.0F, 1.0F, 1.0F ); RenderSystem.setShaderTexture( 0, BACKGROUND ); blit( transform, leftPos, topPos, 0, 0, imageWidth, imageHeight ); } + + @Override + public void render( @Nonnull PoseStack transform, int mouseX, int mouseY, float partialTicks ) + { + renderBackground( transform ); + super.render( transform, mouseX, mouseY, partialTicks ); + renderTooltip( transform, mouseX, mouseY ); + } } diff --git a/src/main/java/dan200/computercraft/client/gui/GuiPrinter.java b/src/main/java/dan200/computercraft/client/gui/GuiPrinter.java index 67905dff3..069fb8d39 100644 --- a/src/main/java/dan200/computercraft/client/gui/GuiPrinter.java +++ b/src/main/java/dan200/computercraft/client/gui/GuiPrinter.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.gui; import com.mojang.blaze3d.systems.RenderSystem; @@ -25,14 +24,6 @@ public GuiPrinter( ContainerPrinter container, Inventory player, Component title super( container, player, title ); } - @Override - public void render( @Nonnull PoseStack stack, int mouseX, int mouseY, float partialTicks ) - { - renderBackground( stack ); - super.render( stack, mouseX, mouseY, partialTicks ); - renderTooltip( stack, mouseX, mouseY ); - } - @Override protected void renderBg( @Nonnull PoseStack transform, float partialTicks, int mouseX, int mouseY ) { @@ -40,9 +31,14 @@ protected void renderBg( @Nonnull PoseStack transform, float partialTicks, int m RenderSystem.setShaderTexture( 0, BACKGROUND ); blit( transform, leftPos, topPos, 0, 0, imageWidth, imageHeight ); - if( getMenu().isPrinting() ) - { - blit( transform, leftPos + 34, topPos + 21, 176, 0, 25, 45 ); - } + if( getMenu().isPrinting() ) blit( transform, leftPos + 34, topPos + 21, 176, 0, 25, 45 ); + } + + @Override + public void render( @Nonnull PoseStack stack, int mouseX, int mouseY, float partialTicks ) + { + renderBackground( stack ); + super.render( stack, mouseX, mouseY, partialTicks ); + renderTooltip( stack, mouseX, mouseY ); } } diff --git a/src/main/java/dan200/computercraft/client/gui/GuiPrintout.java b/src/main/java/dan200/computercraft/client/gui/GuiPrintout.java index 299971999..546951d35 100644 --- a/src/main/java/dan200/computercraft/client/gui/GuiPrintout.java +++ b/src/main/java/dan200/computercraft/client/gui/GuiPrintout.java @@ -3,16 +3,15 @@ * 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 com.mojang.blaze3d.vertex.PoseStack; +import com.mojang.blaze3d.vertex.Tesselator; import com.mojang.math.Matrix4f; import dan200.computercraft.core.terminal.TextBuffer; import dan200.computercraft.shared.common.ContainerHeldItem; import dan200.computercraft.shared.media.items.ItemPrintout; -import net.minecraft.client.Minecraft; import net.minecraft.client.gui.screens.inventory.AbstractContainerScreen; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.network.chat.Component; @@ -40,71 +39,58 @@ public GuiPrintout( ContainerHeldItem container, Inventory player, Component tit String[] text = ItemPrintout.getText( container.getStack() ); this.text = new TextBuffer[text.length]; - for( int i = 0; i < this.text.length; i++ ) - { - this.text[i] = new TextBuffer( text[i] ); - } + for( int i = 0; i < this.text.length; i++ ) this.text[i] = new TextBuffer( text[i] ); String[] colours = ItemPrintout.getColours( container.getStack() ); this.colours = new TextBuffer[colours.length]; - for( int i = 0; i < this.colours.length; i++ ) - { - this.colours[i] = new TextBuffer( colours[i] ); - } + for( int i = 0; i < this.colours.length; i++ ) this.colours[i] = new TextBuffer( colours[i] ); page = 0; pages = Math.max( this.text.length / ItemPrintout.LINES_PER_PAGE, 1 ); - book = ((ItemPrintout) container.getStack() - .getItem()).getType() == ItemPrintout.Type.BOOK; + book = ((ItemPrintout) container.getStack().getItem()).getType() == ItemPrintout.Type.BOOK; } @Override - public boolean mouseScrolled( double x, double y, double delta ) + public boolean keyPressed( int key, int scancode, int modifiers ) { - if( super.mouseScrolled( x, y, delta ) ) + if( super.keyPressed( key, scancode, modifiers ) ) return true; + + if( key == GLFW.GLFW_KEY_RIGHT ) + { + if( page < pages - 1 ) page++; + return true; + } + + if( key == GLFW.GLFW_KEY_LEFT ) { + if( page > 0 ) page--; return true; } + + return false; + } + + @Override + public boolean mouseScrolled( double x, double y, double delta ) + { + if( super.mouseScrolled( x, y, delta ) ) return true; if( delta < 0 ) { // Scroll up goes to the next page - if( page < pages - 1 ) - { - page++; - } + if( page < pages - 1 ) page++; return true; } if( delta > 0 ) { // Scroll down goes to the previous page - if( page > 0 ) - { - page--; - } + if( page > 0 ) page--; return true; } return false; } - @Override - public void render( @Nonnull PoseStack stack, int mouseX, int mouseY, float partialTicks ) - { - // We must take the background further back in order to not overlap with our printed pages. - setBlitOffset( getBlitOffset() - 1 ); - renderBackground( stack ); - setBlitOffset( getBlitOffset() + 1 ); - - super.render( stack, mouseX, mouseY, partialTicks ); - } - - @Override - protected void renderLabels( @Nonnull PoseStack transform, int mouseX, int mouseY ) - { - // Skip rendering labels. - } - @Override protected void renderBg( @Nonnull PoseStack transform, float partialTicks, int mouseX, int mouseY ) { @@ -112,42 +98,27 @@ protected void renderBg( @Nonnull PoseStack transform, float partialTicks, int m RenderSystem.setShaderColor( 1.0f, 1.0f, 1.0f, 1.0f ); RenderSystem.enableDepthTest(); - MultiBufferSource.BufferSource renderer = Minecraft.getInstance() - .renderBuffers() - .bufferSource(); - Matrix4f matrix = transform.last() - .pose(); + MultiBufferSource.BufferSource renderer = MultiBufferSource.immediate( Tesselator.getInstance().getBuilder() ); + Matrix4f matrix = transform.last().pose(); drawBorder( matrix, renderer, leftPos, topPos, getBlitOffset(), page, pages, book, FULL_BRIGHT_LIGHTMAP ); drawText( matrix, renderer, leftPos + X_TEXT_MARGIN, topPos + Y_TEXT_MARGIN, ItemPrintout.LINES_PER_PAGE * page, FULL_BRIGHT_LIGHTMAP, text, colours ); renderer.endBatch(); } @Override - public boolean keyPressed( int key, int scancode, int modifiers ) + public void render( @Nonnull PoseStack stack, int mouseX, int mouseY, float partialTicks ) { - if( super.keyPressed( key, scancode, modifiers ) ) - { - return true; - } - - if( key == GLFW.GLFW_KEY_RIGHT ) - { - if( page < pages - 1 ) - { - page++; - } - return true; - } + // We must take the background further back in order to not overlap with our printed pages. + setBlitOffset( getBlitOffset() - 1 ); + renderBackground( stack ); + setBlitOffset( getBlitOffset() + 1 ); - if( key == GLFW.GLFW_KEY_LEFT ) - { - if( page > 0 ) - { - page--; - } - return true; - } + super.render( stack, mouseX, mouseY, partialTicks ); + } - return false; + @Override + protected void renderLabels( @Nonnull PoseStack transform, int mouseX, int mouseY ) + { + // Skip rendering labels. } } diff --git a/src/main/java/dan200/computercraft/client/gui/GuiTurtle.java b/src/main/java/dan200/computercraft/client/gui/GuiTurtle.java index c2faa896f..adcb93fc5 100644 --- a/src/main/java/dan200/computercraft/client/gui/GuiTurtle.java +++ b/src/main/java/dan200/computercraft/client/gui/GuiTurtle.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.gui; import com.mojang.blaze3d.systems.RenderSystem; @@ -20,12 +19,12 @@ import javax.annotation.Nonnull; -import static dan200.computercraft.shared.turtle.inventory.ContainerTurtle.BORDER; +import static dan200.computercraft.shared.turtle.inventory.ContainerTurtle.*; public class GuiTurtle extends ComputerScreenBase { - private static final ResourceLocation BACKGROUND_NORMAL = new ResourceLocation( "computercraft", "textures/gui/turtle_normal.png" ); - private static final ResourceLocation BACKGROUND_ADVANCED = new ResourceLocation( "computercraft", "textures/gui/turtle_advanced.png" ); + private static final ResourceLocation BACKGROUND_NORMAL = new ResourceLocation( ComputerCraft.MOD_ID, "textures/gui/turtle_normal.png" ); + private static final ResourceLocation BACKGROUND_ADVANCED = new ResourceLocation( ComputerCraft.MOD_ID, "textures/gui/turtle_advanced.png" ); private static final int TEX_WIDTH = 254; private static final int TEX_HEIGHT = 217; @@ -35,11 +34,10 @@ public class GuiTurtle extends ComputerScreenBase public GuiTurtle( ContainerTurtle container, Inventory player, Component title ) { super( container, player, title, BORDER ); - family = container.getFamily(); + imageWidth = TEX_WIDTH + ComputerSidebar.WIDTH; imageHeight = TEX_HEIGHT; - } @Override @@ -52,23 +50,22 @@ protected WidgetTerminal createTerminal() } @Override - public void renderBg( @Nonnull PoseStack transform, float partialTicks, int mouseX, int mouseY ) + protected void renderBg( @Nonnull PoseStack transform, float partialTicks, int mouseX, int mouseY ) { boolean advanced = family == ComputerFamily.ADVANCED; RenderSystem.setShaderTexture( 0, advanced ? BACKGROUND_ADVANCED : BACKGROUND_NORMAL ); blit( transform, leftPos + ComputerSidebar.WIDTH, topPos, 0, 0, TEX_WIDTH, TEX_HEIGHT ); - // Draw selection slot int slot = getMenu().getSelectedSlot(); if( slot >= 0 ) { + RenderSystem.setShaderColor( 1.0F, 1.0F, 1.0F, 1.0F ); int slotX = slot % 4; int slotY = slot / 4; - blit( transform, leftPos + ContainerTurtle.TURTLE_START_X - 2 + slotX * 18, topPos + ContainerTurtle.PLAYER_START_Y - 2 + slotY * 18, - 0, - 217, - 24, - 24 ); + blit( transform, + leftPos + TURTLE_START_X - 2 + slotX * 18, topPos + PLAYER_START_Y - 2 + slotY * 18, + 0, 217, 24, 24 + ); } RenderSystem.setShaderTexture( 0, advanced ? ComputerBorderRenderer.BACKGROUND_ADVANCED : ComputerBorderRenderer.BACKGROUND_NORMAL ); diff --git a/src/main/java/dan200/computercraft/client/gui/NoTermComputerScreen.java b/src/main/java/dan200/computercraft/client/gui/NoTermComputerScreen.java index 300dd7def..225a119a4 100644 --- a/src/main/java/dan200/computercraft/client/gui/NoTermComputerScreen.java +++ b/src/main/java/dan200/computercraft/client/gui/NoTermComputerScreen.java @@ -43,9 +43,10 @@ public T getMenu() @Override protected void init() { - this.passEvents = true; + passEvents = true; // Pass mouse vents through to the game's mouse handler. minecraft.mouseHandler.grabMouse(); minecraft.screen = this; + super.init(); minecraft.keyboardHandler.setSendRepeatsToGui( true ); @@ -102,7 +103,7 @@ public final boolean keyPressed( int key, int scancode, int modifiers ) } @Override - public void render( PoseStack transform, int mouseX, int mouseY, float partialTicks ) + public void render( @Nonnull PoseStack transform, int mouseX, int mouseY, float partialTicks ) { super.render( transform, mouseX, mouseY, partialTicks ); diff --git a/src/main/java/dan200/computercraft/client/gui/OptionScreen.java b/src/main/java/dan200/computercraft/client/gui/OptionScreen.java index 532c08b94..8b9cf05d7 100644 --- a/src/main/java/dan200/computercraft/client/gui/OptionScreen.java +++ b/src/main/java/dan200/computercraft/client/gui/OptionScreen.java @@ -49,9 +49,9 @@ private OptionScreen( Component title, Component message, List b this.originalScreen = originalScreen; } - public static void show( Minecraft client, Component title, Component message, List buttons, Runnable exit ) + public static void show( Minecraft minecraft, Component title, Component message, List buttons, Runnable exit ) { - client.setScreen( new OptionScreen( title, message, buttons, exit, unwrap( client.screen ) ) ); + minecraft.setScreen( new OptionScreen( title, message, buttons, exit, unwrap( minecraft.screen ) ) ); } public static Screen unwrap( Screen screen ) diff --git a/src/main/java/dan200/computercraft/client/gui/widgets/ComputerSidebar.java b/src/main/java/dan200/computercraft/client/gui/widgets/ComputerSidebar.java index afc8139ff..b7f3e24f7 100644 --- a/src/main/java/dan200/computercraft/client/gui/widgets/ComputerSidebar.java +++ b/src/main/java/dan200/computercraft/client/gui/widgets/ComputerSidebar.java @@ -8,7 +8,6 @@ import com.mojang.blaze3d.vertex.PoseStack; 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.ChatFormatting; import net.minecraft.client.gui.components.AbstractWidget; @@ -55,10 +54,10 @@ public static void addButtons( Screen screen, ClientComputer computer, Consumer< TEXTURE, TEX_SIZE, TEX_SIZE, b -> toggleComputer( computer ), () -> computer.isOn() ? Arrays.asList( new TranslatableComponent( "gui.computercraft.tooltip.turn_off" ), - ChatHelpers.coloured( new TranslatableComponent( "gui.computercraft.tooltip.turn_off.key" ), ChatFormatting.GRAY ) + new TranslatableComponent( "gui.computercraft.tooltip.turn_off.key" ).withStyle( ChatFormatting.GRAY ) ) : Arrays.asList( new TranslatableComponent( "gui.computercraft.tooltip.turn_on" ), - ChatHelpers.coloured( new TranslatableComponent( "gui.computercraft.tooltip.turn_off.key" ), ChatFormatting.GRAY ) + new TranslatableComponent( "gui.computercraft.tooltip.turn_off.key" ).withStyle( ChatFormatting.GRAY ) ) ) ); @@ -69,7 +68,7 @@ public static void addButtons( Screen screen, ClientComputer computer, Consumer< TEXTURE, TEX_SIZE, TEX_SIZE, b -> computer.queueEvent( "terminate" ), Arrays.asList( new TranslatableComponent( "gui.computercraft.tooltip.terminate" ), - ChatHelpers.coloured( new TranslatableComponent( "gui.computercraft.tooltip.terminate.key" ), ChatFormatting.GRAY ) + new TranslatableComponent( "gui.computercraft.tooltip.terminate.key" ).withStyle( ChatFormatting.GRAY ) ) ) ); } diff --git a/src/main/java/dan200/computercraft/client/gui/widgets/DynamicImageButton.java b/src/main/java/dan200/computercraft/client/gui/widgets/DynamicImageButton.java index 4d780a08c..2a6e97476 100644 --- a/src/main/java/dan200/computercraft/client/gui/widgets/DynamicImageButton.java +++ b/src/main/java/dan200/computercraft/client/gui/widgets/DynamicImageButton.java @@ -7,6 +7,7 @@ import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.PoseStack; +import dan200.computercraft.shared.util.NonNullSupplier; import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; @@ -16,7 +17,6 @@ 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.components.ImageButton} which allows changing some properties @@ -31,7 +31,7 @@ public class DynamicImageButton extends Button private final int yDiffTex; private final int textureWidth; private final int textureHeight; - private final Supplier> tooltip; + private final NonNullSupplier> tooltip; public DynamicImageButton( Screen screen, int x, int y, int width, int height, int xTexStart, int yTexStart, int yDiffTex, @@ -50,7 +50,7 @@ public DynamicImageButton( public DynamicImageButton( Screen screen, int x, int y, int width, int height, IntSupplier xTexStart, int yTexStart, int yDiffTex, ResourceLocation texture, int textureWidth, int textureHeight, - OnPress onPress, Supplier> tooltip + OnPress onPress, NonNullSupplier> tooltip ) { super( x, y, width, height, TextComponent.EMPTY, onPress ); @@ -76,7 +76,7 @@ public void renderButton( @Nonnull PoseStack stack, int mouseX, int mouseY, floa blit( stack, x, y, xTexStart.getAsInt(), yTex, width, height, textureWidth, textureHeight ); RenderSystem.enableDepthTest(); - if( isHoveredOrFocused() ) renderToolTip( stack, mouseX, mouseY ); + if( isHovered ) renderToolTip( stack, mouseX, mouseY ); } @Nonnull @@ -87,11 +87,10 @@ public Component getMessage() return tooltip.isEmpty() ? TextComponent.EMPTY : tooltip.get( 0 ); } - // @Override + @Override public void renderToolTip( @Nonnull PoseStack stack, int mouseX, int mouseY ) { List tooltip = this.tooltip.get(); - if( !tooltip.isEmpty() ) { screen.renderComponentTooltip( 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 8eaac4d80..8ecbb90c0 100644 --- a/src/main/java/dan200/computercraft/client/gui/widgets/WidgetTerminal.java +++ b/src/main/java/dan200/computercraft/client/gui/widgets/WidgetTerminal.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.gui.widgets; import com.mojang.blaze3d.vertex.PoseStack; @@ -65,15 +64,104 @@ 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 ) ); + computer.queueEvent( "char", new Object[] { Character.toString( ch ) } ); } return true; } - private boolean inTermRegion( double mouseX, double mouseY ) + @Override + public boolean keyPressed( int key, int scancode, int modifiers ) { - return active && visible && mouseX >= innerX && mouseY >= innerY && mouseX < innerX + innerWidth && mouseY < innerY + innerHeight; + if( key == GLFW.GLFW_KEY_ESCAPE ) return false; + if( (modifiers & GLFW.GLFW_MOD_CONTROL) != 0 ) + { + switch( key ) + { + case GLFW.GLFW_KEY_T: + if( terminateTimer < 0 ) terminateTimer = 0; + return true; + case GLFW.GLFW_KEY_S: + if( shutdownTimer < 0 ) shutdownTimer = 0; + return true; + case GLFW.GLFW_KEY_R: + if( rebootTimer < 0 ) rebootTimer = 0; + return true; + + case GLFW.GLFW_KEY_V: + // Ctrl+V for paste + String clipboard = Minecraft.getInstance().keyboardHandler.getClipboard(); + if( clipboard != null ) + { + // Clip to the first occurrence of \r or \n + int newLineIndex1 = clipboard.indexOf( "\r" ); + int newLineIndex2 = clipboard.indexOf( "\n" ); + if( newLineIndex1 >= 0 && newLineIndex2 >= 0 ) + { + clipboard = clipboard.substring( 0, Math.min( newLineIndex1, newLineIndex2 ) ); + } + else if( newLineIndex1 >= 0 ) + { + clipboard = clipboard.substring( 0, newLineIndex1 ); + } + else if( newLineIndex2 >= 0 ) + { + clipboard = clipboard.substring( 0, newLineIndex2 ); + } + + // Filter the string + clipboard = SharedConstants.filterText( clipboard ); + if( !clipboard.isEmpty() ) + { + // Clip to 512 characters and queue the event + if( clipboard.length() > 512 ) clipboard = clipboard.substring( 0, 512 ); + computer.queueEvent( "paste", new Object[] { clipboard } ); + } + + return true; + } + } + } + + if( key >= 0 && terminateTimer < 0 && rebootTimer < 0 && shutdownTimer < 0 ) + { + // Queue the "key" event and add to the down set + boolean repeat = keysDown.get( key ); + keysDown.set( key ); + computer.keyDown( key, repeat ); + } + + return true; + } + + @Override + public boolean keyReleased( int key, int scancode, int modifiers ) + { + // Queue the "key_up" event and remove from the down set + if( key >= 0 && keysDown.get( key ) ) + { + keysDown.set( key, false ); + computer.keyUp( key ); + } + + switch( key ) + { + case GLFW.GLFW_KEY_T: + terminateTimer = -1; + break; + case GLFW.GLFW_KEY_R: + rebootTimer = -1; + break; + case GLFW.GLFW_KEY_S: + shutdownTimer = -1; + break; + case GLFW.GLFW_KEY_LEFT_CONTROL: + case GLFW.GLFW_KEY_RIGHT_CONTROL: + terminateTimer = rebootTimer = shutdownTimer = -1; + break; + } + + return true; } @Override @@ -175,113 +263,27 @@ public boolean mouseScrolled( double mouseX, double mouseY, double delta ) return true; } - @Override - public boolean keyPressed( int key, int scancode, int modifiers ) + private boolean inTermRegion( double mouseX, double mouseY ) { - if( key == GLFW.GLFW_KEY_ESCAPE ) - { - return false; - } - if( (modifiers & GLFW.GLFW_MOD_CONTROL) != 0 ) - { - switch( key ) - { - case GLFW.GLFW_KEY_T: - if( terminateTimer < 0 ) - { - terminateTimer = 0; - } - return true; - case GLFW.GLFW_KEY_S: - if( shutdownTimer < 0 ) - { - shutdownTimer = 0; - } - return true; - case GLFW.GLFW_KEY_R: - if( rebootTimer < 0 ) - { - rebootTimer = 0; - } - return true; - - case GLFW.GLFW_KEY_V: - // Ctrl+V for paste - String clipboard = Minecraft.getInstance().keyboardHandler.getClipboard(); - if( clipboard != null ) - { - // Clip to the first occurrence of \r or \n - int newLineIndex1 = clipboard.indexOf( "\r" ); - int newLineIndex2 = clipboard.indexOf( "\n" ); - if( newLineIndex1 >= 0 && newLineIndex2 >= 0 ) - { - clipboard = clipboard.substring( 0, Math.min( newLineIndex1, newLineIndex2 ) ); - } - else if( newLineIndex1 >= 0 ) - { - clipboard = clipboard.substring( 0, newLineIndex1 ); - } - else if( newLineIndex2 >= 0 ) - { - clipboard = clipboard.substring( 0, newLineIndex2 ); - } - - // Filter the string - clipboard = SharedConstants.filterText( clipboard ); - if( !clipboard.isEmpty() ) - { - // Clip to 512 characters and queue the event - if( clipboard.length() > 512 ) - { - clipboard = clipboard.substring( 0, 512 ); - } - queueEvent( "paste", clipboard ); - } - - return true; - } - } - } - - if( key >= 0 && terminateTimer < 0 && rebootTimer < 0 && shutdownTimer < 0 ) - { - // Queue the "key" event and add to the down set - boolean repeat = keysDown.get( key ); - keysDown.set( key ); - computer.keyDown( key, repeat ); - } - - return true; + return active && visible && mouseX >= innerX && mouseY >= innerY && mouseX < innerX + innerWidth && mouseY < innerY + innerHeight; } - @Override - public boolean keyReleased( int key, int scancode, int modifiers ) + public void update() { - // Queue the "key_up" event and remove from the down set - if( key >= 0 && keysDown.get( key ) ) + if( terminateTimer >= 0 && terminateTimer < TERMINATE_TIME && (terminateTimer += 0.05f) > TERMINATE_TIME ) { - keysDown.set( key, false ); - computer.keyUp( key ); + computer.queueEvent( "terminate" ); } - switch( key ) + if( shutdownTimer >= 0 && shutdownTimer < TERMINATE_TIME && (shutdownTimer += 0.05f) > TERMINATE_TIME ) { - case GLFW.GLFW_KEY_T: - terminateTimer = -1; - break; - case GLFW.GLFW_KEY_R: - rebootTimer = -1; - break; - case GLFW.GLFW_KEY_S: - shutdownTimer = -1; - break; - case GLFW.GLFW_KEY_LEFT_CONTROL: - case GLFW.GLFW_KEY_RIGHT_CONTROL: - terminateTimer = rebootTimer = shutdownTimer = -1; - break; + computer.shutdown(); } - return true; + if( rebootTimer >= 0 && rebootTimer < TERMINATE_TIME && (rebootTimer += 0.05f) > TERMINATE_TIME ) + { + computer.reboot(); + } } @Override @@ -307,44 +309,6 @@ public void onFocusedChanged( boolean focused ) } } - @Override - public boolean isMouseOver( double x, double y ) - { - return true; - } - - private void queueEvent( String event, Object... args ) - { - computer.queueEvent( event, args ); - } - - public void update() - { - if( terminateTimer >= 0 && terminateTimer < TERMINATE_TIME && (terminateTimer += 0.05f) > TERMINATE_TIME ) - { - queueEvent( "terminate" ); - } - - if( shutdownTimer >= 0 && shutdownTimer < TERMINATE_TIME && (shutdownTimer += 0.05f) > TERMINATE_TIME ) - { - computer.shutdown(); - } - - if( rebootTimer >= 0 && rebootTimer < TERMINATE_TIME && (rebootTimer += 0.05f) > TERMINATE_TIME ) - { - computer.reboot(); - } - } - - private void queueEvent( String event ) - { - ClientComputer computer = this.computer; - if( computer != null ) - { - computer.queueEvent( event ); - } - } - @Override public void render( @Nonnull PoseStack transform, int mouseX, int mouseY, float partialTicks ) { @@ -362,9 +326,9 @@ public void render( @Nonnull PoseStack transform, int mouseX, int mouseY, float } @Override - public void updateNarration( NarrationElementOutput builder ) + public void updateNarration( @Nonnull NarrationElementOutput output ) { - + // I'm not sure what the right option is here. } public static int getWidth( int termWidth ) diff --git a/src/main/java/dan200/computercraft/client/proxy/ComputerCraftProxyClient.java b/src/main/java/dan200/computercraft/client/proxy/ComputerCraftProxyClient.java index 11dd8f378..a0f21fa59 100644 --- a/src/main/java/dan200/computercraft/client/proxy/ComputerCraftProxyClient.java +++ b/src/main/java/dan200/computercraft/client/proxy/ComputerCraftProxyClient.java @@ -15,7 +15,7 @@ import dan200.computercraft.client.render.TurtleModelLoader; import dan200.computercraft.client.render.TurtlePlayerRenderer; import dan200.computercraft.fabric.events.ClientUnloadWorldEvent; -import dan200.computercraft.shared.ComputerCraftRegistry; +import dan200.computercraft.shared.Registry; import dan200.computercraft.shared.common.ContainerHeldItem; import dan200.computercraft.shared.common.IColouredItem; import dan200.computercraft.shared.common.TileGeneric; @@ -73,17 +73,17 @@ public void onInitializeClient() registerContainers(); // While turtles themselves are not transparent, their upgrades may be. - BlockRenderLayerMap.INSTANCE.putBlock( ComputerCraftRegistry.ModBlocks.TURTLE_NORMAL, RenderType.translucent() ); - BlockRenderLayerMap.INSTANCE.putBlock( ComputerCraftRegistry.ModBlocks.TURTLE_ADVANCED, RenderType.translucent() ); + BlockRenderLayerMap.INSTANCE.putBlock( Registry.ModBlocks.TURTLE_NORMAL, RenderType.translucent() ); + BlockRenderLayerMap.INSTANCE.putBlock( Registry.ModBlocks.TURTLE_ADVANCED, RenderType.translucent() ); // Monitors' textures have transparent fronts and so count as cutouts. - BlockRenderLayerMap.INSTANCE.putBlock( ComputerCraftRegistry.ModBlocks.MONITOR_NORMAL, RenderType.cutout() ); - BlockRenderLayerMap.INSTANCE.putBlock( ComputerCraftRegistry.ModBlocks.MONITOR_ADVANCED, RenderType.cutout() ); + BlockRenderLayerMap.INSTANCE.putBlock( Registry.ModBlocks.MONITOR_NORMAL, RenderType.cutout() ); + BlockRenderLayerMap.INSTANCE.putBlock( Registry.ModBlocks.MONITOR_ADVANCED, RenderType.cutout() ); // Setup TESRs - 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 ); + BlockEntityRendererRegistry.register( Registry.ModBlockEntities.MONITOR_NORMAL, TileEntityMonitorRenderer::new ); + BlockEntityRendererRegistry.register( Registry.ModBlockEntities.MONITOR_ADVANCED, TileEntityMonitorRenderer::new ); + BlockEntityRendererRegistry.register( Registry.ModBlockEntities.TURTLE_NORMAL, TileEntityTurtleRenderer::new ); + BlockEntityRendererRegistry.register( Registry.ModBlockEntities.TURTLE_ADVANCED, TileEntityTurtleRenderer::new ); ClientSpriteRegistryCallback.event( InventoryMenu.BLOCK_ATLAS ) .register( ClientRegistry::onTextureStitchEvent ); @@ -92,17 +92,17 @@ public void onInitializeClient() TurtleModelLoader.INSTANCE.loadModel( name ) : null ); - EntityRendererRegistry.register( ComputerCraftRegistry.ModEntities.TURTLE_PLAYER, TurtlePlayerRenderer::new ); + EntityRendererRegistry.register( Registry.ModEntities.TURTLE_PLAYER, TurtlePlayerRenderer::new ); registerItemProperty( "state", ( stack, world, player, integer ) -> ItemPocketComputer.getState( stack ) .ordinal(), - () -> ComputerCraftRegistry.ModItems.POCKET_COMPUTER_NORMAL, - () -> ComputerCraftRegistry.ModItems.POCKET_COMPUTER_ADVANCED ); + () -> Registry.ModItems.POCKET_COMPUTER_NORMAL, + () -> Registry.ModItems.POCKET_COMPUTER_ADVANCED ); registerItemProperty( "state", ( stack, world, player, integer ) -> IColouredItem.getColourBasic( stack ) != -1 ? 1 : 0, - () -> ComputerCraftRegistry.ModItems.POCKET_COMPUTER_NORMAL, - () -> ComputerCraftRegistry.ModItems.POCKET_COMPUTER_ADVANCED ); + () -> Registry.ModItems.POCKET_COMPUTER_NORMAL, + () -> Registry.ModItems.POCKET_COMPUTER_ADVANCED ); ClientRegistry.onItemColours(); initEvents(); @@ -111,18 +111,18 @@ public void onInitializeClient() // 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( Registry.ModContainers.COMPUTER, GuiComputer::create ); + ScreenRegistry.>register( Registry.ModContainers.POCKET_COMPUTER, GuiComputer::createPocket ); - ScreenRegistry.>register( ComputerCraftRegistry.ModContainers.POCKET_COMPUTER_NO_TERM, + ScreenRegistry.>register( Registry.ModContainers.POCKET_COMPUTER_NO_TERM, NoTermComputerScreen::new ); - ScreenRegistry.register( ComputerCraftRegistry.ModContainers.TURTLE, GuiTurtle::new ); + ScreenRegistry.register( Registry.ModContainers.TURTLE, GuiTurtle::new ); - ScreenRegistry.register( ComputerCraftRegistry.ModContainers.PRINTER, GuiPrinter::new ); - ScreenRegistry.register( ComputerCraftRegistry.ModContainers.DISK_DRIVE, GuiDiskDrive::new ); - ScreenRegistry.register( ComputerCraftRegistry.ModContainers.PRINTOUT, GuiPrintout::new ); + ScreenRegistry.register( Registry.ModContainers.PRINTER, GuiPrinter::new ); + ScreenRegistry.register( Registry.ModContainers.DISK_DRIVE, GuiDiskDrive::new ); + ScreenRegistry.register( Registry.ModContainers.PRINTOUT, GuiPrintout::new ); - ScreenRegistry.>register( ComputerCraftRegistry.ModContainers.VIEW_COMPUTER, + ScreenRegistry.>register( Registry.ModContainers.VIEW_COMPUTER, GuiComputer::createView ); } diff --git a/src/main/java/dan200/computercraft/client/render/CableHighlightRenderer.java b/src/main/java/dan200/computercraft/client/render/CableHighlightRenderer.java index 1f9935a78..0dbfcf27c 100644 --- a/src/main/java/dan200/computercraft/client/render/CableHighlightRenderer.java +++ b/src/main/java/dan200/computercraft/client/render/CableHighlightRenderer.java @@ -3,14 +3,13 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.client.render; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.blaze3d.vertex.VertexConsumer; import com.mojang.math.Matrix3f; import com.mojang.math.Matrix4f; -import dan200.computercraft.shared.ComputerCraftRegistry; +import dan200.computercraft.shared.Registry; import dan200.computercraft.shared.peripheral.modem.wired.BlockCable; import dan200.computercraft.shared.peripheral.modem.wired.CableShapes; import dan200.computercraft.shared.util.WorldUtil; @@ -33,35 +32,34 @@ private CableHighlightRenderer() { } - public static boolean drawHighlight( PoseStack stack, VertexConsumer consumer, Entity entity, double d, double e, double f, BlockPos pos, - BlockState state ) + /* + * Draw an outline for a specific part of a cable "Multipart". + * + * @see net.minecraft.client.renderer.LevelRenderer#renderHitOutline + */ + public static boolean drawHighlight( PoseStack stack, VertexConsumer buffer, Entity entity, double d, double e, double f, BlockPos pos, BlockState state ) { + HitResult hitResult = Minecraft.getInstance().hitResult; Camera info = Minecraft.getInstance().gameRenderer.getMainCamera(); // We only care about instances with both cable and modem. - if( state.getBlock() != ComputerCraftRegistry.ModBlocks.CABLE || state.getValue( BlockCable.MODEM ) - .getFacing() == null || !state.getValue( BlockCable.CABLE ) ) + if( state.getBlock() != Registry.ModBlocks.CABLE || state.getValue( BlockCable.MODEM ).getFacing() == null || !state.getValue( BlockCable.CABLE ) ) { return false; } - HitResult hitResult = Minecraft.getInstance().hitResult; - Vec3 hitPos = hitResult != null ? hitResult.getLocation() : new Vec3( d, e, f ); - VoxelShape shape = WorldUtil.isVecInside( CableShapes.getModemShape( state ), - hitPos.subtract( pos.getX(), - pos.getY(), - pos.getZ() ) ) ? CableShapes.getModemShape( state ) : CableShapes.getCableShape( - state ); + VoxelShape shape = WorldUtil.isVecInside( CableShapes.getModemShape( state ), hitPos.subtract( pos.getX(), pos.getY(), pos.getZ() ) ) + ? CableShapes.getModemShape( state ) + : CableShapes.getCableShape( state ); Vec3 cameraPos = info.getPosition(); - double xOffset = pos.getX() - cameraPos.x(); double yOffset = pos.getY() - cameraPos.y(); double zOffset = pos.getZ() - cameraPos.z(); - Matrix4f matrix4f = stack.last() - .pose(); + + Matrix4f matrix4f = stack.last().pose(); Matrix3f normal = stack.last().normal(); shape.forAllEdges( ( x1, y1, z1, x2, y2, z2 ) -> { float xDelta = (float) (x2 - x1); @@ -72,11 +70,13 @@ public static boolean drawHighlight( PoseStack stack, VertexConsumer consumer, E yDelta = yDelta / len; zDelta = zDelta / len; - consumer.vertex( matrix4f, (float) (x1 + xOffset), (float) (y1 + yOffset), (float) (z1 + zOffset) ) + buffer + .vertex( matrix4f, (float) (x1 + xOffset), (float) (y1 + yOffset), (float) (z1 + zOffset) ) .color( 0, 0, 0, 0.4f ) .normal( normal, xDelta, yDelta, zDelta ) .endVertex(); - consumer.vertex( matrix4f, (float) (x2 + xOffset), (float) (y2 + yOffset), (float) (z2 + zOffset) ) + buffer + .vertex( matrix4f, (float) (x2 + xOffset), (float) (y2 + yOffset), (float) (z2 + zOffset) ) .color( 0, 0, 0, 0.4f ) .normal( normal, xDelta, yDelta, zDelta ) .endVertex(); diff --git a/src/main/java/dan200/computercraft/client/render/ComputerBorderRenderer.java b/src/main/java/dan200/computercraft/client/render/ComputerBorderRenderer.java index b5f58d77a..a20699087 100644 --- a/src/main/java/dan200/computercraft/client/render/ComputerBorderRenderer.java +++ b/src/main/java/dan200/computercraft/client/render/ComputerBorderRenderer.java @@ -22,15 +22,24 @@ public class ComputerBorderRenderer public static final ResourceLocation BACKGROUND_ADVANCED = new ResourceLocation( ComputerCraft.MOD_ID, "textures/gui/corners_advanced.png" ); public static final ResourceLocation BACKGROUND_COMMAND = new ResourceLocation( ComputerCraft.MOD_ID, "textures/gui/corners_command.png" ); public static final ResourceLocation BACKGROUND_COLOUR = new ResourceLocation( ComputerCraft.MOD_ID, "textures/gui/corners_colour.png" ); + + private static final Matrix4f IDENTITY = new Matrix4f(); + + static + { + IDENTITY.setIdentity(); + } + /** * The margin between the terminal and its border. */ public static final int MARGIN = 2; + /** * The width of the terminal border. */ public static final int BORDER = 12; - private static final Matrix4f IDENTITY = new Matrix4f(); + private static final int CORNER_TOP_Y = 28; private static final int CORNER_BOTTOM_Y = CORNER_TOP_Y + BORDER; private static final int CORNER_LEFT_X = BORDER; @@ -40,21 +49,16 @@ public class ComputerBorderRenderer private static final int LIGHT_CORNER_Y = 80; public static final int LIGHT_HEIGHT = 8; + public static final int TEX_SIZE = 256; private static final float TEX_SCALE = 1 / (float) TEX_SIZE; - static - { - IDENTITY.setIdentity(); - } - private final Matrix4f transform; private final VertexConsumer builder; + private final int light; private final int z; private final float r, g, b; - private final int light; - public ComputerBorderRenderer( Matrix4f transform, VertexConsumer builder, int z, int light, float r, float g, float b ) { this.transform = transform; @@ -82,14 +86,19 @@ public static ResourceLocation getTexture( @Nonnull ComputerFamily family ) } } + public static RenderType getRenderType( ResourceLocation location ) + { + // See note in RenderTypes about why we use text rather than anything intuitive. + return RenderType.text( location ); + } + public static void render( ResourceLocation location, int x, int y, int z, int light, int width, int height ) { MultiBufferSource.BufferSource source = MultiBufferSource.immediate( Tesselator.getInstance().getBuilder() ); - render( IDENTITY, source.getBuffer( RenderType.text( location ) ), x, y, z, light, width, height, false, 1, 1, 1 ); + render( IDENTITY, source.getBuffer( getRenderType( location ) ), x, y, z, light, width, height, false, 1, 1, 1 ); source.endBatch(); } - 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 ) { new ComputerBorderRenderer( transform, buffer, z, light, r, g, b ).doRender( x, y, width, height, withLight ); @@ -125,14 +134,14 @@ public void doRender( int x, int y, int width, int height, boolean withLight ) } } - private void renderLine( int x, int y, int u, int v, int width, int height ) + private void renderCorner( int x, int y, int u, int v ) { - renderTexture( x, y, u, v, width, height, BORDER, BORDER ); + renderTexture( x, y, u, v, BORDER, BORDER, BORDER, BORDER ); } - private void renderCorner( int x, int y, int u, int v ) + private void renderLine( int x, int y, int u, int v, int width, int height ) { - renderTexture( x, y, u, v, BORDER, BORDER, BORDER, BORDER ); + renderTexture( x, y, u, v, width, height, BORDER, BORDER ); } private void renderTexture( int x, int y, int u, int v, int width, int height ) @@ -142,25 +151,9 @@ private void renderTexture( int x, int y, int u, int v, int width, int height ) private void renderTexture( int x, int y, int u, int v, int width, int height, int textureWidth, int textureHeight ) { - builder.vertex( transform, x, y + height, z ) - .color( r, g, b, 1.0f ) - .uv( u * TEX_SCALE, (v + textureHeight) * TEX_SCALE ) - .uv2( light ) - .endVertex(); - builder.vertex( transform, x + width, y + height, z ) - .color( r, g, b, 1.0f ) - .uv( (u + textureWidth) * TEX_SCALE, (v + textureHeight) * TEX_SCALE ) - .uv2( light ) - .endVertex(); - builder.vertex( transform, x + width, y, z ) - .color( r, g, b, 1.0f ) - .uv( (u + textureWidth) * TEX_SCALE, v * TEX_SCALE ) - .uv2( light ) - .endVertex(); - builder.vertex( transform, x, y, z ) - .color( r, g, b, 1.0f ) - .uv( u * TEX_SCALE, v * TEX_SCALE ) - .uv2( light ) - .endVertex(); + builder.vertex( transform, x, y + height, z ).color( r, g, b, 1.0f ).uv( u * TEX_SCALE, (v + textureHeight) * TEX_SCALE ).uv2( light ).endVertex(); + builder.vertex( transform, x + width, y + height, z ).color( r, g, b, 1.0f ).uv( (u + textureWidth) * TEX_SCALE, (v + textureHeight) * TEX_SCALE ).uv2( light ).endVertex(); + builder.vertex( transform, x + width, y, z ).color( r, g, b, 1.0f ).uv( (u + textureWidth) * TEX_SCALE, v * TEX_SCALE ).uv2( light ).endVertex(); + builder.vertex( transform, x, y, z ).color( r, g, b, 1.0f ).uv( u * TEX_SCALE, v * TEX_SCALE ).uv2( light ).endVertex(); } } diff --git a/src/main/java/dan200/computercraft/client/render/ItemMapLikeRenderer.java b/src/main/java/dan200/computercraft/client/render/ItemMapLikeRenderer.java index 62f3069f3..2c1eda00e 100644 --- a/src/main/java/dan200/computercraft/client/render/ItemMapLikeRenderer.java +++ b/src/main/java/dan200/computercraft/client/render/ItemMapLikeRenderer.java @@ -3,30 +3,36 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.client.render; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.math.Vector3f; import dan200.computercraft.fabric.mixin.ItemInHandRendererAccess; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.ItemInHandRenderer; import net.minecraft.client.renderer.MultiBufferSource; +import net.minecraft.client.renderer.block.model.ItemTransforms; import net.minecraft.util.Mth; import net.minecraft.world.InteractionHand; import net.minecraft.world.entity.HumanoidArm; +import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; -@Environment( EnvType.CLIENT ) public abstract class ItemMapLikeRenderer { - public void renderItemFirstPerson( - PoseStack transform, MultiBufferSource render, int lightTexture, InteractionHand hand, float pitch, float equipProgress, - float swingProgress, ItemStack stack - ) + /** + * The main rendering method for the item. + * + * @param transform The matrix transformation stack + * @param render The buffer to render to + * @param stack The stack to render + * @param light The packed lightmap coordinates. + * @see ItemInHandRenderer#renderItem(LivingEntity, ItemStack, ItemTransforms.TransformType, boolean, PoseStack, MultiBufferSource, int) + */ + protected abstract void renderItem( PoseStack transform, MultiBufferSource render, ItemStack stack, int light ); + + public void renderItemFirstPerson( PoseStack transform, MultiBufferSource render, int lightTexture, InteractionHand hand, float pitch, float equipProgress, float swingProgress, ItemStack stack ) { Player player = Minecraft.getInstance().player; @@ -37,61 +43,15 @@ public void renderItemFirstPerson( } else { - renderItemFirstPersonSide( transform, - render, - lightTexture, + renderItemFirstPersonSide( + transform, render, lightTexture, hand == InteractionHand.MAIN_HAND ? player.getMainArm() : player.getMainArm().getOpposite(), - equipProgress, - swingProgress, - stack ); + equipProgress, swingProgress, stack + ); } transform.popPose(); } - /** - * Render an item in the middle of the screen. - * - * @param transform The matrix transformation stack - * @param render The buffer to render to - * @param combinedLight The current light level - * @param pitch The pitch of the player - * @param equipProgress The equip progress of this item - * @param swingProgress The swing progress of this item - * @param stack The stack to render - */ - private void renderItemFirstPersonCenter( PoseStack transform, MultiBufferSource render, int combinedLight, float pitch, float equipProgress, - float swingProgress, ItemStack stack ) - { - Minecraft minecraft = Minecraft.getInstance(); - ItemInHandRenderer renderer = minecraft.getItemInHandRenderer(); - - // Setup the appropriate transformations. This is just copied from the - // corresponding method in ItemRenderer. - float swingRt = Mth.sqrt( swingProgress ); - float tX = -0.2f * Mth.sin( swingProgress * (float) Math.PI ); - float tZ = -0.4f * Mth.sin( swingRt * (float) Math.PI ); - transform.translate( 0, -tX / 2, tZ ); - - ItemInHandRendererAccess access = (ItemInHandRendererAccess) renderer; - float pitchAngle = access.callCalculateMapTilt( pitch ); - transform.translate( 0, 0.04F + equipProgress * -1.2f + pitchAngle * -0.5f, -0.72f ); - transform.mulPose( Vector3f.XP.rotationDegrees( pitchAngle * -85.0f ) ); - if( !minecraft.player.isInvisible() ) - { - transform.pushPose(); - transform.mulPose( Vector3f.YP.rotationDegrees( 90.0F ) ); - access.callRenderMapHand( transform, render, combinedLight, HumanoidArm.RIGHT ); - access.callRenderMapHand( transform, render, combinedLight, HumanoidArm.LEFT ); - transform.popPose(); - } - - float rX = Mth.sin( swingRt * (float) Math.PI ); - transform.mulPose( Vector3f.XP.rotationDegrees( rX * 20.0F ) ); - transform.scale( 2.0F, 2.0F, 2.0F ); - - renderItem( transform, render, stack, combinedLight ); - } - /** * Renders the item to one side of the player. * @@ -102,9 +62,9 @@ private void renderItemFirstPersonCenter( PoseStack transform, MultiBufferSource * @param equipProgress The equip progress of this item * @param swingProgress The swing progress of this item * @param stack The stack to render + * @see ItemInHandRenderer#renderOneHandedMap(PoseStack, MultiBufferSource, int, float, HumanoidArm, float, ItemStack) */ - private void renderItemFirstPersonSide( PoseStack transform, MultiBufferSource render, int combinedLight, HumanoidArm side, float equipProgress, - float swingProgress, ItemStack stack ) + private void renderItemFirstPersonSide( PoseStack transform, MultiBufferSource render, int combinedLight, HumanoidArm side, float equipProgress, float swingProgress, ItemStack stack ) { Minecraft minecraft = Minecraft.getInstance(); float offset = side == HumanoidArm.RIGHT ? 1f : -1f; @@ -115,8 +75,7 @@ private void renderItemFirstPersonSide( PoseStack transform, MultiBufferSource r { transform.pushPose(); transform.mulPose( Vector3f.ZP.rotationDegrees( offset * 10f ) ); - ((ItemInHandRendererAccess) minecraft.getItemInHandRenderer()) - .callRenderPlayerArm( transform, render, combinedLight, equipProgress, swingProgress, side ); + ((ItemInHandRendererAccess) minecraft.getItemInHandRenderer()).callRenderPlayerArm( transform, render, combinedLight, equipProgress, swingProgress, side ); transform.popPose(); } @@ -139,12 +98,46 @@ private void renderItemFirstPersonSide( PoseStack transform, MultiBufferSource r } /** - * The main rendering method for the item. + * Render an item in the middle of the screen. * - * @param transform The matrix transformation stack - * @param render The buffer to render to - * @param stack The stack to render - * @param light TODO rebase + * @param transform The matrix transformation stack + * @param render The buffer to render to + * @param combinedLight The current light level + * @param pitch The pitch of the player + * @param equipProgress The equip progress of this item + * @param swingProgress The swing progress of this item + * @param stack The stack to render + * @see ItemInHandRenderer#renderTwoHandedMap(PoseStack, MultiBufferSource, int, float, float, float) */ - protected abstract void renderItem( PoseStack transform, MultiBufferSource render, ItemStack stack, int light ); + private void renderItemFirstPersonCenter( PoseStack transform, MultiBufferSource render, int combinedLight, float pitch, float equipProgress, float swingProgress, ItemStack stack ) + { + Minecraft minecraft = Minecraft.getInstance(); + ItemInHandRenderer renderer = minecraft.getItemInHandRenderer(); + + // Setup the appropriate transformations. This is just copied from the + // corresponding method in ItemRenderer. + float swingRt = Mth.sqrt( swingProgress ); + float tX = -0.2f * Mth.sin( swingProgress * (float) Math.PI ); + float tZ = -0.4f * Mth.sin( swingRt * (float) Math.PI ); + transform.translate( 0, -tX / 2, tZ ); + + ItemInHandRendererAccess access = (ItemInHandRendererAccess) renderer; + float pitchAngle = access.callCalculateMapTilt( pitch ); + transform.translate( 0, 0.04F + equipProgress * -1.2f + pitchAngle * -0.5f, -0.72f ); + transform.mulPose( Vector3f.XP.rotationDegrees( pitchAngle * -85.0f ) ); + if( !minecraft.player.isInvisible() ) + { + transform.pushPose(); + transform.mulPose( Vector3f.YP.rotationDegrees( 90.0F ) ); + access.callRenderMapHand( transform, render, combinedLight, HumanoidArm.RIGHT ); + access.callRenderMapHand( transform, render, combinedLight, HumanoidArm.LEFT ); + transform.popPose(); + } + + float rX = Mth.sin( swingRt * (float) Math.PI ); + transform.mulPose( Vector3f.XP.rotationDegrees( rX * 20.0F ) ); + transform.scale( 2.0F, 2.0F, 2.0F ); + + renderItem( transform, render, stack, combinedLight ); + } } diff --git a/src/main/java/dan200/computercraft/client/render/ItemPocketRenderer.java b/src/main/java/dan200/computercraft/client/render/ItemPocketRenderer.java index 993c922f2..0c76cc505 100644 --- a/src/main/java/dan200/computercraft/client/render/ItemPocketRenderer.java +++ b/src/main/java/dan200/computercraft/client/render/ItemPocketRenderer.java @@ -3,10 +3,8 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.client.render; -import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.*; import com.mojang.math.Matrix4f; import com.mojang.math.Vector3f; @@ -17,9 +15,7 @@ import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.pocket.items.ItemPocketComputer; import dan200.computercraft.shared.util.Colour; -import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.MultiBufferSource; -import net.minecraft.client.renderer.RenderType; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.ItemStack; @@ -39,7 +35,7 @@ private ItemPocketRenderer() } @Override - protected void renderItem( PoseStack transform, MultiBufferSource render, ItemStack stack, int light ) + protected void renderItem( PoseStack transform, MultiBufferSource renderer, ItemStack stack, int light ) { ClientComputer computer = ItemPocketComputer.createClientComputer( stack ); Terminal terminal = computer == null ? null : computer.getTerminal(); @@ -67,7 +63,7 @@ protected void renderItem( PoseStack transform, MultiBufferSource render, ItemSt transform.scale( 0.5f, 0.5f, 0.5f ); float scale = 0.75f / Math.max( width + BORDER * 2, height + BORDER * 2 + LIGHT_HEIGHT ); - transform.scale( scale, scale, 0 ); + transform.scale( scale, scale, -1.0f ); transform.translate( -0.5 * width, -0.5 * height, 0 ); // Render the main frame @@ -75,26 +71,21 @@ protected void renderItem( PoseStack transform, MultiBufferSource render, ItemSt ComputerFamily family = item.getFamily(); int frameColour = item.getColour( stack ); - Matrix4f matrix = transform.last() - .pose(); - renderFrame( matrix, render, family, frameColour, light, width, height ); + Matrix4f matrix = transform.last().pose(); + renderFrame( matrix, renderer, family, frameColour, light, width, height ); // Render the light int lightColour = ItemPocketComputer.getLightState( stack ); - if( lightColour == -1 ) - { - lightColour = Colour.BLACK.getHex(); - } - renderLight( matrix, lightColour, width, height ); + if( lightColour == -1 ) lightColour = Colour.BLACK.getHex(); + renderLight( matrix, renderer, lightColour, width, height ); if( computer != null && terminal != null ) { FixedWidthFontRenderer.drawTerminal( - matrix, render.getBuffer( RenderTypes.TERMINAL_WITHOUT_DEPTH ), + matrix, renderer.getBuffer( RenderTypes.TERMINAL_WITHOUT_DEPTH ), MARGIN, MARGIN, terminal, !computer.isColour(), MARGIN, MARGIN, MARGIN, MARGIN ); - FixedWidthFontRenderer.drawBlocker( transform.last().pose(), render, 0, 0, width, height ); - + FixedWidthFontRenderer.drawBlocker( transform.last().pose(), renderer, 0, 0, width, height ); } else { @@ -106,45 +97,26 @@ protected void renderItem( PoseStack transform, MultiBufferSource render, ItemSt private static void renderFrame( Matrix4f transform, MultiBufferSource render, ComputerFamily family, int colour, int light, int width, int height ) { - RenderSystem.enableBlend(); - Minecraft.getInstance() - .getTextureManager() - .bindForSetup( colour != -1 ? ComputerBorderRenderer.BACKGROUND_COLOUR : ComputerBorderRenderer.getTexture( family ) ); - ResourceLocation 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; - ComputerBorderRenderer.render( transform, render.getBuffer( RenderType.text( texture ) ), 0, 0, 0, light, width, height, true, r, g, b ); + ComputerBorderRenderer.render( transform, render.getBuffer( ComputerBorderRenderer.getRenderType( texture ) ), 0, 0, 0, light, width, height, true, r, g, b ); } - private static void renderLight( Matrix4f transform, int colour, int width, int height ) + private static void renderLight( Matrix4f transform, MultiBufferSource render, int colour, int width, int height ) { - RenderSystem.disableTexture(); - float r = ((colour >>> 16) & 0xFF) / 255.0f; float g = ((colour >>> 8) & 0xFF) / 255.0f; float b = (colour & 0xFF) / 255.0f; + float z = 0.001f; - Tesselator tessellator = Tesselator.getInstance(); - BufferBuilder buffer = tessellator.getBuilder(); - buffer.begin( VertexFormat.Mode.QUADS, DefaultVertexFormat.POSITION_COLOR ); - buffer.vertex( transform, width - LIGHT_HEIGHT * 2, height + LIGHT_HEIGHT + BORDER / 2.0f, 0 ) - .color( r, g, b, 1.0f ) - .endVertex(); - buffer.vertex( transform, width, height + LIGHT_HEIGHT + BORDER / 2.0f, 0 ) - .color( r, g, b, 1.0f ) - .endVertex(); - buffer.vertex( transform, width, height + BORDER / 2.0f, 0 ) - .color( r, g, b, 1.0f ) - .endVertex(); - buffer.vertex( transform, width - LIGHT_HEIGHT * 2, height + BORDER / 2.0f, 0 ) - .color( r, g, b, 1.0f ) - .endVertex(); - - tessellator.end(); - RenderSystem.enableTexture(); + VertexConsumer buffer = render.getBuffer( RenderTypes.POSITION_COLOR ); + buffer.vertex( transform, width - LIGHT_HEIGHT * 2, height + LIGHT_HEIGHT + BORDER / 2.0f, z ).color( r, g, b, 1.0f ).endVertex(); + buffer.vertex( transform, width, height + LIGHT_HEIGHT + BORDER / 2.0f, z ).color( r, g, b, 1.0f ).endVertex(); + buffer.vertex( transform, width, height + BORDER / 2.0f, z ).color( r, g, b, 1.0f ).endVertex(); + buffer.vertex( transform, width - LIGHT_HEIGHT * 2, height + BORDER / 2.0f, z ).color( r, g, b, 1.0f ).endVertex(); } } diff --git a/src/main/java/dan200/computercraft/client/render/ItemPrintoutRenderer.java b/src/main/java/dan200/computercraft/client/render/ItemPrintoutRenderer.java index d7f3ca2a6..ed3451cb5 100644 --- a/src/main/java/dan200/computercraft/client/render/ItemPrintoutRenderer.java +++ b/src/main/java/dan200/computercraft/client/render/ItemPrintoutRenderer.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.mojang.blaze3d.vertex.PoseStack; @@ -30,6 +29,21 @@ private ItemPrintoutRenderer() { } + public boolean renderInFrame( PoseStack matrixStack, MultiBufferSource consumerProvider, ItemStack stack, int light ) + { + if( !(stack.getItem() instanceof ItemPrintout) ) return false; + + // Move a little bit forward to ensure we're not clipping with the frame + matrixStack.translate( 0.0f, 0.0f, -0.001f ); + matrixStack.mulPose( Vector3f.ZP.rotationDegrees( 180f ) ); + matrixStack.scale( 0.95f, 0.95f, -0.95f ); + matrixStack.translate( -0.5f, -0.5f, 0.0f ); + + drawPrintout( matrixStack, consumerProvider, stack, light ); + + return true; + } + @Override protected void renderItem( PoseStack transform, MultiBufferSource render, ItemStack stack, int light ) { @@ -49,10 +63,7 @@ private static void drawPrintout( PoseStack transform, MultiBufferSource render, double height = LINES_PER_PAGE * FONT_HEIGHT + Y_TEXT_MARGIN * 2; // Non-books will be left aligned - if( !book ) - { - width += offsetAt( pages ); - } + if( !book ) width += offsetAt( pages ); double visualWidth = width, visualHeight = height; @@ -70,27 +81,11 @@ private static void drawPrintout( PoseStack transform, MultiBufferSource render, transform.scale( scale, scale, scale ); transform.translate( (max - width) / 2.0, (max - height) / 2.0, 0.0 ); - Matrix4f matrix = transform.last() - .pose(); + Matrix4f matrix = transform.last().pose(); 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( PoseStack matrixStack, MultiBufferSource consumerProvider, ItemStack stack, int light ) - { - if( !(stack.getItem() instanceof ItemPrintout) ) - { - return false; - } - - // Move a little bit forward to ensure we're not clipping with the frame - matrixStack.translate( 0.0f, 0.0f, -0.001f ); - matrixStack.mulPose( Vector3f.ZP.rotationDegrees( 180f ) ); - matrixStack.scale( 0.95f, 0.95f, -0.95f ); - matrixStack.translate( -0.5f, -0.5f, 0.0f ); - - drawPrintout( matrixStack, consumerProvider, stack, light ); - - return true; + drawText( + matrix, render, X_TEXT_MARGIN, Y_TEXT_MARGIN, 0, light, + ItemPrintout.getText( stack ), ItemPrintout.getColours( stack ) + ); } } diff --git a/src/main/java/dan200/computercraft/client/render/MonitorHighlightRenderer.java b/src/main/java/dan200/computercraft/client/render/MonitorHighlightRenderer.java index 491b4514d..11e690747 100644 --- a/src/main/java/dan200/computercraft/client/render/MonitorHighlightRenderer.java +++ b/src/main/java/dan200/computercraft/client/render/MonitorHighlightRenderer.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.mojang.blaze3d.vertex.PoseStack; @@ -27,8 +26,8 @@ import static net.minecraft.core.Direction.*; /** - * Overrides monitor highlighting to only render the outline of the whole monitor, rather than the current block. This means you do not get an - * intrusive outline on top of the screen. + * Overrides monitor highlighting to only render the outline of the whole monitor, rather than the current + * block. This means you do not get an intrusive outline on top of the screen. */ @Environment( EnvType.CLIENT ) public final class MonitorHighlightRenderer @@ -37,115 +36,62 @@ private MonitorHighlightRenderer() { } - public static boolean drawHighlight( PoseStack matrixStack, VertexConsumer vertexConsumer, Entity entity, double d, double e, double f, BlockPos pos, BlockState blockState ) + public static boolean drawHighlight( PoseStack transformStack, VertexConsumer buffer, Entity entity, double d, double e, double f, BlockPos pos, BlockState blockState ) { // Preserve normal behaviour when crouching. - if( entity.isCrouching() ) - { - return false; - } + if( entity.isCrouching() ) return false; Level world = entity.getCommandSenderWorld(); BlockEntity tile = world.getBlockEntity( pos ); - if( !(tile instanceof TileMonitor monitor) ) - { - return false; - } + if( !(tile instanceof TileMonitor monitor) ) return false; // 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(); faces.remove( front ); - if( monitor.getXIndex() != 0 ) - { - faces.remove( monitor.getRight() - .getOpposite() ); - } - if( monitor.getXIndex() != monitor.getWidth() - 1 ) - { - faces.remove( monitor.getRight() ); - } - if( monitor.getYIndex() != 0 ) - { - faces.remove( monitor.getDown() - .getOpposite() ); - } - if( monitor.getYIndex() != monitor.getHeight() - 1 ) - { - faces.remove( monitor.getDown() ); - } + if( monitor.getXIndex() != 0 ) faces.remove( monitor.getRight().getOpposite() ); + if( monitor.getXIndex() != monitor.getWidth() - 1 ) faces.remove( monitor.getRight() ); + if( monitor.getYIndex() != 0 ) faces.remove( monitor.getDown().getOpposite() ); + if( monitor.getYIndex() != monitor.getHeight() - 1 ) faces.remove( monitor.getDown() ); - Vec3 cameraPos = Minecraft.getInstance().gameRenderer.getMainCamera() - .getPosition(); - matrixStack.pushPose(); - matrixStack.translate( pos.getX() - cameraPos.x(), pos.getY() - cameraPos.y(), pos.getZ() - cameraPos.z() ); + Vec3 cameraPos = Minecraft.getInstance().gameRenderer.getMainCamera().getPosition(); + transformStack.pushPose(); + transformStack.translate( pos.getX() - cameraPos.x(), pos.getY() - cameraPos.y(), pos.getZ() - cameraPos.z() ); // I wish I could think of a better way to do this - Matrix4f transform = matrixStack.last() - .pose(); - Matrix3f normal = matrixStack.last().normal(); - if( faces.contains( NORTH ) || faces.contains( WEST ) ) - { - line( vertexConsumer, transform, normal, 0, 0, 0, UP ); - } - if( faces.contains( SOUTH ) || faces.contains( WEST ) ) - { - line( vertexConsumer, transform, normal, 0, 0, 1, UP ); - } - if( faces.contains( NORTH ) || faces.contains( EAST ) ) - { - line( vertexConsumer, transform, normal, 1, 0, 0, UP ); - } - if( faces.contains( SOUTH ) || faces.contains( EAST ) ) - { - line( vertexConsumer, transform, normal, 1, 0, 1, UP ); - } - if( faces.contains( NORTH ) || faces.contains( DOWN ) ) - { - line( vertexConsumer, transform, normal, 0, 0, 0, EAST ); - } - if( faces.contains( SOUTH ) || faces.contains( DOWN ) ) - { - line( vertexConsumer, transform, normal, 0, 0, 1, EAST ); - } - if( faces.contains( NORTH ) || faces.contains( UP ) ) - { - line( vertexConsumer, transform, normal, 0, 1, 0, EAST ); - } - if( faces.contains( SOUTH ) || faces.contains( UP ) ) - { - line( vertexConsumer, transform, normal, 0, 1, 1, EAST ); - } - if( faces.contains( WEST ) || faces.contains( DOWN ) ) - { - line( vertexConsumer, transform, normal, 0, 0, 0, SOUTH ); - } - if( faces.contains( EAST ) || faces.contains( DOWN ) ) - { - line( vertexConsumer, transform, normal, 1, 0, 0, SOUTH ); - } - if( faces.contains( WEST ) || faces.contains( UP ) ) - { - line( vertexConsumer, transform, normal, 0, 1, 0, SOUTH ); - } - if( faces.contains( EAST ) || faces.contains( UP ) ) - { - line( vertexConsumer, transform, normal, 1, 1, 0, SOUTH ); - } - - matrixStack.popPose(); + Matrix4f transform = transformStack.last().pose(); + Matrix3f normal = transformStack.last().normal(); + if( faces.contains( NORTH ) || faces.contains( WEST ) ) line( buffer, transform, normal, 0, 0, 0, UP ); + if( faces.contains( SOUTH ) || faces.contains( WEST ) ) line( buffer, transform, normal, 0, 0, 1, UP ); + if( faces.contains( NORTH ) || faces.contains( EAST ) ) line( buffer, transform, normal, 1, 0, 0, UP ); + if( faces.contains( SOUTH ) || faces.contains( EAST ) ) line( buffer, transform, normal, 1, 0, 1, UP ); + if( faces.contains( NORTH ) || faces.contains( DOWN ) ) line( buffer, transform, normal, 0, 0, 0, EAST ); + if( faces.contains( SOUTH ) || faces.contains( DOWN ) ) line( buffer, transform, normal, 0, 0, 1, EAST ); + if( faces.contains( NORTH ) || faces.contains( UP ) ) line( buffer, transform, normal, 0, 1, 0, EAST ); + if( faces.contains( SOUTH ) || faces.contains( UP ) ) line( buffer, transform, normal, 0, 1, 1, EAST ); + if( faces.contains( WEST ) || faces.contains( DOWN ) ) line( buffer, transform, normal, 0, 0, 0, SOUTH ); + if( faces.contains( EAST ) || faces.contains( DOWN ) ) line( buffer, transform, normal, 1, 0, 0, SOUTH ); + if( faces.contains( WEST ) || faces.contains( UP ) ) line( buffer, transform, normal, 0, 1, 0, SOUTH ); + if( faces.contains( EAST ) || faces.contains( UP ) ) line( buffer, transform, normal, 1, 1, 0, SOUTH ); + transformStack.popPose(); return true; } private static void line( VertexConsumer buffer, Matrix4f transform, Matrix3f normal, float x, float y, float z, Direction direction ) { - buffer.vertex( transform, x, y, z ) + buffer + .vertex( transform, x, y, z ) .color( 0, 0, 0, 0.4f ) .normal( normal, direction.getStepX(), direction.getStepY(), direction.getStepZ() ) .endVertex(); - buffer.vertex( transform, x + direction.getStepX(), y + direction.getStepY(), z + direction.getStepZ() ) + buffer + .vertex( transform, + x + direction.getStepX(), + y + direction.getStepY(), + z + direction.getStepZ() + ) .color( 0, 0, 0, 0.4f ) .normal( normal, direction.getStepX(), direction.getStepY(), direction.getStepZ() ) .endVertex(); diff --git a/src/main/java/dan200/computercraft/client/render/MonitorTextureBufferShader.java b/src/main/java/dan200/computercraft/client/render/MonitorTextureBufferShader.java index 3602574ec..60135af87 100644 --- a/src/main/java/dan200/computercraft/client/render/MonitorTextureBufferShader.java +++ b/src/main/java/dan200/computercraft/client/render/MonitorTextureBufferShader.java @@ -29,13 +29,13 @@ public class MonitorTextureBufferShader extends ShaderInstance private final Uniform width; private final Uniform height; - public MonitorTextureBufferShader( ResourceProvider factory, String name, VertexFormat format ) throws IOException + public MonitorTextureBufferShader( ResourceProvider provider, String name, VertexFormat format ) throws IOException { - super( factory, name, format ); + super( provider, name, format ); width = getUniformChecked( "Width" ); height = getUniformChecked( "Height" ); - palette = new Uniform( "Palette", Uniform.UT_FLOAT3 /* UT_FLOAT3 */, 16 * 3, this ); + palette = new Uniform( "Palette", Uniform.UT_FLOAT3, 16 * 3, this ); updateUniformLocation( palette ); Uniform tbo = getUniformChecked( "Tbo" ); diff --git a/src/main/java/dan200/computercraft/client/render/PrintoutRenderer.java b/src/main/java/dan200/computercraft/client/render/PrintoutRenderer.java index 2d9e49afa..983a42774 100644 --- a/src/main/java/dan200/computercraft/client/render/PrintoutRenderer.java +++ b/src/main/java/dan200/computercraft/client/render/PrintoutRenderer.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.mojang.blaze3d.vertex.VertexConsumer; @@ -18,31 +17,38 @@ public final class PrintoutRenderer { + private static final float BG_SIZE = 256.0f; + /** * Width of a page. */ public static final int X_SIZE = 172; + /** * Height of a page. */ public static final int Y_SIZE = 209; + /** * Padding between the left and right of a page and the text. */ public static final int X_TEXT_MARGIN = 13; + /** * Padding between the top and bottom of a page and the text. */ public static final int Y_TEXT_MARGIN = 11; - /** - * Size of the leather cover. - */ - public static final int COVER_SIZE = 12; - private static final float BG_SIZE = 256.0f; + /** * Width of the extra page texture. */ private static final int X_FOLD_SIZE = 12; + + /** + * Size of the leather cover. + */ + public static final int COVER_SIZE = 12; + private static final int COVER_Y = Y_SIZE; private static final int COVER_X = X_SIZE + 4 * X_FOLD_SIZE; @@ -53,18 +59,11 @@ public static void drawText( Matrix4f transform, MultiBufferSource renderer, int VertexConsumer buffer = renderer.getBuffer( RenderTypes.PRINTOUT_TEXT ); for( int line = 0; line < LINES_PER_PAGE && line < text.length; line++ ) { - FixedWidthFontRenderer.drawString( transform, - buffer, - x, - y + line * FONT_HEIGHT, - text[start + line], - colours[start + line], - null, - Palette.DEFAULT, - false, - 0, - 0, - light ); + FixedWidthFontRenderer.drawString( transform, buffer, + x, y + line * FONT_HEIGHT, text[start + line], colours[start + line], null, Palette.DEFAULT, + false, 0, 0, + light + ); } } @@ -73,18 +72,12 @@ public static void drawText( Matrix4f transform, MultiBufferSource renderer, int VertexConsumer buffer = renderer.getBuffer( RenderTypes.PRINTOUT_TEXT ); for( int line = 0; line < LINES_PER_PAGE && line < text.length; line++ ) { - FixedWidthFontRenderer.drawString( transform, - buffer, - x, - y + line * FONT_HEIGHT, - new TextBuffer( text[start + line] ), - new TextBuffer( colours[start + line] ), - null, - Palette.DEFAULT, - false, - 0, - 0, - light ); + FixedWidthFontRenderer.drawString( transform, buffer, + x, y + line * FONT_HEIGHT, + new TextBuffer( text[start + line] ), new TextBuffer( colours[start + line] ), + null, Palette.DEFAULT, false, 0, 0, + light + ); } } @@ -107,18 +100,11 @@ public static void drawBorder( Matrix4f transform, MultiBufferSource renderer, f 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, - buffer, - x - offset, - y, - z - 0.02f, - X_SIZE + offset * 2, - Y_SIZE, - COVER_X + COVER_SIZE / 2.0f, - COVER_SIZE, - COVER_SIZE, - Y_SIZE, - light ); + drawTexture( transform, buffer, + x - offset, y, z - 0.02f, X_SIZE + offset * 2, Y_SIZE, + COVER_X + COVER_SIZE / 2.0f, COVER_SIZE, COVER_SIZE, Y_SIZE, + light + ); float borderX = left; while( borderX < right ) @@ -134,26 +120,27 @@ public static void drawBorder( Matrix4f transform, MultiBufferSource renderer, f 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, + 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, light ); + 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, light ); for( int n = 0; n <= rightPages; n++ ) { - drawTexture( transform, buffer, x + (X_SIZE - X_FOLD_SIZE) + offsetAt( n ), y, z - 1e-3f * 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, light ); + X_FOLD_SIZE * 2 + X_SIZE + (n == rightPages ? X_FOLD_SIZE : 0), 0, + X_FOLD_SIZE, Y_SIZE, light + ); } } - public static float offsetAt( int page ) - { - 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, int light ) { vertex( buffer, matrix, x, y + height, z, u / BG_SIZE, (v + height) / BG_SIZE, light ); @@ -162,8 +149,7 @@ private static void drawTexture( Matrix4f matrix, VertexConsumer buffer, float x 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, int 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, int light ) { 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 ); @@ -175,4 +161,9 @@ private static void vertex( VertexConsumer buffer, Matrix4f matrix, float x, flo { buffer.vertex( matrix, x, y, z ).color( 255, 255, 255, 255 ).uv( u, v ).uv2( light ).endVertex(); } + + public static float offsetAt( int page ) + { + return (float) (32 * (1 - Math.pow( 1.2, -page ))); + } } diff --git a/src/main/java/dan200/computercraft/client/render/RenderTypes.java b/src/main/java/dan200/computercraft/client/render/RenderTypes.java index 857bbd74f..6449ae09c 100644 --- a/src/main/java/dan200/computercraft/client/render/RenderTypes.java +++ b/src/main/java/dan200/computercraft/client/render/RenderTypes.java @@ -14,25 +14,26 @@ import net.minecraft.resources.ResourceLocation; 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 ShaderInstance terminalShader; public static final RenderType TERMINAL_WITHOUT_DEPTH = Types.TERMINAL_WITHOUT_DEPTH; - public static final RenderType MONITOR_TBO = Types.MONITOR_TBO; - public static final RenderType TERMINAL_BLOCKER = Types.BLOCKER; + public static final RenderType TERMINAL_BLOCKER = Types.TERMINAL_BLOCKER; public static final RenderType TERMINAL_WITH_DEPTH = Types.TERMINAL_WITH_DEPTH; + public static final RenderType MONITOR_TBO = Types.MONITOR_TBO; public static final RenderType PRINTOUT_TEXT = Types.PRINTOUT_TEXT; + /** + * This looks wrong (it should be POSITION_COLOR_TEX_LIGHTMAP surely!) but the fragment/vertex shader for that + * appear to entirely ignore the lightmap. + * + * Note that vanilla maps do the same, so this isn't unreasonable. + */ public static final RenderType PRINTOUT_BACKGROUND = RenderType.text( new ResourceLocation( "computercraft", "textures/gui/printout.png" ) ); public static final RenderType POSITION_COLOR = Types.POSITION_COLOR; @@ -53,24 +54,26 @@ static ShaderInstance getTerminalShader() private static final class Types extends RenderStateShard { - private static final VertexFormat.Mode GL_MODE = VertexFormat.Mode.TRIANGLES; - private static final VertexFormat FORMAT = DefaultVertexFormat.POSITION_COLOR_TEX; - private static final ShaderStateShard TERM_SHADER = new ShaderStateShard( RenderTypes::getTerminalShader ); - - private static final RenderStateShard.TextureStateShard TERM_FONT_TEXTURE = new RenderStateShard.TextureStateShard( + private static final RenderStateShard.TextureStateShard TERM_FONT_TEXTURE = new TextureStateShard( FixedWidthFontRenderer.FONT, false, false // blur, minimap ); + private static final VertexFormat TERM_FORMAT = DefaultVertexFormat.POSITION_COLOR_TEX; + private static final VertexFormat.Mode TERM_MODE = VertexFormat.Mode.TRIANGLES; + private static final ShaderStateShard TERM_SHADER = new ShaderStateShard( RenderTypes::getTerminalShader ); - public static final RenderType MONITOR_TBO = RenderType.create( "monitor_tbo", DefaultVertexFormat.POSITION_TEX, VertexFormat.Mode.TRIANGLE_STRIP, 128, false, false, // useDelegate, needsSorting + static final RenderType MONITOR_TBO = RenderType.create( + "monitor_tbo", DefaultVertexFormat.POSITION_TEX, VertexFormat.Mode.TRIANGLE_STRIP, 128, + false, false, // useDelegate, needsSorting RenderType.CompositeState.builder() - .setTextureState( TERM_FONT_TEXTURE ) // blur, minimap - .setShaderState( new RenderStateShard.ShaderStateShard( RenderTypes::getMonitorTextureBufferShader ) ) - .setWriteMaskState( RenderType.COLOR_DEPTH_WRITE ) - .createCompositeState( false ) ); + .setTextureState( TERM_FONT_TEXTURE ) + .setShaderState( new ShaderStateShard( RenderTypes::getMonitorTextureBufferShader ) ) + .setWriteMaskState( COLOR_WRITE ) + .createCompositeState( false ) + ); static final RenderType TERMINAL_WITHOUT_DEPTH = RenderType.create( - "terminal_without_depth", FORMAT, GL_MODE, 1024, + "terminal_without_depth", TERM_FORMAT, TERM_MODE, 1024, false, false, // useDelegate, needsSorting RenderType.CompositeState.builder() .setTextureState( TERM_FONT_TEXTURE ) @@ -79,15 +82,18 @@ private static final class Types extends RenderStateShard .createCompositeState( false ) ); - static final RenderType BLOCKER = RenderType.create( "terminal_blocker", FORMAT, GL_MODE, 256, false, false, // useDelegate, needsSorting + static final RenderType TERMINAL_BLOCKER = RenderType.create( + "terminal_blocker", TERM_FORMAT, TERM_MODE, 256, + false, false, // useDelegate, needsSorting RenderType.CompositeState.builder() .setTextureState( TERM_FONT_TEXTURE ) .setShaderState( TERM_SHADER ) .setWriteMaskState( DEPTH_WRITE ) - .createCompositeState( false ) ); + .createCompositeState( false ) + ); static final RenderType TERMINAL_WITH_DEPTH = RenderType.create( - "terminal_with_depth", FORMAT, GL_MODE, 1024, + "terminal_with_depth", TERM_FORMAT, TERM_MODE, 1024, false, false, // useDelegate, needsSorting RenderType.CompositeState.builder() .setTextureState( TERM_FONT_TEXTURE ) @@ -95,8 +101,11 @@ private static final class Types extends RenderStateShard .createCompositeState( false ) ); + /** + * A variant of {@link #TERMINAL_WITH_DEPTH} which uses the lightmap rather than rendering fullbright. + */ static final RenderType PRINTOUT_TEXT = RenderType.create( - "printout_text", DefaultVertexFormat.POSITION_COLOR_TEX_LIGHTMAP, GL_MODE, 1024, + "printout_text", DefaultVertexFormat.POSITION_COLOR_TEX_LIGHTMAP, TERM_MODE, 1024, false, false, // useDelegate, needsSorting RenderType.CompositeState.builder() .setTextureState( TERM_FONT_TEXTURE ) diff --git a/src/main/java/dan200/computercraft/client/render/TileEntityMonitorRenderer.java b/src/main/java/dan200/computercraft/client/render/TileEntityMonitorRenderer.java index 29c8ca657..588ccb0c8 100644 --- a/src/main/java/dan200/computercraft/client/render/TileEntityMonitorRenderer.java +++ b/src/main/java/dan200/computercraft/client/render/TileEntityMonitorRenderer.java @@ -3,10 +3,10 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.client.render; import com.mojang.blaze3d.platform.GlStateManager; +import com.mojang.blaze3d.platform.MemoryTracker; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.*; import com.mojang.math.Matrix4f; @@ -35,27 +35,25 @@ import javax.annotation.Nonnull; import java.nio.ByteBuffer; -import static com.mojang.blaze3d.platform.MemoryTracker.create; import static dan200.computercraft.client.gui.FixedWidthFontRenderer.*; 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. + * {@link TileMonitor#RENDER_MARGIN}, but a tiny bit of additional padding to ensure that there is no space between + * the monitor frame and contents. */ private static final float MARGIN = (float) (TileMonitor.RENDER_MARGIN * 1.1); - private static final Matrix4f IDENTITY = Transformation.identity() - .getMatrix(); private static ByteBuffer tboContents; + private static final Matrix4f IDENTITY = Transformation.identity().getMatrix(); + public TileEntityMonitorRenderer( BlockEntityRendererProvider.Context context ) { - // super( context ); } @Override - public void render( @Nonnull TileMonitor monitor, float partialTicks, @Nonnull PoseStack transform, @Nonnull MultiBufferSource renderer, - int lightmapCoord, int overlayLight ) + public void render( @Nonnull TileMonitor monitor, float partialTicks, @Nonnull PoseStack transform, @Nonnull MultiBufferSource renderer, int lightmapCoord, int overlayLight ) { // Render from the origin monitor ClientMonitor originTerminal = monitor.getClientMonitor(); @@ -86,15 +84,19 @@ public void render( @Nonnull TileMonitor monitor, float partialTicks, @Nonnull P // Setup initial transform transform.pushPose(); - transform.translate( originPos.getX() - monitorPos.getX() + 0.5, + transform.translate( + originPos.getX() - monitorPos.getX() + 0.5, originPos.getY() - monitorPos.getY() + 0.5, - originPos.getZ() - monitorPos.getZ() + 0.5 ); + originPos.getZ() - monitorPos.getZ() + 0.5 + ); transform.mulPose( Vector3f.YN.rotationDegrees( yaw ) ); transform.mulPose( Vector3f.XP.rotationDegrees( pitch ) ); - transform.translate( -0.5 + TileMonitor.RENDER_BORDER + TileMonitor.RENDER_MARGIN, + transform.translate( + -0.5 + TileMonitor.RENDER_BORDER + TileMonitor.RENDER_MARGIN, origin.getHeight() - 0.5 - (TileMonitor.RENDER_BORDER + TileMonitor.RENDER_MARGIN) + 0, - 0.50 ); + 0.5 + ); double xSize = origin.getWidth() - 2.0 * (TileMonitor.RENDER_MARGIN + TileMonitor.RENDER_BORDER); double ySize = origin.getHeight() - 2.0 * (TileMonitor.RENDER_MARGIN + TileMonitor.RENDER_BORDER); @@ -116,58 +118,56 @@ public void render( @Nonnull TileMonitor monitor, float partialTicks, @Nonnull P // 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, renderer.getBuffer( RenderTypes.TERMINAL_WITHOUT_DEPTH ), 0, 0, terminal, !originTerminal.isColour() ); + FixedWidthFontRenderer.drawCursor( + matrix, renderer.getBuffer( RenderTypes.TERMINAL_WITHOUT_DEPTH ), + 0, 0, terminal, !originTerminal.isColour() + ); transform.popPose(); - // Draw the background blocker FixedWidthFontRenderer.drawBlocker( transform.last().pose(), renderer, -MARGIN, MARGIN, (float) (xSize + 2 * MARGIN), (float) -(ySize + MARGIN * 2) ); + // Force a flush of the blocker. WorldRenderer.updateCameraAndRender will "finish" all the built-in + // buffers before calling renderer.finish, which means the blocker isn't actually rendered at that point! renderer.getBuffer( RenderType.solid() ); } else { - FixedWidthFontRenderer.drawEmptyTerminal( transform.last() - .pose(), - renderer, - -MARGIN, - MARGIN, - (float) (xSize + 2 * MARGIN), - (float) -(ySize + MARGIN * 2) ); + FixedWidthFontRenderer.drawEmptyTerminal( + transform.last().pose(), renderer, + -MARGIN, MARGIN, + (float) (xSize + 2 * MARGIN), (float) -(ySize + MARGIN * 2) + ); } transform.popPose(); } - private static void renderTerminal( MultiBufferSource renderer, Matrix4f matrix, ClientMonitor monitor, float xMargin, float yMargin ) + private static void renderTerminal( @Nonnull MultiBufferSource renderer, Matrix4f matrix, ClientMonitor monitor, float xMargin, float yMargin ) { Terminal terminal = monitor.getTerminal(); MonitorRenderer renderType = MonitorRenderer.current(); boolean redraw = monitor.pollTerminalChanged(); - if( monitor.createBuffer( renderType ) ) - { - redraw = true; - } + if( monitor.createBuffer( renderType ) ) redraw = true; switch( renderType ) { case TBO: { - int width = terminal.getWidth(), height = terminal.getHeight(); - int pixelWidth = width * FONT_WIDTH, pixelHeight = height * FONT_HEIGHT; + int pixelWidth = width * FONT_WIDTH, pixelHeight = height * FONT_HEIGHT; if( redraw ) { int size = width * height * 3; if( tboContents == null || tboContents.capacity() < size ) { - tboContents = create( size ); + tboContents = MemoryTracker.create( size ); } ByteBuffer monitorBuffer = tboContents; @@ -204,37 +204,34 @@ private static void renderTerminal( MultiBufferSource renderer, Matrix4f matrix, tboVertex( buffer, matrix, pixelWidth + xMargin, -yMargin ); tboVertex( buffer, matrix, pixelWidth + xMargin, pixelHeight + yMargin ); + // And force things to flush. We strictly speaking do this later on anyway for the cursor, but nice to + // be consistent. renderer.getBuffer( RenderTypes.TERMINAL_WITHOUT_DEPTH ); break; } case VBO: + { VertexBuffer vbo = monitor.buffer; if( redraw ) { Tesselator tessellator = Tesselator.getInstance(); BufferBuilder builder = tessellator.getBuilder(); builder.begin( RenderTypes.TERMINAL_WITHOUT_DEPTH.mode(), RenderTypes.TERMINAL_WITHOUT_DEPTH.format() ); - FixedWidthFontRenderer.drawTerminalWithoutCursor( IDENTITY, - builder, - 0, - 0, - terminal, - !monitor.isColour(), - yMargin, - yMargin, - xMargin, - xMargin ); + FixedWidthFontRenderer.drawTerminalWithoutCursor( + IDENTITY, builder, 0, 0, + terminal, !monitor.isColour(), yMargin, yMargin, xMargin, xMargin + ); builder.end(); vbo.upload( builder ); } renderer.getBuffer( RenderTypes.TERMINAL_WITHOUT_DEPTH ); - RenderTypes.TERMINAL_WITHOUT_DEPTH.setupRenderState(); vbo.drawWithShader( matrix, RenderSystem.getProjectionMatrix(), RenderTypes.getTerminalShader() ); break; + } } } diff --git a/src/main/java/dan200/computercraft/client/render/TileEntityTurtleRenderer.java b/src/main/java/dan200/computercraft/client/render/TileEntityTurtleRenderer.java index 0feb22c16..b1c788069 100644 --- a/src/main/java/dan200/computercraft/client/render/TileEntityTurtleRenderer.java +++ b/src/main/java/dan200/computercraft/client/render/TileEntityTurtleRenderer.java @@ -47,7 +47,7 @@ public class TileEntityTurtleRenderer implements BlockEntityRenderer private final Random random = new Random( 0 ); - BlockEntityRenderDispatcher renderer; + private final BlockEntityRenderDispatcher renderer; public TileEntityTurtleRenderer( BlockEntityRendererProvider.Context context ) { @@ -68,39 +68,28 @@ public static ModelResourceLocation getTurtleModel( ComputerFamily family, boole public static ModelResourceLocation getTurtleOverlayModel( ResourceLocation overlay, boolean christmas ) { - if( overlay != null ) - { - return new ModelResourceLocation( overlay, "inventory" ); - } - if( christmas ) - { - return ELF_OVERLAY_MODEL; - } + if( overlay != null ) return new ModelResourceLocation( overlay, "inventory" ); + if( christmas ) return ELF_OVERLAY_MODEL; return null; } @Override - public void render( @Nonnull TileTurtle turtle, float partialTicks, @Nonnull PoseStack transform, @Nonnull MultiBufferSource buffers, - int lightmapCoord, int overlayLight ) + public void render( @Nonnull TileTurtle turtle, float partialTicks, @Nonnull PoseStack transform, @Nonnull MultiBufferSource buffers, int lightmapCoord, int overlayLight ) { // Render the label - String label = turtle.createProxy() - .getLabel(); + String label = turtle.createProxy().getLabel(); HitResult hit = renderer.cameraHitResult; - if( label != null && hit.getType() == HitResult.Type.BLOCK && turtle.getBlockPos() - .equals( ((BlockHitResult) hit).getBlockPos() ) ) + if( label != null && hit.getType() == HitResult.Type.BLOCK && turtle.getBlockPos().equals( ((BlockHitResult) hit).getBlockPos() ) ) { Minecraft mc = Minecraft.getInstance(); Font font = mc.font; transform.pushPose(); transform.translate( 0.5, 1.2, 0.5 ); - transform.mulPose( mc.getEntityRenderDispatcher() - .cameraOrientation() ); + transform.mulPose( mc.getEntityRenderDispatcher().cameraOrientation() ); transform.scale( -0.025f, -0.025f, 0.025f ); - Matrix4f matrix = transform.last() - .pose(); + Matrix4f matrix = transform.last().pose(); int opacity = (int) (mc.options.getBackgroundOpacity( 0.25f ) * 255) << 24; float width = -font.width( label ) / 2.0f; font.drawInBatch( label, width, (float) 0, 0x20ffffff, false, matrix, buffers, true, opacity, lightmapCoord ); @@ -147,14 +136,10 @@ public void render( @Nonnull TileTurtle turtle, float partialTicks, @Nonnull Pos transform.popPose(); } - private void renderUpgrade( @Nonnull PoseStack transform, @Nonnull VertexConsumer renderer, int lightmapCoord, int overlayLight, TileTurtle turtle, - TurtleSide side, float f ) + private void renderUpgrade( @Nonnull PoseStack transform, @Nonnull VertexConsumer renderer, int lightmapCoord, int overlayLight, TileTurtle turtle, TurtleSide side, float f ) { ITurtleUpgrade upgrade = turtle.getUpgrade( side ); - if( upgrade == null ) - { - return; - } + if( upgrade == null ) return; transform.pushPose(); float toolAngle = turtle.getToolRenderAngle( side, f ); @@ -170,18 +155,13 @@ private void renderUpgrade( @Nonnull PoseStack transform, @Nonnull VertexConsume transform.popPose(); } - private void renderModel( @Nonnull PoseStack transform, @Nonnull VertexConsumer renderer, int lightmapCoord, int overlayLight, - ModelResourceLocation modelLocation, int[] tints ) + private void renderModel( @Nonnull PoseStack transform, @Nonnull VertexConsumer renderer, int lightmapCoord, int overlayLight, ModelResourceLocation modelLocation, int[] tints ) { - ModelManager modelManager = Minecraft.getInstance() - .getItemRenderer() - .getItemModelShaper() - .getModelManager(); + ModelManager modelManager = Minecraft.getInstance().getItemRenderer().getItemModelShaper().getModelManager(); renderModel( transform, renderer, lightmapCoord, overlayLight, modelManager.getModel( modelLocation ), tints ); } - private void renderModel( @Nonnull PoseStack transform, @Nonnull VertexConsumer renderer, int lightmapCoord, int overlayLight, BakedModel model, - int[] tints ) + private void renderModel( @Nonnull PoseStack 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 ); @@ -191,8 +171,7 @@ private void renderModel( @Nonnull PoseStack transform, @Nonnull VertexConsumer } } - private static void renderQuads( @Nonnull PoseStack transform, @Nonnull VertexConsumer buffer, int lightmapCoord, int overlayLight, - List quads, int[] tints ) + private static void renderQuads( @Nonnull PoseStack transform, @Nonnull VertexConsumer buffer, int lightmapCoord, int overlayLight, List quads, int[] tints ) { PoseStack.Pose matrix = transform.last(); @@ -202,10 +181,7 @@ private static void renderQuads( @Nonnull PoseStack transform, @Nonnull VertexCo if( tints != null && bakedquad.isTinted() ) { int idx = bakedquad.getTintIndex(); - if( idx >= 0 && idx < tints.length ) - { - tint = tints[bakedquad.getTintIndex()]; - } + if( idx >= 0 && idx < tints.length ) tint = tints[bakedquad.getTintIndex()]; } float f = (float) (tint >> 16 & 255) / 255.0F; diff --git a/src/main/java/dan200/computercraft/client/render/TurtleModelLoader.java b/src/main/java/dan200/computercraft/client/render/TurtleModelLoader.java index 01a32e812..d2f43d0c1 100644 --- a/src/main/java/dan200/computercraft/client/render/TurtleModelLoader.java +++ b/src/main/java/dan200/computercraft/client/render/TurtleModelLoader.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.mojang.datafixers.util.Pair; diff --git a/src/main/java/dan200/computercraft/client/render/TurtleMultiModel.java b/src/main/java/dan200/computercraft/client/render/TurtleMultiModel.java index 7f364725e..3e64b187f 100644 --- a/src/main/java/dan200/computercraft/client/render/TurtleMultiModel.java +++ b/src/main/java/dan200/computercraft/client/render/TurtleMultiModel.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.mojang.math.Transformation; @@ -32,8 +31,7 @@ public class TurtleMultiModel implements BakedModel private List generalQuads = null; private final Map> faceQuads = new EnumMap<>( Direction.class ); - public TurtleMultiModel( BakedModel baseModel, BakedModel overlayModel, Transformation generalTransform, TransformedModel leftUpgradeModel, - TransformedModel rightUpgradeModel ) + public TurtleMultiModel( BakedModel baseModel, BakedModel overlayModel, Transformation generalTransform, TransformedModel leftUpgradeModel, TransformedModel rightUpgradeModel ) { // Get the models this.baseModel = baseModel; @@ -45,22 +43,17 @@ public TurtleMultiModel( BakedModel baseModel, BakedModel overlayModel, Transfor @Nonnull @Override + @Deprecated public List getQuads( BlockState state, Direction side, @Nonnull Random rand ) { if( side != null ) { - if( !faceQuads.containsKey( side ) ) - { - faceQuads.put( side, buildQuads( state, side, rand ) ); - } + if( !faceQuads.containsKey( side ) ) faceQuads.put( side, buildQuads( state, side, rand ) ); return faceQuads.get( side ); } else { - if( generalQuads == null ) - { - generalQuads = buildQuads( state, side, rand ); - } + if( generalQuads == null ) generalQuads = buildQuads( state, side, rand ); return generalQuads; } } @@ -70,24 +63,20 @@ private List buildQuads( BlockState state, Direction side, Random ran ArrayList quads = new ArrayList<>(); - ModelTransformer.transformQuadsTo( quads, baseModel.getQuads( state, side, rand ), generalTransform.getMatrix() ); + transformQuadsTo( quads, baseModel.getQuads( state, side, rand ), generalTransform ); if( overlayModel != null ) { - ModelTransformer.transformQuadsTo( quads, overlayModel.getQuads( state, side, rand ), generalTransform.getMatrix() ); + transformQuadsTo( quads, overlayModel.getQuads( state, side, rand ), generalTransform ); } if( leftUpgradeModel != null ) { Transformation upgradeTransform = generalTransform.compose( leftUpgradeModel.getMatrix() ); - ModelTransformer.transformQuadsTo( quads, leftUpgradeModel.getModel() - .getQuads( state, side, rand ), - upgradeTransform.getMatrix() ); + transformQuadsTo( quads, leftUpgradeModel.getModel().getQuads( state, side, rand ), upgradeTransform ); } if( rightUpgradeModel != null ) { Transformation upgradeTransform = generalTransform.compose( rightUpgradeModel.getMatrix() ); - ModelTransformer.transformQuadsTo( quads, rightUpgradeModel.getModel() - .getQuads( state, side, rand ), - upgradeTransform.getMatrix() ); + transformQuadsTo( quads, rightUpgradeModel.getModel().getQuads( state, side, rand ), upgradeTransform ); } quads.trimToSize(); return quads; @@ -106,15 +95,15 @@ public boolean isGui3d() } @Override - public boolean usesBlockLight() + public boolean isCustomRenderer() { - return baseModel.usesBlockLight(); + return baseModel.isCustomRenderer(); } @Override - public boolean isCustomRenderer() + public boolean usesBlockLight() { - return baseModel.isCustomRenderer(); + return baseModel.usesBlockLight(); } @Nonnull @@ -139,4 +128,9 @@ public ItemOverrides getOverrides() { return ItemOverrides.EMPTY; } + + private void transformQuadsTo( List output, List quads, Transformation transform ) + { + ModelTransformer.transformQuadsTo( output, quads, transform.getMatrix() ); + } } diff --git a/src/main/java/dan200/computercraft/client/render/TurtlePlayerRenderer.java b/src/main/java/dan200/computercraft/client/render/TurtlePlayerRenderer.java index 047ebd0e1..2a2de5127 100644 --- a/src/main/java/dan200/computercraft/client/render/TurtlePlayerRenderer.java +++ b/src/main/java/dan200/computercraft/client/render/TurtlePlayerRenderer.java @@ -15,22 +15,21 @@ import javax.annotation.Nonnull; public class TurtlePlayerRenderer extends EntityRenderer -{ //FIXME Make sure this isn't an issue. Context was EntityRenderDispatcher. - public TurtlePlayerRenderer( EntityRendererProvider.Context context ) +{ + public TurtlePlayerRenderer( EntityRendererProvider.Context renderManager ) { - super( context ); + super( renderManager ); } + @Nonnull @Override - public void render( @Nonnull TurtlePlayer entityIn, float entityYaw, float partialTicks, @Nonnull PoseStack transform, - @Nonnull MultiBufferSource buffer, int packedLightIn ) + public ResourceLocation getTextureLocation( @Nonnull TurtlePlayer entity ) { + return ComputerBorderRenderer.BACKGROUND_NORMAL; } - @Nonnull @Override - public ResourceLocation getTextureLocation( @Nonnull TurtlePlayer entity ) + public void render( @Nonnull TurtlePlayer entityIn, float entityYaw, float partialTicks, @Nonnull PoseStack transform, @Nonnull MultiBufferSource buffer, int packedLightIn ) { - return ComputerBorderRenderer.BACKGROUND_NORMAL; } } diff --git a/src/main/java/dan200/computercraft/client/render/TurtleSmartItemModel.java b/src/main/java/dan200/computercraft/client/render/TurtleSmartItemModel.java index 1b575234f..dfe684fc9 100644 --- a/src/main/java/dan200/computercraft/client/render/TurtleSmartItemModel.java +++ b/src/main/java/dan200/computercraft/client/render/TurtleSmartItemModel.java @@ -5,7 +5,6 @@ */ package dan200.computercraft.client.render; -import com.google.common.base.Objects; import com.mojang.blaze3d.vertex.PoseStack; import com.mojang.math.Transformation; import dan200.computercraft.api.client.TransformedModel; @@ -50,59 +49,18 @@ public class TurtleSmartItemModel implements BakedModel stack.translate( 0, 0, 1 ); identity = Transformation.identity(); - flip = new Transformation( stack.last() - .pose() ); + flip = new Transformation( stack.last().pose() ); } - private static class TurtleModelCombination + private static record TurtleModelCombination( + boolean colour, + ITurtleUpgrade leftUpgrade, + ITurtleUpgrade rightUpgrade, + ResourceLocation overlay, + boolean christmas, + boolean flip + ) { - final boolean colour; - final ITurtleUpgrade leftUpgrade; - final ITurtleUpgrade rightUpgrade; - final ResourceLocation overlay; - final boolean christmas; - final boolean flip; - - TurtleModelCombination( boolean colour, ITurtleUpgrade leftUpgrade, ITurtleUpgrade rightUpgrade, ResourceLocation 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; @@ -121,7 +79,7 @@ public TurtleSmartItemModel( BakedModel familyModel, BakedModel colourModel ) { @Nonnull @Override - public BakedModel resolve( BakedModel originalModel, ItemStack stack, @Nullable ClientLevel world, @Nullable LivingEntity entity, int seed ) + public BakedModel resolve( @Nonnull BakedModel originalModel, @Nonnull ItemStack stack, @Nullable ClientLevel world, @Nullable LivingEntity entity, int random ) { ItemTurtle turtle = (ItemTurtle) stack.getItem(); int colour = turtle.getColour( stack ); @@ -134,21 +92,23 @@ public BakedModel resolve( BakedModel originalModel, ItemStack stack, @Nullable TurtleModelCombination combo = new TurtleModelCombination( colour != -1, leftUpgrade, rightUpgrade, overlay, christmas, flip ); BakedModel model = cachedModels.get( combo ); - if( model == null ) - { - cachedModels.put( combo, model = buildModel( combo ) ); - } + if( model == null ) cachedModels.put( combo, model = buildModel( combo ) ); return model; } }; } + @Nonnull + @Override + public ItemOverrides getOverrides() + { + return overrides; + } + private BakedModel buildModel( TurtleModelCombination combo ) { Minecraft mc = Minecraft.getInstance(); - ModelManager modelManager = mc.getItemRenderer() - .getItemModelShaper() - .getModelManager(); + ModelManager modelManager = mc.getItemRenderer().getItemModelShaper().getModelManager(); ModelResourceLocation overlayModelLocation = TileEntityTurtleRenderer.getTurtleOverlayModel( combo.overlay, combo.christmas ); BakedModel baseModel = combo.colour ? colourModel : familyModel; @@ -179,12 +139,6 @@ public boolean isGui3d() return familyModel.isGui3d(); } - @Override - public boolean usesBlockLight() - { - return familyModel.usesBlockLight(); - } - @Override public boolean isCustomRenderer() { @@ -192,17 +146,17 @@ public boolean isCustomRenderer() } @Override - @Deprecated - public TextureAtlasSprite getParticleIcon() + public boolean usesBlockLight() { - return familyModel.getParticleIcon(); + return familyModel.usesBlockLight(); } @Nonnull @Override - public ItemOverrides getOverrides() + @Deprecated + public TextureAtlasSprite getParticleIcon() { - return overrides; + return familyModel.getParticleIcon(); } @Nonnull diff --git a/src/main/java/dan200/computercraft/core/apis/FSAPI.java b/src/main/java/dan200/computercraft/core/apis/FSAPI.java index 9e1059545..6c3fd3522 100644 --- a/src/main/java/dan200/computercraft/core/apis/FSAPI.java +++ b/src/main/java/dan200/computercraft/core/apis/FSAPI.java @@ -484,7 +484,7 @@ public final Object getFreeSpace( String path ) throws LuaException * * This string is formatted like a normal path string, but can include any * number of wildcards ({@code *}) to look for files matching anything. - * For example, {@code rom/* /command*} will look for any path starting with + * For example, rom/*/command* will look for any path starting with * {@code command} inside any subdirectory of {@code /rom}. * * @param path The wildcard-qualified path to search for. diff --git a/src/main/java/dan200/computercraft/core/apis/PeripheralAPI.java b/src/main/java/dan200/computercraft/core/apis/PeripheralAPI.java index 02e382090..35e20ca03 100644 --- a/src/main/java/dan200/computercraft/core/apis/PeripheralAPI.java +++ b/src/main/java/dan200/computercraft/core/apis/PeripheralAPI.java @@ -17,6 +17,7 @@ import dan200.computercraft.core.asm.PeripheralMethod; import dan200.computercraft.core.computer.ComputerSide; import dan200.computercraft.core.tracking.TrackingField; +import dan200.computercraft.shared.util.LuaUtil; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -36,6 +37,7 @@ private class PeripheralWrapper extends ComputerAccess private final IPeripheral peripheral; private final String type; + private final Set additionalTypes; private final Map methodMap; private boolean attached; @@ -47,6 +49,7 @@ private class PeripheralWrapper extends ComputerAccess attached = false; type = Objects.requireNonNull( peripheral.getType(), "Peripheral type cannot be null" ); + additionalTypes = peripheral.getAdditionalTypes(); methodMap = PeripheralAPI.getMethods( peripheral ); } @@ -61,6 +64,11 @@ public String getType() return type; } + public Set getAdditionalTypes() + { + return additionalTypes; + } + public Collection getMethods() { return methodMap.keySet(); @@ -298,7 +306,23 @@ public final Object[] getType( String sideName ) synchronized( peripherals ) { PeripheralWrapper p = peripherals[side.ordinal()]; - if( p != null ) return new Object[] { p.getType() }; + return p == null ? null : LuaUtil.consArray( p.getType(), p.getAdditionalTypes() ); + } + } + + @LuaFunction + public final Object[] hasType( String sideName, String type ) + { + ComputerSide side = ComputerSide.valueOfInsensitive( sideName ); + if( side == null ) return null; + + synchronized( peripherals ) + { + PeripheralWrapper p = peripherals[side.ordinal()]; + if( p != null ) + { + return new Object[] { p.getType().equals( type ) || p.getAdditionalTypes().contains( type ) }; + } } return null; } diff --git a/src/main/java/dan200/computercraft/core/apis/handles/BinaryWritableHandle.java b/src/main/java/dan200/computercraft/core/apis/handles/BinaryWritableHandle.java index 39e234648..0e3e7dd8b 100644 --- a/src/main/java/dan200/computercraft/core/apis/handles/BinaryWritableHandle.java +++ b/src/main/java/dan200/computercraft/core/apis/handles/BinaryWritableHandle.java @@ -100,7 +100,7 @@ public final void flush() throws LuaException try { // Technically this is not needed - if( writer instanceof FileChannel ) ((FileChannel) writer).force( false ); + if( writer instanceof FileChannel channel ) channel.force( false ); } catch( IOException ignored ) { diff --git a/src/main/java/dan200/computercraft/core/apis/http/websocket/WebsocketHandle.java b/src/main/java/dan200/computercraft/core/apis/http/websocket/WebsocketHandle.java index 0901cc25e..1d34b1458 100644 --- a/src/main/java/dan200/computercraft/core/apis/http/websocket/WebsocketHandle.java +++ b/src/main/java/dan200/computercraft/core/apis/http/websocket/WebsocketHandle.java @@ -153,7 +153,7 @@ else if( event.length >= 2 && Objects.equal( event[0], CLOSE_EVENT ) && Objects. return MethodResult.of(); } else if( event.length >= 2 && timeoutId != -1 && Objects.equal( event[0], TIMER_EVENT ) - && event[1] instanceof Number && ((Number) event[1]).intValue() == timeoutId ) + && event[1] instanceof Number id && id.intValue() == timeoutId ) { // If we received a matching timer event then abort. return MethodResult.of(); diff --git a/src/main/java/dan200/computercraft/fabric/mixin/MixinServerPlayerGameMode.java b/src/main/java/dan200/computercraft/fabric/mixin/MixinServerPlayerGameMode.java index 3a15aedda..3fd00bef9 100644 --- a/src/main/java/dan200/computercraft/fabric/mixin/MixinServerPlayerGameMode.java +++ b/src/main/java/dan200/computercraft/fabric/mixin/MixinServerPlayerGameMode.java @@ -5,7 +5,7 @@ */ package dan200.computercraft.fabric.mixin; -import dan200.computercraft.shared.ComputerCraftRegistry; +import dan200.computercraft.shared.Registry; import net.minecraft.advancements.CriteriaTriggers; import net.minecraft.core.BlockPos; import net.minecraft.server.level.ServerPlayer; @@ -33,7 +33,7 @@ private void useItemOn( ServerPlayer player, Level world, ItemStack stack, Inter { BlockPos pos = hitResult.getBlockPos(); BlockState state = world.getBlockState( pos ); - if( player.getMainHandItem().getItem() == ComputerCraftRegistry.ModItems.DISK && state.getBlock() == ComputerCraftRegistry.ModBlocks.DISK_DRIVE ) + if( player.getMainHandItem().getItem() == Registry.ModItems.DISK && state.getBlock() == Registry.ModBlocks.DISK_DRIVE ) { InteractionResult actionResult = state.use( world, player, hand, hitResult ); if( actionResult.consumesAction() ) diff --git a/src/main/java/dan200/computercraft/shared/BundledRedstone.java b/src/main/java/dan200/computercraft/shared/BundledRedstone.java index d99e13f9d..94ce77ff5 100644 --- a/src/main/java/dan200/computercraft/shared/BundledRedstone.java +++ b/src/main/java/dan200/computercraft/shared/BundledRedstone.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared; import dan200.computercraft.ComputerCraft; @@ -34,18 +33,9 @@ public static int getDefaultOutput( @Nonnull Level world, @Nonnull BlockPos pos, return world.isInWorldBounds( pos ) ? DefaultBundledRedstoneProvider.getDefaultBundledRedstoneOutput( world, pos, side ) : -1; } - public static int getOutput( Level world, BlockPos pos, Direction side ) - { - int signal = getUnmaskedOutput( world, pos, side ); - return signal >= 0 ? signal : 0; - } - private static int getUnmaskedOutput( Level world, BlockPos pos, Direction side ) { - if( !world.isInWorldBounds( pos ) ) - { - return -1; - } + if( !world.isInWorldBounds( pos ) ) return -1; // Try the providers in order: int combinedSignal = -1; @@ -67,4 +57,10 @@ private static int getUnmaskedOutput( Level world, BlockPos pos, Direction side return combinedSignal; } + + public static int getOutput( Level world, BlockPos pos, Direction side ) + { + int signal = getUnmaskedOutput( world, pos, side ); + return signal >= 0 ? signal : 0; + } } diff --git a/src/main/java/dan200/computercraft/shared/ComputerCraftRegistry.java b/src/main/java/dan200/computercraft/shared/ComputerCraftRegistry.java deleted file mode 100644 index ffc9e7b87..000000000 --- a/src/main/java/dan200/computercraft/shared/ComputerCraftRegistry.java +++ /dev/null @@ -1,330 +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; - -import dan200.computercraft.ComputerCraft; -import dan200.computercraft.api.ComputerCraftAPI; -import dan200.computercraft.shared.common.ContainerHeldItem; -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.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; -import dan200.computercraft.shared.peripheral.modem.wired.*; -import dan200.computercraft.shared.peripheral.modem.wireless.BlockWirelessModem; -import dan200.computercraft.shared.peripheral.modem.wireless.TileWirelessModem; -import dan200.computercraft.shared.peripheral.monitor.BlockMonitor; -import dan200.computercraft.shared.peripheral.monitor.TileMonitor; -import dan200.computercraft.shared.peripheral.printer.BlockPrinter; -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.items.ItemPocketComputer; -import dan200.computercraft.shared.pocket.peripherals.PocketModem; -import dan200.computercraft.shared.pocket.peripherals.PocketSpeaker; -import dan200.computercraft.shared.turtle.blocks.BlockTurtle; -import dan200.computercraft.shared.turtle.blocks.TileTurtle; -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 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.fabricmc.fabric.api.tool.attribute.v1.FabricToolTags; -import net.minecraft.core.BlockPos; -import net.minecraft.core.Registry; -import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.entity.EntityType; -import net.minecraft.world.entity.MobCategory; -import net.minecraft.world.inventory.AbstractContainerMenu; -import net.minecraft.world.inventory.MenuType; -import net.minecraft.world.item.BlockItem; -import net.minecraft.world.item.CreativeModeTab; -import net.minecraft.world.item.Item; -import net.minecraft.world.item.Items; -import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.SoundType; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.entity.BlockEntityType; -import net.minecraft.world.level.block.state.BlockBehaviour; -import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.material.Material; - -import java.util.function.BiFunction; - -import static net.minecraft.core.Registry.BLOCK_ENTITY_TYPE; - -public final class ComputerCraftRegistry -{ - public static final String MOD_ID = ComputerCraft.MOD_ID; - - public static void init() - { - Object[] o = { - ModTiles.CABLE, - ModBlocks.CABLE, - ModItems.CABLE, - ModEntities.TURTLE_PLAYER, - ModContainers.COMPUTER, - }; - - TurtleUpgrades.registerTurtleUpgrades(); - PocketUpgrades.registerPocketUpgrades(); - } - - public static final class ModBlocks - { - public static final BlockComputer COMPUTER_NORMAL = register( "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, - 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, - ComputerCraftRegistry.ModTiles.COMPUTER_COMMAND ) ); - public static final BlockTurtle TURTLE_NORMAL = register( "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, 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, 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(), ComputerCraftRegistry.ModTiles.WIRELESS_MODEM_NORMAL, ComputerFamily.NORMAL ) ); - public static final BlockWirelessModem WIRELESS_MODEM_ADVANCED = register( "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( modemProperties(), ComputerCraftRegistry.ModTiles.WIRED_MODEM_FULL ) ); - public static final BlockCable CABLE = register( "cable", new BlockCable( modemProperties() ) ); - - private static BlockBehaviour.Properties properties() - { - //return FabricBlockSettings.copyOf(Blocks.GLASS) - // .strength(2); - return BlockBehaviour.Properties.of( Material.GLASS ) - .strength( 2F ) - .sound( SoundType.STONE ) - .noOcclusion(); - } - - private static BlockBehaviour.Properties turtleProperties() - { - return FabricBlockSettings.copyOf( Blocks.STONE ) - .strength( 2.5f ); - } - - private static BlockBehaviour.Properties modemProperties() - { - return FabricBlockSettings.copyOf( Blocks.STONE ) - .breakByHand( true ) - .breakByTool( FabricToolTags.PICKAXES ) - .strength( 1.5f ); - } - - public static T register( String id, T value ) - { - return Registry.register( Registry.BLOCK, new ResourceLocation( MOD_ID, id ), value ); - } - } - - public static class ModTiles - { - - public static final BlockEntityType MONITOR_NORMAL = ofBlock( ModBlocks.MONITOR_NORMAL, - "monitor_normal", - ( blockPos, blockState ) -> new TileMonitor( ModTiles.MONITOR_NORMAL, false, blockPos, blockState ) ); - public static final BlockEntityType MONITOR_ADVANCED = ofBlock( ModBlocks.MONITOR_ADVANCED, - "monitor_advanced", - ( blockPos, blockState ) -> new TileMonitor( ModTiles.MONITOR_ADVANCED, true, blockPos, blockState ) ); - public static final BlockEntityType COMPUTER_NORMAL = ofBlock( ModBlocks.COMPUTER_NORMAL, - "computer_normal", - ( blockPos, blockState ) -> new TileComputer( ComputerFamily.NORMAL, ModTiles.COMPUTER_NORMAL, blockPos, blockState ) ); - public static final BlockEntityType COMPUTER_ADVANCED = ofBlock( ModBlocks.COMPUTER_ADVANCED, - "computer_advanced", - ( blockPos, blockState ) -> new TileComputer( ComputerFamily.ADVANCED, ModTiles.COMPUTER_ADVANCED, blockPos, blockState ) ); - public static final BlockEntityType COMPUTER_COMMAND = ofBlock( ModBlocks.COMPUTER_COMMAND, - "computer_command", - ( blockPos, blockState ) -> new TileCommandComputer( ComputerFamily.COMMAND, ModTiles.COMPUTER_COMMAND, blockPos, blockState ) ); - public static final BlockEntityType TURTLE_NORMAL = ofBlock( ModBlocks.TURTLE_NORMAL, - "turtle_normal", - ( blockPos, blockState ) -> new TileTurtle( ModTiles.TURTLE_NORMAL, blockPos, blockState, ComputerFamily.NORMAL ) ); - public static final BlockEntityType TURTLE_ADVANCED = ofBlock( ModBlocks.TURTLE_ADVANCED, - "turtle_advanced", - ( 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", - ( 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", - ( 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", - ( blockPos, blockState ) -> new TileWirelessModem( ModTiles.WIRELESS_MODEM_ADVANCED, true, blockPos, blockState ) ); - - 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 ResourceLocation( MOD_ID, id ), - blockEntityType - ); - } - } - - public static final class ModItems - { - private static final CreativeModeTab mainItemGroup = ComputerCraft.MAIN_GROUP; - public static final ItemComputer COMPUTER_NORMAL = ofBlock( ModBlocks.COMPUTER_NORMAL, ItemComputer::new ); - public static final ItemComputer COMPUTER_ADVANCED = ofBlock( ModBlocks.COMPUTER_ADVANCED, ItemComputer::new ); - public static final ItemComputer COMPUTER_COMMAND = ofBlock( ModBlocks.COMPUTER_COMMAND, ItemComputer::new ); - public static final ItemPocketComputer POCKET_COMPUTER_NORMAL = register( "pocket_computer_normal", - new ItemPocketComputer( properties().stacksTo( 1 ), ComputerFamily.NORMAL ) ); - public static final ItemPocketComputer POCKET_COMPUTER_ADVANCED = register( "pocket_computer_advanced", - new ItemPocketComputer( properties().stacksTo( 1 ), - ComputerFamily.ADVANCED ) ); - public static final ItemTurtle TURTLE_NORMAL = ofBlock( ModBlocks.TURTLE_NORMAL, ItemTurtle::new ); - public static final ItemTurtle TURTLE_ADVANCED = ofBlock( ModBlocks.TURTLE_ADVANCED, ItemTurtle::new ); - public static final ItemDisk DISK = register( "disk", new ItemDisk( properties().stacksTo( 1 ) ) ); - public static final ItemTreasureDisk TREASURE_DISK = register( "treasure_disk", new ItemTreasureDisk( properties().stacksTo( 1 ) ) ); - public static final ItemPrintout PRINTED_PAGE = register( "printed_page", new ItemPrintout( properties().stacksTo( 1 ), ItemPrintout.Type.PAGE ) ); - public static final ItemPrintout PRINTED_PAGES = register( "printed_pages", new ItemPrintout( properties().stacksTo( 1 ), ItemPrintout.Type.PAGES ) ); - public static final ItemPrintout PRINTED_BOOK = register( "printed_book", new ItemPrintout( properties().stacksTo( 1 ), ItemPrintout.Type.BOOK ) ); - public static final BlockItem SPEAKER = ofBlock( ModBlocks.SPEAKER, BlockItem::new ); - public static final BlockItem DISK_DRIVE = ofBlock( ModBlocks.DISK_DRIVE, BlockItem::new ); - public static final BlockItem PRINTER = ofBlock( ModBlocks.PRINTER, BlockItem::new ); - public static final BlockItem MONITOR_NORMAL = ofBlock( ModBlocks.MONITOR_NORMAL, BlockItem::new ); - public static final BlockItem MONITOR_ADVANCED = ofBlock( ModBlocks.MONITOR_ADVANCED, BlockItem::new ); - public static final BlockItem WIRELESS_MODEM_NORMAL = ofBlock( ModBlocks.WIRELESS_MODEM_NORMAL, BlockItem::new ); - public static final BlockItem WIRELESS_MODEM_ADVANCED = ofBlock( ModBlocks.WIRELESS_MODEM_ADVANCED, BlockItem::new ); - public static final BlockItem WIRED_MODEM_FULL = ofBlock( ModBlocks.WIRED_MODEM_FULL, BlockItem::new ); - public static final ItemBlockCable.Cable CABLE = register( "cable", new ItemBlockCable.Cable( ModBlocks.CABLE, properties() ) ); - public static final ItemBlockCable.WiredModem WIRED_MODEM = register( "wired_modem", new ItemBlockCable.WiredModem( ModBlocks.CABLE, properties() ) ); - - private static I ofBlock( B parent, BiFunction supplier ) - { - return Registry.register( Registry.ITEM, Registry.BLOCK.getKey( parent ), supplier.apply( parent, properties() ) ); - } - - private static Item.Properties properties() - { - return new Item.Properties().tab( mainItemGroup ); - } - - private static T register( String id, T item ) - { - return Registry.register( Registry.ITEM, new ResourceLocation( MOD_ID, id ), item ); - } - } - - public static class ModEntities - { - public static final EntityType TURTLE_PLAYER = Registry.register( Registry.ENTITY_TYPE, - new ResourceLocation( MOD_ID, "turtle_player" ), - EntityType.Builder.createNothing( MobCategory.MISC ).noSave() - .noSummon() - .sized( - 0, - 0 ) - .build( - ComputerCraft.MOD_ID + ":turtle_player" ) ); - } - - public static class ModContainers - { - public static final MenuType COMPUTER = ContainerData.toType( new ResourceLocation( MOD_ID, "computer" ), ModContainers.COMPUTER, ComputerContainerData::new, ComputerMenuWithoutInventory::new ); - public static final MenuType POCKET_COMPUTER = ContainerData.toType( new ResourceLocation( MOD_ID, "pocket_computer" ), ModContainers.POCKET_COMPUTER, ComputerContainerData::new, ComputerMenuWithoutInventory::new ); - public static final MenuType POCKET_COMPUTER_NO_TERM = ContainerData.toType( new ResourceLocation( MOD_ID, "pocket_computer_no_term" ), ModContainers.POCKET_COMPUTER_NO_TERM, ComputerContainerData::new, ComputerMenuWithoutInventory::new ); - public static final MenuType TURTLE = ContainerData.toType( new ResourceLocation( MOD_ID, "turtle" ), ComputerContainerData::new, ContainerTurtle::new ); - public static final MenuType DISK_DRIVE = registerSimple( "disk_drive", ContainerDiskDrive::new ); - public static final MenuType PRINTER = registerSimple( "printer", ContainerPrinter::new ); - public static final MenuType PRINTOUT = ContainerData.toType( new ResourceLocation( MOD_ID, "printout" ), HeldItemContainerData::new, ContainerHeldItem::createPrintout ); - public static final MenuType VIEW_COMPUTER = ContainerData.toType( new ResourceLocation( MOD_ID, "view_computer" ), ViewComputerContainerData::new, ContainerViewComputer::new ); - - private static MenuType registerSimple( String id, - ScreenHandlerRegistry.SimpleClientHandlerFactory function ) - { - return ScreenHandlerRegistry.registerSimple( new ResourceLocation( MOD_ID, id ), function ); - } - } - - public static final class TurtleUpgrades - { - public static TurtleModem wirelessModemNormal = new TurtleModem( false, new ResourceLocation( ComputerCraft.MOD_ID, "wireless_modem_normal" ) ); - public static TurtleModem wirelessModemAdvanced = new TurtleModem( true, new ResourceLocation( ComputerCraft.MOD_ID, "wireless_modem_advanced" ) ); - public static TurtleSpeaker speaker = new TurtleSpeaker( new ResourceLocation( ComputerCraft.MOD_ID, "speaker" ) ); - - public static TurtleCraftingTable craftingTable = new TurtleCraftingTable( new ResourceLocation( "minecraft", "crafting_table" ) ); - public static TurtleSword diamondSword = new TurtleSword( new ResourceLocation( "minecraft", "diamond_sword" ), Items.DIAMOND_SWORD ); - public static TurtleShovel diamondShovel = new TurtleShovel( new ResourceLocation( "minecraft", "diamond_shovel" ), Items.DIAMOND_SHOVEL ); - public static TurtleTool diamondPickaxe = new TurtleTool( new ResourceLocation( "minecraft", "diamond_pickaxe" ), Items.DIAMOND_PICKAXE ); - public static TurtleAxe diamondAxe = new TurtleAxe( new ResourceLocation( "minecraft", "diamond_axe" ), Items.DIAMOND_AXE ); - public static TurtleHoe diamondHoe = new TurtleHoe( new ResourceLocation( "minecraft", "diamond_hoe" ), Items.DIAMOND_HOE ); - - public static TurtleTool netheritePickaxe = new TurtleTool( new ResourceLocation( "minecraft", "netherite_pickaxe" ), Items.NETHERITE_PICKAXE ); - - public static void registerTurtleUpgrades() - { - ComputerCraftAPI.registerTurtleUpgrade( wirelessModemNormal ); - ComputerCraftAPI.registerTurtleUpgrade( wirelessModemAdvanced ); - ComputerCraftAPI.registerTurtleUpgrade( speaker ); - - ComputerCraftAPI.registerTurtleUpgrade( craftingTable ); - ComputerCraftAPI.registerTurtleUpgrade( diamondSword ); - ComputerCraftAPI.registerTurtleUpgrade( diamondShovel ); - ComputerCraftAPI.registerTurtleUpgrade( diamondPickaxe ); - ComputerCraftAPI.registerTurtleUpgrade( diamondAxe ); - ComputerCraftAPI.registerTurtleUpgrade( diamondHoe ); - - ComputerCraftAPI.registerTurtleUpgrade( netheritePickaxe ); - } - } - - public static final class PocketUpgrades - { - public static PocketModem wirelessModemNormal = new PocketModem( false ); - public static PocketModem wirelessModemAdvanced = new PocketModem( true ); - public static PocketSpeaker speaker = new PocketSpeaker(); - - public static void registerPocketUpgrades() - { - ComputerCraftAPI.registerPocketUpgrade( wirelessModemNormal ); - ComputerCraftAPI.registerPocketUpgrade( wirelessModemAdvanced ); - ComputerCraftAPI.registerPocketUpgrade( speaker ); - } - } - - -} diff --git a/src/main/java/dan200/computercraft/shared/MediaProviders.java b/src/main/java/dan200/computercraft/shared/MediaProviders.java index f934ea453..48fef052a 100644 --- a/src/main/java/dan200/computercraft/shared/MediaProviders.java +++ b/src/main/java/dan200/computercraft/shared/MediaProviders.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared; import dan200.computercraft.ComputerCraft; @@ -30,10 +29,7 @@ public static synchronized void register( @Nonnull IMediaProvider provider ) public static IMedia get( @Nonnull ItemStack stack ) { - if( stack.isEmpty() ) - { - return null; - } + if( stack.isEmpty() ) return null; // Try the handlers in order: for( IMediaProvider mediaProvider : providers ) @@ -41,10 +37,7 @@ public static IMedia get( @Nonnull ItemStack stack ) try { IMedia media = mediaProvider.getMedia( stack ); - if( media != null ) - { - return media; - } + if( media != null ) return media; } catch( Exception e ) { diff --git a/src/main/java/dan200/computercraft/shared/Peripherals.java b/src/main/java/dan200/computercraft/shared/Peripherals.java index 6f3329c4b..e7cee0cc8 100644 --- a/src/main/java/dan200/computercraft/shared/Peripherals.java +++ b/src/main/java/dan200/computercraft/shared/Peripherals.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared; import dan200.computercraft.ComputerCraft; @@ -47,10 +46,7 @@ private static IPeripheral getPeripheralAt( Level world, BlockPos pos, Direction try { IPeripheral peripheral = peripheralProvider.getPeripheral( world, pos, side ); - if( peripheral != null ) - { - return peripheral; - } + if( peripheral != null ) return peripheral; } catch( Exception e ) { diff --git a/src/main/java/dan200/computercraft/shared/PocketUpgrades.java b/src/main/java/dan200/computercraft/shared/PocketUpgrades.java index 4c9cafa3c..5066e720a 100644 --- a/src/main/java/dan200/computercraft/shared/PocketUpgrades.java +++ b/src/main/java/dan200/computercraft/shared/PocketUpgrades.java @@ -15,6 +15,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.*; +import java.util.stream.Stream; public final class PocketUpgrades { @@ -72,17 +73,18 @@ public static String getOwner( IPocketUpgrade upgrade ) return upgradeOwners.get( upgrade ); } - public static Iterable getVanillaUpgrades() + public static Iterable getUpgrades() { - List vanilla = new ArrayList<>(); - vanilla.add( ComputerCraftRegistry.PocketUpgrades.wirelessModemNormal ); - vanilla.add( ComputerCraftRegistry.PocketUpgrades.wirelessModemAdvanced ); - vanilla.add( ComputerCraftRegistry.PocketUpgrades.speaker ); - return vanilla; + return Collections.unmodifiableCollection( upgrades.values() ); } - public static Iterable getUpgrades() + public static Stream getVanillaUpgrades() { - return Collections.unmodifiableCollection( upgrades.values() ); + List vanilla = Arrays.asList( + Registry.PocketUpgrades.wirelessModemNormal, + Registry.PocketUpgrades.wirelessModemAdvanced, + Registry.PocketUpgrades.speaker + ); + return vanilla.stream(); } } diff --git a/src/main/java/dan200/computercraft/shared/Registry.java b/src/main/java/dan200/computercraft/shared/Registry.java new file mode 100644 index 000000000..29aa0a33d --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/Registry.java @@ -0,0 +1,393 @@ +/* + * 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; + +import dan200.computercraft.ComputerCraft; +import dan200.computercraft.api.ComputerCraftAPI; +import dan200.computercraft.shared.common.ContainerHeldItem; +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.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; +import dan200.computercraft.shared.peripheral.modem.wired.*; +import dan200.computercraft.shared.peripheral.modem.wireless.BlockWirelessModem; +import dan200.computercraft.shared.peripheral.modem.wireless.TileWirelessModem; +import dan200.computercraft.shared.peripheral.monitor.BlockMonitor; +import dan200.computercraft.shared.peripheral.monitor.TileMonitor; +import dan200.computercraft.shared.peripheral.printer.BlockPrinter; +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.items.ItemPocketComputer; +import dan200.computercraft.shared.pocket.peripherals.PocketModem; +import dan200.computercraft.shared.pocket.peripherals.PocketSpeaker; +import dan200.computercraft.shared.turtle.blocks.BlockTurtle; +import dan200.computercraft.shared.turtle.blocks.TileTurtle; +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 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.fabricmc.fabric.api.tool.attribute.v1.FabricToolTags; +import net.minecraft.core.BlockPos; +import net.minecraft.core.cauldron.CauldronInteraction; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.entity.MobCategory; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.MenuType; +import net.minecraft.world.item.*; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.Material; + +import java.util.function.BiFunction; + +import static net.minecraft.core.Registry.BLOCK_ENTITY_TYPE; + +public final class Registry +{ + public static final String MOD_ID = ComputerCraft.MOD_ID; + + public static void init() + { + TurtleUpgrades.registerTurtleUpgrades(); + PocketUpgrades.registerPocketUpgrades(); + + CauldronInteraction.WATER.put( ModItems.TURTLE_NORMAL, ItemTurtle.CAULDRON_INTERACTION ); + CauldronInteraction.WATER.put( ModItems.TURTLE_ADVANCED, ItemTurtle.CAULDRON_INTERACTION ); + } + + public static final class ModBlocks + { + public static T register( String id, T value ) + { + return net.minecraft.core.Registry.register( net.minecraft.core.Registry.BLOCK, new ResourceLocation( MOD_ID, id ), value ); + } + + public static final BlockMonitor MONITOR_NORMAL = + register( "monitor_normal", new BlockMonitor( properties(), () -> ModBlockEntities.MONITOR_NORMAL ) ); + + public static final BlockMonitor MONITOR_ADVANCED = + register( "monitor_advanced", new BlockMonitor( properties(), () -> ModBlockEntities.MONITOR_ADVANCED ) ); + + public static final BlockComputer COMPUTER_NORMAL = + register( "computer_normal", new BlockComputer( properties(), ComputerFamily.NORMAL, () -> ModBlockEntities.COMPUTER_NORMAL ) ); + + public static final BlockComputer COMPUTER_ADVANCED = + register( "computer_advanced", new BlockComputer( properties(), ComputerFamily.ADVANCED, () -> ModBlockEntities.COMPUTER_ADVANCED ) ); + + public static final BlockComputer COMPUTER_COMMAND = + register( "computer_command", new BlockComputer( FabricBlockSettings.copyOf( Blocks.STONE ).strength( -1, 6000000.0F ), ComputerFamily.COMMAND, () -> ModBlockEntities.COMPUTER_COMMAND ) ); + + public static final BlockTurtle TURTLE_NORMAL = + register( "turtle_normal", new BlockTurtle( turtleProperties(), ComputerFamily.NORMAL, () -> ModBlockEntities.TURTLE_NORMAL ) ); + + public static final BlockTurtle TURTLE_ADVANCED = + register( "turtle_advanced", new BlockTurtle( turtleProperties(), ComputerFamily.ADVANCED, () -> ModBlockEntities.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 BlockWirelessModem WIRELESS_MODEM_NORMAL = + register( "wireless_modem_normal", new BlockWirelessModem( properties(), () -> ModBlockEntities.WIRELESS_MODEM_NORMAL ) ); + + public static final BlockWirelessModem WIRELESS_MODEM_ADVANCED = + register( "wireless_modem_advanced", new BlockWirelessModem( properties(), () -> ModBlockEntities.WIRELESS_MODEM_ADVANCED ) ); + + public static final BlockWiredModemFull WIRED_MODEM_FULL = + register( "wired_modem_full", new BlockWiredModemFull( modemProperties() ) ); + + public static final BlockCable CABLE = + register( "cable", new BlockCable( modemProperties() ) ); + + private static BlockBehaviour.Properties properties() + { + return BlockBehaviour.Properties.of( Material.GLASS ).strength( 2F ).sound( SoundType.STONE ).noOcclusion(); + } + + private static BlockBehaviour.Properties turtleProperties() + { + return FabricBlockSettings.copyOf( Blocks.STONE ).strength( 2.5f ); + } + + private static BlockBehaviour.Properties modemProperties() + { + return FabricBlockSettings.copyOf( Blocks.STONE ).breakByHand( true ).breakByTool( FabricToolTags.PICKAXES ).strength( 1.5f ); + } + } + + public static class ModBlockEntities + { + private static BlockEntityType ofBlock( Block block, String id, BiFunction factory ) + { + BlockEntityType blockEntityType = FabricBlockEntityTypeBuilder.create( factory::apply, block ).build(); + return net.minecraft.core.Registry.register( BLOCK_ENTITY_TYPE, new ResourceLocation( MOD_ID, id ), blockEntityType ); + } + + public static final BlockEntityType MONITOR_NORMAL = + ofBlock( ModBlocks.MONITOR_NORMAL, "monitor_normal", ( blockPos, blockState ) -> new TileMonitor( ModBlockEntities.MONITOR_NORMAL, blockPos, blockState, false ) ); + + public static final BlockEntityType MONITOR_ADVANCED = + ofBlock( ModBlocks.MONITOR_ADVANCED, "monitor_advanced", ( blockPos, blockState ) -> new TileMonitor( ModBlockEntities.MONITOR_ADVANCED, blockPos, blockState, true ) ); + + public static final BlockEntityType COMPUTER_NORMAL = + ofBlock( ModBlocks.COMPUTER_NORMAL, "computer_normal", ( blockPos, blockState ) -> new TileComputer( ModBlockEntities.COMPUTER_NORMAL, blockPos, blockState, ComputerFamily.NORMAL ) ); + + public static final BlockEntityType COMPUTER_ADVANCED = + ofBlock( ModBlocks.COMPUTER_ADVANCED, "computer_advanced", ( blockPos, blockState ) -> new TileComputer( ModBlockEntities.COMPUTER_ADVANCED, blockPos, blockState, ComputerFamily.ADVANCED ) ); + + public static final BlockEntityType COMPUTER_COMMAND = + ofBlock( ModBlocks.COMPUTER_COMMAND, "computer_command", ( blockPos, blockState ) -> new TileCommandComputer( ModBlockEntities.COMPUTER_COMMAND, blockPos, blockState ) ); + + public static final BlockEntityType TURTLE_NORMAL = + ofBlock( ModBlocks.TURTLE_NORMAL, "turtle_normal", ( blockPos, blockState ) -> new TileTurtle( ModBlockEntities.TURTLE_NORMAL, blockPos, blockState, ComputerFamily.NORMAL ) ); + + public static final BlockEntityType TURTLE_ADVANCED = + ofBlock( ModBlocks.TURTLE_ADVANCED, "turtle_advanced", ( blockPos, blockState ) -> new TileTurtle( ModBlockEntities.TURTLE_ADVANCED, blockPos, blockState, ComputerFamily.ADVANCED ) ); + + public static final BlockEntityType SPEAKER = + ofBlock( ModBlocks.SPEAKER, "speaker", ( blockPos, blockState ) -> new TileSpeaker( ModBlockEntities.SPEAKER, blockPos, blockState ) ); + + public static final BlockEntityType DISK_DRIVE = + ofBlock( ModBlocks.DISK_DRIVE, "disk_drive", ( blockPos, blockState ) -> new TileDiskDrive( ModBlockEntities.DISK_DRIVE, blockPos, blockState ) ); + + public static final BlockEntityType PRINTER = + ofBlock( ModBlocks.PRINTER, "printer", ( blockPos, blockState ) -> new TilePrinter( ModBlockEntities.PRINTER, blockPos, blockState ) ); + + public static final BlockEntityType WIRED_MODEM_FULL = + ofBlock( ModBlocks.WIRED_MODEM_FULL, "wired_modem_full", ( blockPos, blockState ) -> new TileWiredModemFull( ModBlockEntities.WIRED_MODEM_FULL, blockPos, blockState ) ); + + public static final BlockEntityType CABLE = + ofBlock( ModBlocks.CABLE, "cable", ( blockPos, blockState ) -> new TileCable( ModBlockEntities.CABLE, blockPos, blockState ) ); + + public static final BlockEntityType WIRELESS_MODEM_NORMAL = + ofBlock( ModBlocks.WIRELESS_MODEM_NORMAL, "wireless_modem_normal", ( blockPos, blockState ) -> new TileWirelessModem( ModBlockEntities.WIRELESS_MODEM_NORMAL, blockPos, blockState, false ) ); + + public static final BlockEntityType WIRELESS_MODEM_ADVANCED = + ofBlock( ModBlocks.WIRELESS_MODEM_ADVANCED, "wireless_modem_advanced", ( blockPos, blockState ) -> new TileWirelessModem( ModBlockEntities.WIRELESS_MODEM_ADVANCED, blockPos, blockState, true ) ); + } + + public static final class ModItems + { + private static final CreativeModeTab mainItemGroup = ComputerCraft.MAIN_GROUP; + + public static final ItemComputer COMPUTER_NORMAL = + ofBlock( ModBlocks.COMPUTER_NORMAL, ItemComputer::new ); + + public static final ItemComputer COMPUTER_ADVANCED = + ofBlock( ModBlocks.COMPUTER_ADVANCED, ItemComputer::new ); + + public static final ItemComputer COMPUTER_COMMAND = + ofBlock( ModBlocks.COMPUTER_COMMAND, ItemComputer::new ); + + public static final ItemPocketComputer POCKET_COMPUTER_NORMAL = + register( "pocket_computer_normal", new ItemPocketComputer( properties().stacksTo( 1 ), ComputerFamily.NORMAL ) ); + + public static final ItemPocketComputer POCKET_COMPUTER_ADVANCED = + register( "pocket_computer_advanced", new ItemPocketComputer( properties().stacksTo( 1 ), ComputerFamily.ADVANCED ) ); + + public static final ItemTurtle TURTLE_NORMAL = + ofBlock( ModBlocks.TURTLE_NORMAL, ItemTurtle::new ); + + public static final ItemTurtle TURTLE_ADVANCED = + ofBlock( ModBlocks.TURTLE_ADVANCED, ItemTurtle::new ); + + public static final ItemDisk DISK = + register( "disk", new ItemDisk( properties().stacksTo( 1 ) ) ); + + public static final ItemTreasureDisk TREASURE_DISK = + register( "treasure_disk", new ItemTreasureDisk( properties().stacksTo( 1 ) ) ); + + public static final ItemPrintout PRINTED_PAGE = + register( "printed_page", new ItemPrintout( properties().stacksTo( 1 ), ItemPrintout.Type.PAGE ) ); + + public static final ItemPrintout PRINTED_PAGES = + register( "printed_pages", new ItemPrintout( properties().stacksTo( 1 ), ItemPrintout.Type.PAGES ) ); + + public static final ItemPrintout PRINTED_BOOK = + register( "printed_book", new ItemPrintout( properties().stacksTo( 1 ), ItemPrintout.Type.BOOK ) ); + + public static final BlockItem SPEAKER = + ofBlock( ModBlocks.SPEAKER, BlockItem::new ); + + public static final BlockItem DISK_DRIVE = + ofBlock( ModBlocks.DISK_DRIVE, BlockItem::new ); + + public static final BlockItem PRINTER = + ofBlock( ModBlocks.PRINTER, BlockItem::new ); + + public static final BlockItem MONITOR_NORMAL = + ofBlock( ModBlocks.MONITOR_NORMAL, BlockItem::new ); + + public static final BlockItem MONITOR_ADVANCED = + ofBlock( ModBlocks.MONITOR_ADVANCED, BlockItem::new ); + + public static final BlockItem WIRELESS_MODEM_NORMAL = + ofBlock( ModBlocks.WIRELESS_MODEM_NORMAL, BlockItem::new ); + + public static final BlockItem WIRELESS_MODEM_ADVANCED = + ofBlock( ModBlocks.WIRELESS_MODEM_ADVANCED, BlockItem::new ); + + public static final BlockItem WIRED_MODEM_FULL = + ofBlock( ModBlocks.WIRED_MODEM_FULL, BlockItem::new ); + + public static final ItemBlockCable.Cable CABLE = + register( "cable", new ItemBlockCable.Cable( ModBlocks.CABLE, properties() ) ); + + public static final ItemBlockCable.WiredModem WIRED_MODEM = + register( "wired_modem", new ItemBlockCable.WiredModem( ModBlocks.CABLE, properties() ) ); + + + private static I ofBlock( B parent, BiFunction supplier ) + { + return net.minecraft.core.Registry.register( net.minecraft.core.Registry.ITEM, net.minecraft.core.Registry.BLOCK.getKey( parent ), supplier.apply( parent, properties() ) ); + } + + private static Item.Properties properties() + { + return new Item.Properties().tab( mainItemGroup ); + } + + private static T register( String id, T item ) + { + return net.minecraft.core.Registry.register( net.minecraft.core.Registry.ITEM, new ResourceLocation( MOD_ID, id ), item ); + } + } + + public static class ModEntities + { + public static final EntityType TURTLE_PLAYER = + net.minecraft.core.Registry.register( net.minecraft.core.Registry.ENTITY_TYPE, new ResourceLocation( MOD_ID, "turtle_player" ), + EntityType.Builder.createNothing( MobCategory.MISC ).noSave().noSummon().sized( 0, 0 ).build( ComputerCraft.MOD_ID + ":turtle_player" ) ); + } + + public static class ModContainers + { + public static final MenuType COMPUTER = + ContainerData.toType( new ResourceLocation( MOD_ID, "computer" ), ModContainers.COMPUTER, ComputerContainerData::new, ComputerMenuWithoutInventory::new ); + + public static final MenuType POCKET_COMPUTER = + ContainerData.toType( new ResourceLocation( MOD_ID, "pocket_computer" ), ModContainers.POCKET_COMPUTER, ComputerContainerData::new, ComputerMenuWithoutInventory::new ); + + public static final MenuType POCKET_COMPUTER_NO_TERM = + ContainerData.toType( new ResourceLocation( MOD_ID, "pocket_computer_no_term" ), ModContainers.POCKET_COMPUTER_NO_TERM, ComputerContainerData::new, ComputerMenuWithoutInventory::new ); + + public static final MenuType TURTLE = + ContainerData.toType( new ResourceLocation( MOD_ID, "turtle" ), ComputerContainerData::new, ContainerTurtle::new ); + + public static final MenuType DISK_DRIVE = + registerSimple( "disk_drive", ContainerDiskDrive::new ); + + public static final MenuType PRINTER = + registerSimple( "printer", ContainerPrinter::new ); + + public static final MenuType PRINTOUT = + ContainerData.toType( new ResourceLocation( MOD_ID, "printout" ), HeldItemContainerData::new, ContainerHeldItem::createPrintout ); + + public static final MenuType VIEW_COMPUTER = + ContainerData.toType( new ResourceLocation( MOD_ID, "view_computer" ), ViewComputerContainerData::new, ContainerViewComputer::new ); + + private static MenuType registerSimple( String id, ScreenHandlerRegistry.SimpleClientHandlerFactory function ) + { + return ScreenHandlerRegistry.registerSimple( new ResourceLocation( MOD_ID, id ), function ); + } + } + + public static final class TurtleUpgrades + { + public static TurtleModem wirelessModemNormal = + new TurtleModem( new ResourceLocation( ComputerCraft.MOD_ID, "wireless_modem_normal" ), new ItemStack( ModItems.WIRELESS_MODEM_NORMAL ), false ); + + public static TurtleModem wirelessModemAdvanced = + new TurtleModem( new ResourceLocation( ComputerCraft.MOD_ID, "wireless_modem_advanced" ), new ItemStack( ModItems.WIRELESS_MODEM_ADVANCED ), true ); + + public static TurtleSpeaker speaker = + new TurtleSpeaker( new ResourceLocation( ComputerCraft.MOD_ID, "speaker" ), new ItemStack( ModItems.SPEAKER ) ); + + public static TurtleCraftingTable craftingTable = + new TurtleCraftingTable( new ResourceLocation( "minecraft", "crafting_table" ), new ItemStack( Items.CRAFTING_TABLE ) ); + + public static TurtleSword diamondSword = + new TurtleSword( new ResourceLocation( "minecraft", "diamond_sword" ), Items.DIAMOND_SWORD, 9.0f ); + + public static TurtleShovel diamondShovel = + new TurtleShovel( new ResourceLocation( "minecraft", "diamond_shovel" ), Items.DIAMOND_SHOVEL, 1.0f ); + + public static TurtleTool diamondPickaxe = + new TurtleTool( new ResourceLocation( "minecraft", "diamond_pickaxe" ), Items.DIAMOND_PICKAXE, 1.0f ); + + public static TurtleAxe diamondAxe = + new TurtleAxe( new ResourceLocation( "minecraft", "diamond_axe" ), Items.DIAMOND_AXE, 6.0f ); + + public static TurtleHoe diamondHoe = + new TurtleHoe( new ResourceLocation( "minecraft", "diamond_hoe" ), Items.DIAMOND_HOE, 1.0f ); + + public static TurtleTool netheritePickaxe = + new TurtleTool( new ResourceLocation( "minecraft", "netherite_pickaxe" ), Items.NETHERITE_PICKAXE, 1.0f ); + + public static void registerTurtleUpgrades() + { + ComputerCraftAPI.registerTurtleUpgrade( wirelessModemNormal ); + ComputerCraftAPI.registerTurtleUpgrade( wirelessModemAdvanced ); + ComputerCraftAPI.registerTurtleUpgrade( speaker ); + ComputerCraftAPI.registerTurtleUpgrade( craftingTable ); + + ComputerCraftAPI.registerTurtleUpgrade( diamondSword ); + ComputerCraftAPI.registerTurtleUpgrade( diamondShovel ); + ComputerCraftAPI.registerTurtleUpgrade( diamondPickaxe ); + ComputerCraftAPI.registerTurtleUpgrade( diamondAxe ); + ComputerCraftAPI.registerTurtleUpgrade( diamondHoe ); + ComputerCraftAPI.registerTurtleUpgrade( netheritePickaxe ); + } + } + + public static final class PocketUpgrades + { + public static PocketModem wirelessModemNormal = new PocketModem( new ResourceLocation( ComputerCraft.MOD_ID, "wireless_modem_normal" ), new ItemStack( ModItems.WIRELESS_MODEM_NORMAL ), false ); + + public static PocketModem wirelessModemAdvanced = new PocketModem( new ResourceLocation( ComputerCraft.MOD_ID, "wireless_modem_advanced" ), new ItemStack( ModItems.WIRELESS_MODEM_ADVANCED ), true ); + + public static PocketSpeaker speaker = new PocketSpeaker( new ResourceLocation( ComputerCraft.MOD_ID, "speaker" ), new ItemStack( ModItems.SPEAKER ) ); + + public static void registerPocketUpgrades() + { + ComputerCraftAPI.registerPocketUpgrade( wirelessModemNormal ); + ComputerCraftAPI.registerPocketUpgrade( wirelessModemAdvanced ); + ComputerCraftAPI.registerPocketUpgrade( speaker ); + } + } + + +} diff --git a/src/main/java/dan200/computercraft/shared/TurtleUpgrades.java b/src/main/java/dan200/computercraft/shared/TurtleUpgrades.java index 514c56a59..854996c94 100644 --- a/src/main/java/dan200/computercraft/shared/TurtleUpgrades.java +++ b/src/main/java/dan200/computercraft/shared/TurtleUpgrades.java @@ -98,17 +98,17 @@ public static Stream getVanillaUpgrades() { vanilla = new ITurtleUpgrade[] { // ComputerCraft upgrades - ComputerCraftRegistry.TurtleUpgrades.wirelessModemNormal, - ComputerCraftRegistry.TurtleUpgrades.wirelessModemAdvanced, - ComputerCraftRegistry.TurtleUpgrades.speaker, + Registry.TurtleUpgrades.wirelessModemNormal, + Registry.TurtleUpgrades.wirelessModemAdvanced, + Registry.TurtleUpgrades.speaker, // Vanilla Minecraft upgrades - ComputerCraftRegistry.TurtleUpgrades.diamondPickaxe, - ComputerCraftRegistry.TurtleUpgrades.diamondAxe, - ComputerCraftRegistry.TurtleUpgrades.diamondSword, - ComputerCraftRegistry.TurtleUpgrades.diamondShovel, - ComputerCraftRegistry.TurtleUpgrades.diamondHoe, - ComputerCraftRegistry.TurtleUpgrades.craftingTable, + Registry.TurtleUpgrades.diamondPickaxe, + Registry.TurtleUpgrades.diamondAxe, + Registry.TurtleUpgrades.diamondSword, + Registry.TurtleUpgrades.diamondShovel, + Registry.TurtleUpgrades.diamondHoe, + Registry.TurtleUpgrades.craftingTable, }; } diff --git a/src/main/java/dan200/computercraft/shared/command/ClientCommands.java b/src/main/java/dan200/computercraft/shared/command/ClientCommands.java index f67453827..2942f4d2e 100644 --- a/src/main/java/dan200/computercraft/shared/command/ClientCommands.java +++ b/src/main/java/dan200/computercraft/shared/command/ClientCommands.java @@ -6,7 +6,9 @@ package dan200.computercraft.shared.command; import dan200.computercraft.shared.util.IDAssigner; +import me.shedaniel.cloth.api.utils.v1.GameInstanceUtils; import net.minecraft.Util; +import net.minecraft.server.MinecraftServer; import java.io.File; @@ -28,6 +30,9 @@ public static boolean onClientSendMessage( String message ) // Emulate the command on the client side if( message.startsWith( OPEN_COMPUTER ) ) { + MinecraftServer server = GameInstanceUtils.getServer(); + if( server == null || server.isDedicatedServer() ) return false; + String idStr = message.substring( OPEN_COMPUTER.length() ).trim(); int id; try @@ -36,7 +41,7 @@ public static boolean onClientSendMessage( String message ) } catch( NumberFormatException ignore ) { - return true; + return false; } File file = new File( IDAssigner.getDir(), "computer/" + id ); diff --git a/src/main/java/dan200/computercraft/shared/command/CommandComputerCraft.java b/src/main/java/dan200/computercraft/shared/command/CommandComputerCraft.java index b1586a1ac..b13e7fa71 100644 --- a/src/main/java/dan200/computercraft/shared/command/CommandComputerCraft.java +++ b/src/main/java/dan200/computercraft/shared/command/CommandComputerCraft.java @@ -26,6 +26,7 @@ import net.minecraft.commands.CommandSourceStack; import net.minecraft.core.BlockPos; import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.chat.Component; import net.minecraft.network.chat.MutableComponent; import net.minecraft.network.chat.TextComponent; import net.minecraft.network.chat.TranslatableComponent; @@ -82,15 +83,15 @@ public static void register( CommandDispatcher dispatcher, B BlockPos pos = new BlockPos( source.getPosition() ); computers.sort( ( a, b ) -> { - if( a.getWorld() == b.getWorld() && a.getWorld() == world ) + if( a.getLevel() == b.getLevel() && a.getLevel() == world ) { return Double.compare( a.getPosition().distSqr( pos ), b.getPosition().distSqr( pos ) ); } - else if( a.getWorld() == world ) + else if( a.getLevel() == world ) { return -1; } - else if( b.getWorld() == world ) + else if( b.getLevel() == world ) { return 1; } @@ -171,15 +172,14 @@ else if( b.getWorld() == world ) .arg( "computer", oneComputer() ) .executes( context -> { ServerComputer computer = getComputerArgument( context, "computer" ); - Level world = computer.getWorld(); + Level world = computer.getLevel(); BlockPos pos = computer.getPosition(); if( world == null || pos == null ) throw TP_NOT_THERE.create(); Entity entity = context.getSource().getEntityOrException(); - if( !(entity instanceof ServerPlayer) ) throw TP_NOT_PLAYER.create(); + if( !(entity instanceof ServerPlayer player) ) throw TP_NOT_PLAYER.create(); - ServerPlayer player = (ServerPlayer) entity; if( player.getCommandSenderWorld() == world ) { player.connection.teleport( @@ -236,7 +236,7 @@ public void writeScreenOpeningData( ServerPlayer player, FriendlyByteBuf buf ) @Nonnull @Override - public MutableComponent getDisplayName() + public Component getDisplayName() { return new TranslatableComponent( "gui.computercraft.view_computer" ); } @@ -292,7 +292,7 @@ public AbstractContainerMenu createMenu( int id, @Nonnull Inventory player, @Non ); } - private static MutableComponent linkComputer( CommandSourceStack source, ServerComputer serverComputer, int computerId ) + private static Component linkComputer( CommandSourceStack source, ServerComputer serverComputer, int computerId ) { MutableComponent out = new TextComponent( "" ); @@ -333,14 +333,14 @@ private static MutableComponent linkComputer( CommandSourceStack source, ServerC if( UserLevel.OWNER.test( source ) && isPlayer( source ) ) { - MutableComponent linkPath = linkStorage( computerId ); + Component linkPath = linkStorage( computerId ); if( linkPath != null ) out.append( " " ).append( linkPath ); } return out; } - private static MutableComponent linkPosition( CommandSourceStack context, ServerComputer computer ) + private static Component linkPosition( CommandSourceStack context, ServerComputer computer ) { if( UserLevel.OP.test( context ) ) { @@ -356,7 +356,7 @@ private static MutableComponent linkPosition( CommandSourceStack context, Server } } - private static MutableComponent linkStorage( int id ) + private static Component linkStorage( int id ) { File file = new File( IDAssigner.getDir(), "computer/" + id ); if( !file.isDirectory() ) return null; @@ -398,7 +398,7 @@ private static int displayTimings( CommandSourceStack source, @Nonnull Listcomparing( x -> x.get( sortField ) ).reversed() ); - MutableComponent[] headers = new MutableComponent[1 + fields.size()]; + Component[] headers = new Component[1 + fields.size()]; headers[0] = translate( "commands.computercraft.track.dump.computer" ); for( int i = 0; i < fields.size(); i++ ) headers[i + 1] = translate( fields.get( i ).translationKey() ); TableBuilder table = new TableBuilder( TRACK_ID, headers ); @@ -408,9 +408,9 @@ private static int displayTimings( CommandSourceStack source, @Nonnull List suggestOnServer( CommandContext context, SuggestionsBuilder builder, - Function, CompletableFuture> supplier ) + public static CompletableFuture suggestOnServer( CommandContext context, SuggestionsBuilder builder, Function, CompletableFuture> supplier ) { Object source = context.getSource(); if( !(source instanceof SharedSuggestionProvider) ) @@ -49,26 +49,21 @@ else if( source instanceof CommandSourceStack ) } } - public static CompletableFuture suggest( SuggestionsBuilder builder, T[] candidates, Function toString ) - { - return suggest( builder, Arrays.asList( candidates ), toString ); - } - public static CompletableFuture suggest( SuggestionsBuilder builder, Iterable candidates, Function toString ) { - String remaining = builder.getRemaining() - .toLowerCase( Locale.ROOT ); + String remaining = builder.getRemaining().toLowerCase( Locale.ROOT ); for( T choice : candidates ) { String name = toString.apply( choice ); - if( !name.toLowerCase( Locale.ROOT ) - .startsWith( remaining ) ) - { - continue; - } + if( !name.toLowerCase( Locale.ROOT ).startsWith( remaining ) ) continue; builder.suggest( name ); } return builder.buildFuture(); } + + public static CompletableFuture suggest( SuggestionsBuilder builder, T[] candidates, Function toString ) + { + return suggest( builder, Arrays.asList( candidates ), toString ); + } } diff --git a/src/main/java/dan200/computercraft/shared/command/Exceptions.java b/src/main/java/dan200/computercraft/shared/command/Exceptions.java index bf077b239..714fd9f12 100644 --- a/src/main/java/dan200/computercraft/shared/command/Exceptions.java +++ b/src/main/java/dan200/computercraft/shared/command/Exceptions.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.command; import com.mojang.brigadier.exceptions.Dynamic2CommandExceptionType; @@ -17,12 +16,15 @@ public final class Exceptions public static final Dynamic2CommandExceptionType COMPUTER_ARG_MANY = translated2( "argument.computercraft.computer.many_matching" ); public static final DynamicCommandExceptionType TRACKING_FIELD_ARG_NONE = translated1( "argument.computercraft.tracking_field.no_field" ); - public static final SimpleCommandExceptionType ARGUMENT_EXPECTED = translated( "argument.computercraft.argument_expected" ); + static final SimpleCommandExceptionType NOT_TRACKING_EXCEPTION = translated( "commands.computercraft.track.stop.not_enabled" ); static final SimpleCommandExceptionType NO_TIMINGS_EXCEPTION = translated( "commands.computercraft.track.dump.no_timings" ); + static final SimpleCommandExceptionType TP_NOT_THERE = translated( "commands.computercraft.tp.not_there" ); static final SimpleCommandExceptionType TP_NOT_PLAYER = translated( "commands.computercraft.tp.not_player" ); + public static final SimpleCommandExceptionType ARGUMENT_EXPECTED = translated( "argument.computercraft.argument_expected" ); + private static SimpleCommandExceptionType translated( String key ) { return new SimpleCommandExceptionType( new TranslatableComponent( key ) ); diff --git a/src/main/java/dan200/computercraft/shared/command/UserLevel.java b/src/main/java/dan200/computercraft/shared/command/UserLevel.java index 8f5715edb..53ae7d69a 100644 --- a/src/main/java/dan200/computercraft/shared/command/UserLevel.java +++ b/src/main/java/dan200/computercraft/shared/command/UserLevel.java @@ -67,6 +67,6 @@ private static boolean isOwner( CommandSourceStack source ) Entity sender = source.getEntity(); return server.isDedicatedServer() ? source.getEntity() == null && source.hasPermission( 4 ) && source.getTextName().equals( "Server" ) - : sender instanceof Player && ((Player) sender).getGameProfile().getName().equalsIgnoreCase( server.getServerModName() ); + : sender instanceof Player player && player.getGameProfile().getName().equalsIgnoreCase( server.getServerModName() ); } } diff --git a/src/main/java/dan200/computercraft/shared/command/arguments/ArgumentSerializers.java b/src/main/java/dan200/computercraft/shared/command/arguments/ArgumentSerializers.java index afacc524e..43909b534 100644 --- a/src/main/java/dan200/computercraft/shared/command/arguments/ArgumentSerializers.java +++ b/src/main/java/dan200/computercraft/shared/command/arguments/ArgumentSerializers.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.command.arguments; import com.mojang.brigadier.arguments.ArgumentType; @@ -15,27 +14,27 @@ public final class ArgumentSerializers { - public static void register() + @SuppressWarnings( "unchecked" ) + private static > void registerUnsafe( ResourceLocation id, Class type, ArgumentSerializer serializer ) { - register( new ResourceLocation( ComputerCraft.MOD_ID, "tracking_field" ), TrackingFieldArgumentType.trackingField() ); - register( new ResourceLocation( ComputerCraft.MOD_ID, "computer" ), ComputerArgumentType.oneComputer() ); - register( new ResourceLocation( ComputerCraft.MOD_ID, "computers" ), ComputersArgumentType.class, new ComputersArgumentType.Serializer() ); - registerUnsafe( new ResourceLocation( ComputerCraft.MOD_ID, "repeat" ), RepeatArgumentType.class, new RepeatArgumentType.Serializer() ); + ArgumentTypes.register( id.toString(), type, (ArgumentSerializer) serializer ); } - private static > void register( ResourceLocation id, T instance ) + private static > void register( ResourceLocation id, Class type, ArgumentSerializer serializer ) { - registerUnsafe( id, instance.getClass(), new EmptyArgumentSerializer<>( () -> instance ) ); + ArgumentTypes.register( id.toString(), type, serializer ); } - private static > void register( ResourceLocation id, Class type, ArgumentSerializer serializer ) + private static > void register( ResourceLocation id, T instance ) { - ArgumentTypes.register( id.toString(), type, serializer ); + registerUnsafe( id, instance.getClass(), new EmptyArgumentSerializer<>( () -> instance ) ); } - @SuppressWarnings( "unchecked" ) - private static > void registerUnsafe( ResourceLocation id, Class type, ArgumentSerializer serializer ) + public static void register() { - ArgumentTypes.register( id.toString(), type, (ArgumentSerializer) serializer ); + register( new ResourceLocation( ComputerCraft.MOD_ID, "tracking_field" ), TrackingFieldArgumentType.trackingField() ); + register( new ResourceLocation( ComputerCraft.MOD_ID, "computer" ), ComputerArgumentType.oneComputer() ); + register( new ResourceLocation( ComputerCraft.MOD_ID, "computers" ), ComputersArgumentType.class, new ComputersArgumentType.Serializer() ); + registerUnsafe( new ResourceLocation( ComputerCraft.MOD_ID, "repeat" ), RepeatArgumentType.class, new RepeatArgumentType.Serializer() ); } } diff --git a/src/main/java/dan200/computercraft/shared/command/arguments/ChoiceArgumentType.java b/src/main/java/dan200/computercraft/shared/command/arguments/ChoiceArgumentType.java index 642b5af10..cbed13bb7 100644 --- a/src/main/java/dan200/computercraft/shared/command/arguments/ChoiceArgumentType.java +++ b/src/main/java/dan200/computercraft/shared/command/arguments/ChoiceArgumentType.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.command.arguments; import com.mojang.brigadier.Message; @@ -43,10 +42,7 @@ public T parse( StringReader reader ) throws CommandSyntaxException for( T choice : choices ) { String choiceName = this.name.apply( choice ); - if( name.equals( choiceName ) ) - { - return choice; - } + if( name.equals( choiceName ) ) return choice; } reader.setCursor( start ); @@ -56,16 +52,11 @@ public T parse( StringReader reader ) throws CommandSyntaxException @Override public CompletableFuture listSuggestions( CommandContext context, SuggestionsBuilder builder ) { - String remaining = builder.getRemaining() - .toLowerCase( Locale.ROOT ); + String remaining = builder.getRemaining().toLowerCase( Locale.ROOT ); for( T choice : choices ) { String name = this.name.apply( choice ); - if( !name.toLowerCase( Locale.ROOT ) - .startsWith( remaining ) ) - { - continue; - } + if( !name.toLowerCase( Locale.ROOT ).startsWith( remaining ) ) continue; builder.suggest( name, tooltip.apply( choice ) ); } @@ -76,10 +67,7 @@ public CompletableFuture listSuggestions( CommandContext con public Collection getExamples() { List items = choices instanceof Collection ? new ArrayList<>( ((Collection) choices).size() ) : new ArrayList<>(); - for( T choice : choices ) - { - items.add( name.apply( choice ) ); - } + for( T choice : choices ) items.add( name.apply( choice ) ); items.sort( Comparator.naturalOrder() ); return items; } diff --git a/src/main/java/dan200/computercraft/shared/command/arguments/ComputerArgumentType.java b/src/main/java/dan200/computercraft/shared/command/arguments/ComputerArgumentType.java index 2d6e0e77e..dc82a83b3 100644 --- a/src/main/java/dan200/computercraft/shared/command/arguments/ComputerArgumentType.java +++ b/src/main/java/dan200/computercraft/shared/command/arguments/ComputerArgumentType.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.command.arguments; import com.mojang.brigadier.StringReader; @@ -25,10 +24,6 @@ public final class ComputerArgumentType implements ArgumentType context, String name ) throws CommandSyntaxException { - return context.getArgument( name, ComputerSupplier.class ) - .unwrap( context.getSource() ); + return context.getArgument( name, ComputerSupplier.class ).unwrap( context.getSource() ); + } + + private ComputerArgumentType() + { } @Override public ComputerSupplier parse( StringReader reader ) throws CommandSyntaxException { int start = reader.getCursor(); - ComputersSupplier supplier = ComputersArgumentType.someComputers() - .parse( reader ); - String selector = reader.getString() - .substring( start, reader.getCursor() ); + ComputersSupplier supplier = ComputersArgumentType.someComputers().parse( reader ); + String selector = reader.getString().substring( start, reader.getCursor() ); return s -> { Collection computers = supplier.unwrap( s ); - if( computers.size() == 1 ) - { - return computers.iterator() - .next(); - } + if( computers.size() == 1 ) return computers.iterator().next(); StringBuilder builder = new StringBuilder(); boolean first = true; @@ -84,15 +76,13 @@ public ComputerSupplier parse( StringReader reader ) throws CommandSyntaxExcepti @Override public CompletableFuture listSuggestions( CommandContext context, SuggestionsBuilder builder ) { - return ComputersArgumentType.someComputers() - .listSuggestions( context, builder ); + return ComputersArgumentType.someComputers().listSuggestions( context, builder ); } @Override public Collection getExamples() { - return ComputersArgumentType.someComputers() - .getExamples(); + return ComputersArgumentType.someComputers().getExamples(); } @FunctionalInterface diff --git a/src/main/java/dan200/computercraft/shared/command/arguments/ComputersArgumentType.java b/src/main/java/dan200/computercraft/shared/command/arguments/ComputersArgumentType.java index a3ad2e1c8..3e57133a0 100644 --- a/src/main/java/dan200/computercraft/shared/command/arguments/ComputersArgumentType.java +++ b/src/main/java/dan200/computercraft/shared/command/arguments/ComputersArgumentType.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.command.arguments; import com.google.gson.JsonObject; @@ -36,13 +35,9 @@ public final class ComputersArgumentType implements ArgumentType EXAMPLES = Arrays.asList( "0", "#0", "@Label", "~Advanced" ); - private final boolean requireSome; - - private ComputersArgumentType( boolean requireSome ) - { - this.requireSome = requireSome; - } + private static final List EXAMPLES = Arrays.asList( + "0", "#0", "@Label", "~Advanced" + ); public static ComputersArgumentType manyComputers() { @@ -56,18 +51,14 @@ public static ComputersArgumentType someComputers() public static Collection getComputersArgument( CommandContext context, String name ) throws CommandSyntaxException { - return context.getArgument( name, ComputersSupplier.class ) - .unwrap( context.getSource() ); + return context.getArgument( name, ComputersSupplier.class ).unwrap( context.getSource() ); } - public static Set unwrap( CommandSourceStack source, Collection suppliers ) throws CommandSyntaxException + private final boolean requireSome; + + private ComputersArgumentType( boolean requireSome ) { - Set computers = new HashSet<>(); - for( ComputersSupplier supplier : suppliers ) - { - computers.addAll( supplier.unwrap( source ) ); - } - return computers; + this.requireSome = requireSome; } @Override @@ -86,9 +77,7 @@ else if( kind == '~' ) { reader.skip(); String family = reader.readUnquotedString(); - computers = getComputers( x -> x.getFamily() - .name() - .equalsIgnoreCase( family ) ); + computers = getComputers( x -> x.getFamily().name().equalsIgnoreCase( family ) ); } else if( kind == '#' ) { @@ -107,14 +96,10 @@ else if( kind == '#' ) if( requireSome ) { - String selector = reader.getString() - .substring( start, reader.getCursor() ); + String selector = reader.getString().substring( start, reader.getCursor() ); return source -> { Collection matched = computers.unwrap( source ); - if( matched.isEmpty() ) - { - throw COMPUTER_ARG_NONE.create( selector ); - } + if( matched.isEmpty() ) throw COMPUTER_ARG_NONE.create( selector ); return matched; }; } @@ -169,8 +154,7 @@ private static void suggestComputers( SuggestionsBuilder builder, String remaini for( ServerComputer computer : ComputerCraft.serverComputerRegistry.getComputers() ) { String converted = renderer.apply( computer ); - if( converted != null && converted.toLowerCase( Locale.ROOT ) - .startsWith( remaining ) ) + if( converted != null && converted.toLowerCase( Locale.ROOT ).startsWith( remaining ) ) { builder.suggest( converted ); } @@ -179,16 +163,12 @@ private static void suggestComputers( SuggestionsBuilder builder, String remaini private static ComputersSupplier getComputers( Predicate predicate ) { - return s -> Collections.unmodifiableList( ComputerCraft.serverComputerRegistry.getComputers() + return s -> Collections.unmodifiableList( ComputerCraft.serverComputerRegistry + .getComputers() .stream() .filter( predicate ) - .collect( Collectors.toList() ) ); - } - - @FunctionalInterface - public interface ComputersSupplier - { - Collection unwrap( CommandSourceStack source ) throws CommandSyntaxException; + .collect( Collectors.toList() ) + ); } public static class Serializer implements ArgumentSerializer @@ -213,4 +193,17 @@ public void serializeToJson( @Nonnull ComputersArgumentType arg, @Nonnull JsonOb json.addProperty( "requireSome", arg.requireSome ); } } + + @FunctionalInterface + public interface ComputersSupplier + { + Collection unwrap( CommandSourceStack source ) throws CommandSyntaxException; + } + + public static Set unwrap( CommandSourceStack source, Collection suppliers ) throws CommandSyntaxException + { + Set computers = new HashSet<>(); + for( ComputersSupplier supplier : suppliers ) computers.addAll( supplier.unwrap( source ) ); + return computers; + } } diff --git a/src/main/java/dan200/computercraft/shared/command/arguments/RepeatArgumentType.java b/src/main/java/dan200/computercraft/shared/command/arguments/RepeatArgumentType.java index 41076124d..0cf588beb 100644 --- a/src/main/java/dan200/computercraft/shared/command/arguments/RepeatArgumentType.java +++ b/src/main/java/dan200/computercraft/shared/command/arguments/RepeatArgumentType.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.command.arguments; import com.google.gson.JsonObject; @@ -32,8 +31,8 @@ /** * Reads one argument multiple times. * - * Note that this must be the last element in an argument chain: in order to improve the quality of error messages, we will always try to consume another - * argument while there is input remaining. + * Note that this must be the last element in an argument chain: in order to improve the quality of error messages, + * we will always try to consume another argument while there is input remaining. * * One problem with how parsers function, is that they must consume some input: and thus we * @@ -73,10 +72,7 @@ public List parse( StringReader reader ) throws CommandSyntaxException while( true ) { reader.skipWhitespace(); - if( !reader.canRead() ) - { - break; - } + if( !reader.canRead() ) break; int startParse = reader.getCursor(); appender.accept( out, child.parse( reader ) ); @@ -91,10 +87,7 @@ public List parse( StringReader reader ) throws CommandSyntaxException // Note that each child may return an empty list, we just require that some actual input // was consumed. // We should probably review that this is sensible in the future. - if( !hadSome ) - { - throw some.createWithContext( reader ); - } + if( !hadSome ) throw some.createWithContext( reader ); return Collections.unmodifiableList( out ); } @@ -118,10 +111,7 @@ public CompletableFuture listSuggestions( CommandContext con int cursor = reader.getCursor(); reader.skipWhitespace(); - if( cursor == reader.getCursor() ) - { - break; - } + if( cursor == reader.getCursor() ) break; previous = reader.getCursor(); } @@ -147,10 +137,7 @@ public void serializeToNetwork( @Nonnull RepeatArgumentType arg, @Nonnull @Nonnull @Override - @SuppressWarnings( { - "unchecked", - "rawtypes" - } ) + @SuppressWarnings( { "unchecked", "rawtypes" } ) public RepeatArgumentType deserializeFromNetwork( @Nonnull FriendlyByteBuf buf ) { boolean isList = buf.readBoolean(); @@ -170,12 +157,8 @@ public void serializeToJson( @Nonnull RepeatArgumentType arg, @Nonnull Jso private static Component getMessage( RepeatArgumentType arg ) { - Message message = arg.some.create() - .getRawMessage(); - if( message instanceof Component ) - { - return (Component) message; - } + Message message = arg.some.create().getRawMessage(); + if( message instanceof Component ) return (Component) message; return new TextComponent( message.getString() ); } } diff --git a/src/main/java/dan200/computercraft/shared/command/arguments/TrackingFieldArgumentType.java b/src/main/java/dan200/computercraft/shared/command/arguments/TrackingFieldArgumentType.java index 06f2330fd..71b3c9f65 100644 --- a/src/main/java/dan200/computercraft/shared/command/arguments/TrackingFieldArgumentType.java +++ b/src/main/java/dan200/computercraft/shared/command/arguments/TrackingFieldArgumentType.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.command.arguments; import dan200.computercraft.core.tracking.TrackingField; @@ -17,8 +16,7 @@ public final class TrackingFieldArgumentType extends ChoiceArgumentType translate( x.translationKey() ), Exceptions.TRACKING_FIELD_ARG_NONE ); + super( TrackingField.fields().values(), TrackingField::id, x -> translate( x.translationKey() ), Exceptions.TRACKING_FIELD_ARG_NONE ); } public static TrackingFieldArgumentType trackingField() diff --git a/src/main/java/dan200/computercraft/shared/command/builder/CommandBuilder.java b/src/main/java/dan200/computercraft/shared/command/builder/CommandBuilder.java index 07c5d9ee6..11e00bc1b 100644 --- a/src/main/java/dan200/computercraft/shared/command/builder/CommandBuilder.java +++ b/src/main/java/dan200/computercraft/shared/command/builder/CommandBuilder.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.command.builder; import com.mojang.brigadier.Command; @@ -25,13 +24,14 @@ import static dan200.computercraft.shared.command.builder.HelpingArgumentBuilder.literal; /** - * An alternative way of building command nodes, so one does not have to nest. {@link ArgumentBuilder#then(CommandNode)}s. + * An alternative way of building command nodes, so one does not have to nest. + * {@link ArgumentBuilder#then(CommandNode)}s. * * @param The command source we consume. */ public class CommandBuilder implements CommandNodeBuilder> { - private List> args = new ArrayList<>(); + private final List> args = new ArrayList<>(); private Predicate requires; public static CommandBuilder args() @@ -58,14 +58,14 @@ public CommandBuilder arg( String name, ArgumentType type ) return this; } - public CommandNodeBuilder>> argManyValue( String name, ArgumentType type, T defaultValue ) + public CommandNodeBuilder>> argManyValue( String name, ArgumentType type, List empty ) { - return argManyValue( name, type, Collections.singletonList( defaultValue ) ); + return argMany( name, type, () -> empty ); } - public CommandNodeBuilder>> argManyValue( String name, ArgumentType type, List empty ) + public CommandNodeBuilder>> argManyValue( String name, ArgumentType type, T defaultValue ) { - return argMany( name, type, () -> empty ); + return argManyValue( name, type, Collections.singletonList( defaultValue ) ); } public CommandNodeBuilder>> argMany( String name, ArgumentType type, Supplier> empty ) @@ -73,19 +73,23 @@ public CommandNodeBuilder>> argMany( String name, A return argMany( name, RepeatArgumentType.some( type, ARGUMENT_EXPECTED ), empty ); } + public CommandNodeBuilder>> argManyFlatten( String name, ArgumentType> type, Supplier> empty ) + { + return argMany( name, RepeatArgumentType.someFlat( type, ARGUMENT_EXPECTED ), empty ); + } + private CommandNodeBuilder>> argMany( String name, RepeatArgumentType type, Supplier> empty ) { - if( args.isEmpty() ) - { - throw new IllegalStateException( "Cannot have empty arg chain builder" ); - } + if( args.isEmpty() ) throw new IllegalStateException( "Cannot have empty arg chain builder" ); return command -> { // The node for no arguments ArgumentBuilder tail = tail( ctx -> command.run( ctx, empty.get() ) ); // The node for one or more arguments - ArgumentBuilder moreArg = RequiredArgumentBuilder.>argument( name, type ).executes( ctx -> command.run( ctx, getList( ctx, name ) ) ); + ArgumentBuilder moreArg = RequiredArgumentBuilder + .>argument( name, type ) + .executes( ctx -> command.run( ctx, getList( ctx, name ) ) ); // Chain all of them together! tail.then( moreArg ); @@ -93,46 +97,31 @@ private CommandNodeBuilder>> argMany( String nam }; } - private ArgumentBuilder tail( Command command ) - { - ArgumentBuilder defaultTail = args.get( args.size() - 1 ); - defaultTail.executes( command ); - if( requires != null ) - { - defaultTail.requires( requires ); - } - return defaultTail; - } - @SuppressWarnings( "unchecked" ) private static List getList( CommandContext context, String name ) { return (List) context.getArgument( name, List.class ); } - private CommandNode link( ArgumentBuilder tail ) + @Override + public CommandNode executes( Command command ) { - for( int i = args.size() - 2; i >= 0; i-- ) - { - tail = args.get( i ) - .then( tail ); - } - return tail.build(); + if( args.isEmpty() ) throw new IllegalStateException( "Cannot have empty arg chain builder" ); + + return link( tail( command ) ); } - public CommandNodeBuilder>> argManyFlatten( String name, ArgumentType> type, Supplier> empty ) + private ArgumentBuilder tail( Command command ) { - return argMany( name, RepeatArgumentType.someFlat( type, ARGUMENT_EXPECTED ), empty ); + ArgumentBuilder defaultTail = args.get( args.size() - 1 ); + defaultTail.executes( command ); + if( requires != null ) defaultTail.requires( requires ); + return defaultTail; } - @Override - public CommandNode executes( Command command ) + private CommandNode link( ArgumentBuilder tail ) { - if( args.isEmpty() ) - { - throw new IllegalStateException( "Cannot have empty arg chain builder" ); - } - - return link( tail( command ) ); + for( int i = args.size() - 2; i >= 0; i-- ) tail = args.get( i ).then( tail ); + return tail.build(); } } diff --git a/src/main/java/dan200/computercraft/shared/command/builder/HelpingArgumentBuilder.java b/src/main/java/dan200/computercraft/shared/command/builder/HelpingArgumentBuilder.java index 286b25986..ad7579368 100644 --- a/src/main/java/dan200/computercraft/shared/command/builder/HelpingArgumentBuilder.java +++ b/src/main/java/dan200/computercraft/shared/command/builder/HelpingArgumentBuilder.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.command.builder; import com.mojang.brigadier.Command; @@ -28,14 +27,11 @@ import static dan200.computercraft.shared.command.text.ChatHelpers.translate; /** - * An alternative to {@link LiteralArgumentBuilder} which also provides a {@code /... help} command, and defaults to that command when no arguments are - * given. + * An alternative to {@link LiteralArgumentBuilder} which also provides a {@code /... help} command, and defaults + * to that command when no arguments are given. */ public final class HelpingArgumentBuilder extends LiteralArgumentBuilder { - private static final ChatFormatting HEADER = ChatFormatting.LIGHT_PURPLE; - private static final ChatFormatting SYNOPSIS = ChatFormatting.AQUA; - private static final ChatFormatting NAME = ChatFormatting.GREEN; private final Collection children = new ArrayList<>(); private HelpingArgumentBuilder( String literal ) @@ -48,69 +44,16 @@ public static HelpingArgumentBuilder choice( String literal ) return new HelpingArgumentBuilder( literal ); } - private static Command helpForChild( CommandNode node, String id, String command ) - { - return context -> { - context.getSource() - .sendSuccess( getHelp( context, - node, - id + "." + node.getName() - .replace( '-', '_' ), - command + " " + node.getName() ), false ); - return 0; - }; - } - - private static Component getHelp( CommandContext context, CommandNode node, String id, String command ) + @Override + public LiteralArgumentBuilder executes( final Command command ) { - // An ugly hack to extract usage information from the dispatcher. We generate a temporary node, generate - // the shorthand usage, and emit that. - CommandDispatcher dispatcher = context.getSource() - .getServer() - .getCommands() - .getDispatcher(); - CommandNode temp = new LiteralCommandNode<>( "_", null, x -> true, null, null, false ); - temp.addChild( node ); - String usage = dispatcher.getSmartUsage( temp, context.getSource() ) - .get( node ) - .substring( node.getName() - .length() ); - - MutableComponent output = new TextComponent( "" ).append( coloured( "/" + command + usage, HEADER ) ) - .append( " " ) - .append( coloured( translate( "commands." + id + ".synopsis" ), SYNOPSIS ) ) - .append( "\n" ) - .append( translate( "commands." + id + ".desc" ) ); - - for( CommandNode child : node.getChildren() ) - { - if( !child.getRequirement() - .test( context.getSource() ) || !(child instanceof LiteralCommandNode) ) - { - continue; - } - - output.append( "\n" ); - - MutableComponent component = coloured( child.getName(), NAME ); - component.getStyle() - .withClickEvent( new ClickEvent( ClickEvent.Action.SUGGEST_COMMAND, "/" + command + " " + child.getName() ) ); - output.append( component ); - - output.append( " - " ) - .append( translate( "commands." + id + "." + child.getName() + ".synopsis" ) ); - } - - return output; + throw new IllegalStateException( "Cannot use executes on a HelpingArgumentBuilder" ); } @Override public LiteralArgumentBuilder then( final ArgumentBuilder argument ) { - if( getRedirect() != null ) - { - throw new IllegalStateException( "Cannot add children to a redirected node" ); - } + if( getRedirect() != null ) throw new IllegalStateException( "Cannot add children to a redirected node" ); if( argument instanceof HelpingArgumentBuilder ) { @@ -138,12 +81,6 @@ public LiteralArgumentBuilder then( CommandNode executes( final Command command ) - { - throw new IllegalStateException( "Cannot use executes on a HelpingArgumentBuilder" ); - } - @Override public LiteralCommandNode build() { @@ -158,28 +95,24 @@ private LiteralCommandNode build( @Nonnull String id, @Nonnu private LiteralCommandNode buildImpl( String id, String command ) { HelpCommand helpCommand = new HelpCommand( id, command ); - LiteralCommandNode node = new LiteralCommandNode<>( getLiteral(), - helpCommand, getRequirement(), - getRedirect(), getRedirectModifier(), isFork() ); + LiteralCommandNode node = new LiteralCommandNode<>( getLiteral(), helpCommand, getRequirement(), getRedirect(), getRedirectModifier(), isFork() ); helpCommand.node = node; // Set up a /... help command - LiteralArgumentBuilder helpNode = - LiteralArgumentBuilder.literal( "help" ).requires( x -> getArguments().stream() - .anyMatch( - y -> y.getRequirement() - .test( - x ) ) ) - .executes( helpCommand ); + LiteralArgumentBuilder helpNode = LiteralArgumentBuilder.literal( "help" ) + .requires( x -> getArguments().stream().anyMatch( y -> y.getRequirement().test( x ) ) ) + .executes( helpCommand ); // Add all normal command children to this and the help node for( CommandNode child : getArguments() ) { node.addChild( child ); - helpNode.then( LiteralArgumentBuilder.literal( child.getName() ).requires( child.getRequirement() ) + helpNode.then( LiteralArgumentBuilder.literal( child.getName() ) + .requires( child.getRequirement() ) .executes( helpForChild( child, id, command ) ) - .build() ); + .build() + ); } // And add alternative versions of which forward instead @@ -187,10 +120,12 @@ helpCommand, getRequirement(), { LiteralCommandNode child = childBuilder.build( id, command ); node.addChild( child ); - helpNode.then( LiteralArgumentBuilder.literal( child.getName() ).requires( child.getRequirement() ) + helpNode.then( LiteralArgumentBuilder.literal( child.getName() ) + .requires( child.getRequirement() ) .executes( helpForChild( child, id, command ) ) .redirect( child.getChild( "help" ) ) - .build() ); + .build() + ); } node.addChild( helpNode.build() ); @@ -198,6 +133,10 @@ helpCommand, getRequirement(), return node; } + private static final ChatFormatting HEADER = ChatFormatting.LIGHT_PURPLE; + private static final ChatFormatting SYNOPSIS = ChatFormatting.AQUA; + private static final ChatFormatting NAME = ChatFormatting.GREEN; + private static final class HelpCommand implements Command { private final String id; @@ -213,9 +152,54 @@ private HelpCommand( String id, String command ) @Override public int run( CommandContext context ) { - context.getSource() - .sendSuccess( getHelp( context, node, id, command ), false ); + context.getSource().sendSuccess( getHelp( context, node, id, command ), false ); return 0; } } + + private static Command helpForChild( CommandNode node, String id, String command ) + { + return context -> { + context.getSource().sendSuccess( getHelp( context, node, id + "." + node.getName().replace( '-', '_' ), command + " " + node.getName() ), false ); + return 0; + }; + } + + private static Component getHelp( CommandContext context, CommandNode node, String id, String command ) + { + // An ugly hack to extract usage information from the dispatcher. We generate a temporary node, generate + // the shorthand usage, and emit that. + CommandDispatcher dispatcher = context.getSource().getServer().getCommands().getDispatcher(); + CommandNode temp = new LiteralCommandNode<>( "_", null, x -> true, null, null, false ); + temp.addChild( node ); + String usage = dispatcher.getSmartUsage( temp, context.getSource() ).get( node ).substring( node.getName().length() ); + + MutableComponent output = new TextComponent( "" ) + .append( coloured( "/" + command + usage, HEADER ) ) + .append( " " ) + .append( coloured( translate( "commands." + id + ".synopsis" ), SYNOPSIS ) ) + .append( "\n" ) + .append( translate( "commands." + id + ".desc" ) ); + + for( CommandNode child : node.getChildren() ) + { + if( !child.getRequirement().test( context.getSource() ) || !(child instanceof LiteralCommandNode) ) + { + continue; + } + + output.append( "\n" ); + + MutableComponent component = coloured( child.getName(), NAME ); + component.getStyle().withClickEvent( new ClickEvent( + ClickEvent.Action.SUGGEST_COMMAND, + "/" + command + " " + child.getName() + ) ); + output.append( component ); + + output.append( " - " ).append( translate( "commands." + id + "." + child.getName() + ".synopsis" ) ); + } + + return output; + } } diff --git a/src/main/java/dan200/computercraft/shared/command/text/ChatHelpers.java b/src/main/java/dan200/computercraft/shared/command/text/ChatHelpers.java index 6bce38c25..9134f59ed 100644 --- a/src/main/java/dan200/computercraft/shared/command/text/ChatHelpers.java +++ b/src/main/java/dan200/computercraft/shared/command/text/ChatHelpers.java @@ -20,14 +20,12 @@ private ChatHelpers() {} public static MutableComponent coloured( String text, ChatFormatting colour ) { - MutableComponent component = new TextComponent( text == null ? "" : text ); - component.setStyle( component.getStyle().withColor( colour ) ); - return component; + return new TextComponent( text == null ? "" : text ).withStyle( colour ); } public static T coloured( T component, ChatFormatting colour ) { - component.setStyle( component.getStyle().withColor( colour ) ); + component.withStyle( colour ); return component; } @@ -46,10 +44,10 @@ public static MutableComponent translate( String text, Object... args ) return new TranslatableComponent( text == null ? "" : text, args ); } - public static MutableComponent list( MutableComponent... children ) + public static MutableComponent list( Component... children ) { MutableComponent component = new TextComponent( "" ); - for( MutableComponent child : children ) + for( Component child : children ) { component.append( child ); } @@ -69,12 +67,12 @@ public static MutableComponent bool( boolean value ) : coloured( translate( "commands.computercraft.generic.no" ), ChatFormatting.RED ); } - public static MutableComponent link( MutableComponent component, String command, MutableComponent toolTip ) + public static Component link( MutableComponent component, String command, Component toolTip ) { return link( component, new ClickEvent( ClickEvent.Action.RUN_COMMAND, command ), toolTip ); } - public static MutableComponent link( MutableComponent component, ClickEvent click, MutableComponent toolTip ) + public static Component link( Component component, ClickEvent click, Component toolTip ) { Style style = component.getStyle(); @@ -82,9 +80,7 @@ public static MutableComponent link( MutableComponent component, ClickEvent clic style = style.withClickEvent( click ); style = style.withHoverEvent( new HoverEvent( HoverEvent.Action.SHOW_TEXT, toolTip ) ); - component.setStyle( style ); - - return component; + return component.copy().withStyle( style ); } public static MutableComponent header( String text ) @@ -95,9 +91,9 @@ public static MutableComponent header( String text ) public static MutableComponent copy( String text ) { TextComponent name = new TextComponent( text ); - name.setStyle( name.getStyle() + Style style = name.getStyle() .withClickEvent( new ClickEvent( ClickEvent.Action.COPY_TO_CLIPBOARD, text ) ) - .withHoverEvent( new HoverEvent( HoverEvent.Action.SHOW_TEXT, new TranslatableComponent( "gui.computercraft.tooltip.copy" ) ) ) ); - return name; + .withHoverEvent( new HoverEvent( HoverEvent.Action.SHOW_TEXT, new TranslatableComponent( "gui.computercraft.tooltip.copy" ) ) ); + return name.withStyle( style ); } } diff --git a/src/main/java/dan200/computercraft/shared/command/text/ServerTableFormatter.java b/src/main/java/dan200/computercraft/shared/command/text/ServerTableFormatter.java index 2076f77f0..1e80fc425 100644 --- a/src/main/java/dan200/computercraft/shared/command/text/ServerTableFormatter.java +++ b/src/main/java/dan200/computercraft/shared/command/text/ServerTableFormatter.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.command.text; import net.minecraft.commands.CommandSourceStack; @@ -27,10 +26,7 @@ public ServerTableFormatter( CommandSourceStack source ) public Component getPadding( Component component, int width ) { int extraWidth = width - getWidth( component ); - if( extraWidth <= 0 ) - { - return null; - } + if( extraWidth <= 0 ) return null; return new TextComponent( StringUtils.repeat( ' ', extraWidth ) ); } @@ -43,8 +39,7 @@ public int getColumnPadding() @Override public int getWidth( Component component ) { - return component.getString() - .length(); + return component.getString().length(); } @Override diff --git a/src/main/java/dan200/computercraft/shared/command/text/TableBuilder.java b/src/main/java/dan200/computercraft/shared/command/text/TableBuilder.java index 8bdc6495a..a644547f8 100644 --- a/src/main/java/dan200/computercraft/shared/command/text/TableBuilder.java +++ b/src/main/java/dan200/computercraft/shared/command/text/TableBuilder.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.command.text; import dan200.computercraft.shared.command.CommandUtils; @@ -21,17 +20,14 @@ public class TableBuilder { private final int id; + private int columns = -1; private final Component[] headers; private final ArrayList rows = new ArrayList<>(); - private int columns = -1; private int additional; public TableBuilder( int id, @Nonnull Component... headers ) { - if( id < 0 ) - { - throw new IllegalArgumentException( "ID must be positive" ); - } + if( id < 0 ) throw new IllegalArgumentException( "ID must be positive" ); this.id = id; this.headers = headers; columns = headers.length; @@ -39,47 +35,33 @@ public TableBuilder( int id, @Nonnull Component... headers ) public TableBuilder( int id ) { - if( id < 0 ) - { - throw new IllegalArgumentException( "ID must be positive" ); - } + if( id < 0 ) throw new IllegalArgumentException( "ID must be positive" ); this.id = id; headers = null; } public TableBuilder( int id, @Nonnull String... headers ) { - if( id < 0 ) - { - throw new IllegalArgumentException( "ID must be positive" ); - } + if( id < 0 ) throw new IllegalArgumentException( "ID must be positive" ); this.id = id; this.headers = new Component[headers.length]; columns = headers.length; - for( int i = 0; i < headers.length; i++ ) - { - this.headers[i] = ChatHelpers.header( headers[i] ); - } + for( int i = 0; i < headers.length; i++ ) this.headers[i] = ChatHelpers.header( headers[i] ); } public void row( @Nonnull Component... row ) { - if( columns == -1 ) - { - columns = row.length; - } - if( row.length != columns ) - { - throw new IllegalArgumentException( "Row is the incorrect length" ); - } + if( columns == -1 ) columns = row.length; + if( row.length != columns ) throw new IllegalArgumentException( "Row is the incorrect length" ); rows.add( row ); } /** * Get the unique identifier for this table type. * - * When showing a table within Minecraft, previous instances of this table with the same ID will be removed from chat. + * When showing a table within Minecraft, previous instances of this table with + * the same ID will be removed from chat. * * @return This table's type. */ @@ -91,7 +73,8 @@ public int getId() /** * Get the number of columns for this table. * - * This will be the same as {@link #getHeaders()}'s length if it is is non-{@code null}, otherwise the length of the first column. + * This will be the same as {@link #getHeaders()}'s length if it is is non-{@code null}, + * otherwise the length of the first column. * * @return The number of columns. */ @@ -122,6 +105,20 @@ public void setAdditional( int additional ) this.additional = additional; } + /** + * Trim this table to a given height. + * + * @param height The desired height. + */ + public void trim( int height ) + { + if( rows.size() > height ) + { + additional += rows.size() - height - 1; + rows.subList( height - 1, rows.size() ).clear(); + } + } + public void display( CommandSourceStack source ) { if( CommandUtils.isPlayer( source ) ) @@ -135,19 +132,4 @@ public void display( CommandSourceStack source ) new ServerTableFormatter( source ).display( this ); } } - - /** - * Trim this table to a given height. - * - * @param height The desired height. - */ - public void trim( int height ) - { - if( rows.size() > height ) - { - additional += rows.size() - height - 1; - rows.subList( height - 1, rows.size() ) - .clear(); - } - } } diff --git a/src/main/java/dan200/computercraft/shared/command/text/TableFormatter.java b/src/main/java/dan200/computercraft/shared/command/text/TableFormatter.java index a3db10584..c3536fe92 100644 --- a/src/main/java/dan200/computercraft/shared/command/text/TableFormatter.java +++ b/src/main/java/dan200/computercraft/shared/command/text/TableFormatter.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.command.text; import net.minecraft.ChatFormatting; @@ -21,12 +20,30 @@ public interface TableFormatter Component SEPARATOR = coloured( "| ", ChatFormatting.GRAY ); Component HEADER = coloured( "=", ChatFormatting.GRAY ); + /** + * Get additional padding for the component. + * + * @param component The component to pad + * @param width The desired width for the component + * @return The padding for this component, or {@code null} if none is needed. + */ + @Nullable + Component getPadding( Component component, int width ); + + /** + * Get the minimum padding between each column. + * + * @return The minimum padding. + */ + int getColumnPadding(); + + int getWidth( Component component ); + + void writeLine( int id, Component component ); + default int display( TableBuilder table ) { - if( table.getColumns() <= 0 ) - { - return 0; - } + if( table.getColumns() <= 0 ) return 0; int rowId = table.getId(); int columns = table.getColumns(); @@ -35,10 +52,7 @@ default int display( TableBuilder table ) Component[] headers = table.getHeaders(); if( headers != null ) { - for( int i = 0; i < columns; i++ ) - { - maxWidths[i] = getWidth( headers[i] ); - } + for( int i = 0; i < columns; i++ ) maxWidths[i] = getWidth( headers[i] ); } for( Component[] row : table.getRows() ) @@ -46,28 +60,19 @@ default int display( TableBuilder table ) for( int i = 0; i < row.length; i++ ) { int width = getWidth( row[i] ); - if( width > maxWidths[i] ) - { - maxWidths[i] = width; - } + if( width > maxWidths[i] ) maxWidths[i] = width; } } // Add a small amount of padding after each column { int padding = getColumnPadding(); - for( int i = 0; i < maxWidths.length - 1; i++ ) - { - maxWidths[i] += padding; - } + for( int i = 0; i < maxWidths.length - 1; i++ ) maxWidths[i] += padding; } // And compute the total width int totalWidth = (columns - 1) * getWidth( SEPARATOR ); - for( int x : maxWidths ) - { - totalWidth += x; - } + for( int x : maxWidths ) totalWidth += x; if( headers != null ) { @@ -76,10 +81,7 @@ default int display( TableBuilder table ) { line.append( headers[i] ); Component padding = getPadding( headers[i], maxWidths[i] ); - if( padding != null ) - { - line.append( padding ); - } + if( padding != null ) line.append( padding ); line.append( SEPARATOR ); } line.append( headers[columns - 1] ); @@ -100,10 +102,7 @@ default int display( TableBuilder table ) { line.append( row[i] ); Component padding = getPadding( row[i], maxWidths[i] ); - if( padding != null ) - { - line.append( padding ); - } + if( padding != null ) line.append( padding ); line.append( SEPARATOR ); } line.append( row[columns - 1] ); @@ -117,25 +116,4 @@ default int display( TableBuilder table ) return rowId - table.getId(); } - - int getWidth( Component component ); - - /** - * Get the minimum padding between each column. - * - * @return The minimum padding. - */ - int getColumnPadding(); - - /** - * Get additional padding for the component. - * - * @param component The component to pad - * @param width The desired width for the component - * @return The padding for this component, or {@code null} if none is needed. - */ - @Nullable - Component getPadding( Component component, int width ); - - void writeLine( int id, Component component ); } diff --git a/src/main/java/dan200/computercraft/shared/common/BlockGeneric.java b/src/main/java/dan200/computercraft/shared/common/BlockGeneric.java index 3ed0c90c7..81cb1d44e 100644 --- a/src/main/java/dan200/computercraft/shared/common/BlockGeneric.java +++ b/src/main/java/dan200/computercraft/shared/common/BlockGeneric.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.common; import net.minecraft.core.BlockPos; @@ -23,66 +22,45 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.Random; +import java.util.function.Supplier; public abstract class BlockGeneric extends BaseEntityBlock { - private final BlockEntityType type; + private final Supplier> type; - public BlockGeneric( Properties settings, BlockEntityType type ) + public BlockGeneric( Properties settings, Supplier> type ) { super( settings ); this.type = type; } - public BlockEntityType getType() - { - return type; - } - - @Override - public RenderShape getRenderShape( BlockState state ) - { - return RenderShape.MODEL; - } - @Override @Deprecated - public final void neighborChanged( @Nonnull BlockState state, Level world, @Nonnull BlockPos pos, @Nonnull Block neighbourBlock, - @Nonnull BlockPos neighbourPos, boolean isMoving ) + public final void onRemove( @Nonnull BlockState block, @Nonnull Level world, @Nonnull BlockPos pos, BlockState replace, boolean bool ) { + if( block.getBlock() == replace.getBlock() ) return; + BlockEntity tile = world.getBlockEntity( pos ); - if( tile instanceof TileGeneric ) - { - ((TileGeneric) tile).onNeighbourChange( neighbourPos ); - } + super.onRemove( block, world, pos, replace, bool ); + world.removeBlockEntity( pos ); + if( tile instanceof TileGeneric generic ) generic.destroy(); } + @Nonnull @Override @Deprecated - public final void onRemove( @Nonnull BlockState block, @Nonnull Level world, @Nonnull BlockPos pos, BlockState replace, boolean bool ) + public final InteractionResult use( @Nonnull BlockState state, Level world, @Nonnull BlockPos pos, @Nonnull Player player, @Nonnull InteractionHand hand, @Nonnull BlockHitResult hit ) { - if( block.getBlock() == replace.getBlock() ) - { - return; - } - BlockEntity tile = world.getBlockEntity( pos ); - super.onRemove( block, world, pos, replace, bool ); - world.removeBlockEntity( pos ); - if( tile instanceof TileGeneric ) - { - ((TileGeneric) tile).destroy(); - } + return tile instanceof TileGeneric generic ? generic.onActivate( player, hand, hit ) : InteractionResult.PASS; } - @Nonnull @Override @Deprecated - public final InteractionResult use( @Nonnull BlockState state, Level world, @Nonnull BlockPos pos, @Nonnull Player player, @Nonnull InteractionHand hand, - @Nonnull BlockHitResult hit ) + public final void neighborChanged( @Nonnull BlockState state, Level world, @Nonnull BlockPos pos, @Nonnull Block neighbourBlock, @Nonnull BlockPos neighbourPos, boolean isMoving ) { BlockEntity tile = world.getBlockEntity( pos ); - return tile instanceof TileGeneric ? ((TileGeneric) tile).onActivate( player, hand, hit ) : InteractionResult.PASS; + if( tile instanceof TileGeneric generic ) generic.onNeighbourChange( neighbourPos ); } @Override @@ -90,20 +68,21 @@ public final InteractionResult use( @Nonnull BlockState state, Level world, @Non public void tick( @Nonnull BlockState state, ServerLevel world, @Nonnull BlockPos pos, @Nonnull Random rand ) { BlockEntity te = world.getBlockEntity( pos ); - if( te instanceof TileGeneric ) - { - ((TileGeneric) te).blockTick(); - } + if( te instanceof TileGeneric generic ) generic.blockTick(); } @Nullable @Override - public BlockEntity newBlockEntity( BlockPos pos, BlockState state ) + public BlockEntity newBlockEntity( @Nonnull BlockPos pos, @Nonnull BlockState state ) { - if( this.type != null ) - { - return type.create( pos, state ); - } - return null; + return type.get().create( pos, state ); + } + + @Nonnull + @Override + @Deprecated + public RenderShape getRenderShape( @Nonnull BlockState state ) + { + return RenderShape.MODEL; } } diff --git a/src/main/java/dan200/computercraft/shared/common/ClientTerminal.java b/src/main/java/dan200/computercraft/shared/common/ClientTerminal.java index be189e35e..1aca87cc6 100644 --- a/src/main/java/dan200/computercraft/shared/common/ClientTerminal.java +++ b/src/main/java/dan200/computercraft/shared/common/ClientTerminal.java @@ -3,12 +3,10 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.common; import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.shared.network.client.TerminalState; -import net.minecraft.nbt.CompoundTag; public class ClientTerminal implements ITerminal { @@ -79,19 +77,4 @@ private void deleteTerminal() terminalChanged = true; } } - - public void readDescription( CompoundTag nbt ) - { - colour = nbt.getBoolean( "colour" ); - if( nbt.contains( "terminal" ) ) - { - CompoundTag terminal = nbt.getCompound( "terminal" ); - resizeTerminal( terminal.getInt( "term_width" ), terminal.getInt( "term_height" ) ); - this.terminal.readFromNBT( terminal ); - } - else - { - deleteTerminal(); - } - } } diff --git a/src/main/java/dan200/computercraft/shared/common/ColourableRecipe.java b/src/main/java/dan200/computercraft/shared/common/ColourableRecipe.java index 21c0282ad..ab9baff41 100644 --- a/src/main/java/dan200/computercraft/shared/common/ColourableRecipe.java +++ b/src/main/java/dan200/computercraft/shared/common/ColourableRecipe.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.common; import dan200.computercraft.shared.util.ColourTracker; @@ -21,8 +20,6 @@ public final class ColourableRecipe extends CustomRecipe { - public static final RecipeSerializer SERIALIZER = new SimpleRecipeSerializer<>( ColourableRecipe::new ); - private ColourableRecipe( ResourceLocation id ) { super( id ); @@ -36,17 +33,11 @@ public boolean matches( @Nonnull CraftingContainer inv, @Nonnull Level world ) for( int i = 0; i < inv.getContainerSize(); i++ ) { ItemStack stack = inv.getItem( i ); - if( stack.isEmpty() ) - { - continue; - } + if( stack.isEmpty() ) continue; if( stack.getItem() instanceof IColouredItem ) { - if( hasColourable ) - { - return false; - } + if( hasColourable ) return false; hasColourable = true; } else if( ColourUtils.getStackColour( stack ) != null ) @@ -74,9 +65,11 @@ public ItemStack assemble( @Nonnull CraftingContainer inv ) { ItemStack stack = inv.getItem( i ); - if( stack.isEmpty() ) + if( stack.isEmpty() ) continue; + + if( stack.getItem() instanceof IColouredItem ) { - continue; + colourable = stack; } else { @@ -104,4 +97,6 @@ public RecipeSerializer getSerializer() { return SERIALIZER; } + + public static final SimpleRecipeSerializer SERIALIZER = new SimpleRecipeSerializer<>( ColourableRecipe::new ); } diff --git a/src/main/java/dan200/computercraft/shared/common/ContainerHeldItem.java b/src/main/java/dan200/computercraft/shared/common/ContainerHeldItem.java index e3492864e..3b2cd3e7b 100644 --- a/src/main/java/dan200/computercraft/shared/common/ContainerHeldItem.java +++ b/src/main/java/dan200/computercraft/shared/common/ContainerHeldItem.java @@ -3,10 +3,9 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.common; -import dan200.computercraft.shared.ComputerCraftRegistry; +import dan200.computercraft.shared.Registry; import dan200.computercraft.shared.network.container.HeldItemContainerData; import net.fabricmc.fabric.api.screenhandler.v1.ExtendedScreenHandlerFactory; import net.minecraft.network.FriendlyByteBuf; @@ -32,13 +31,12 @@ public ContainerHeldItem( MenuType type, int id, Pl super( type, id ); this.hand = hand; - stack = player.getItemInHand( hand ) - .copy(); + stack = player.getItemInHand( hand ).copy(); } public static ContainerHeldItem createPrintout( int id, Inventory inventory, HeldItemContainerData data ) { - return new ContainerHeldItem( ComputerCraftRegistry.ModContainers.PRINTOUT, id, inventory.player, data.getHand() ); + return new ContainerHeldItem( Registry.ModContainers.PRINTOUT, id, inventory.player, data.getHand() ); } @Nonnull @@ -50,10 +48,7 @@ public ItemStack getStack() @Override public boolean stillValid( @Nonnull Player player ) { - if( !player.isAlive() ) - { - return false; - } + if( !player.isAlive() ) return false; ItemStack stack = player.getItemInHand( hand ); return stack == this.stack || !stack.isEmpty() && !this.stack.isEmpty() && stack.getItem() == this.stack.getItem(); diff --git a/src/main/java/dan200/computercraft/shared/common/DefaultBundledRedstoneProvider.java b/src/main/java/dan200/computercraft/shared/common/DefaultBundledRedstoneProvider.java index 09800368b..07c49934d 100644 --- a/src/main/java/dan200/computercraft/shared/common/DefaultBundledRedstoneProvider.java +++ b/src/main/java/dan200/computercraft/shared/common/DefaultBundledRedstoneProvider.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.common; import dan200.computercraft.api.redstone.IBundledRedstoneProvider; @@ -24,11 +23,9 @@ public int getBundledRedstoneOutput( @Nonnull Level world, @Nonnull BlockPos pos public static int getDefaultBundledRedstoneOutput( Level world, BlockPos pos, Direction side ) { - Block block = world.getBlockState( pos ) - .getBlock(); - if( block instanceof IBundledRedstoneBlock ) + Block block = world.getBlockState( pos ).getBlock(); + if( block instanceof IBundledRedstoneBlock generic ) { - IBundledRedstoneBlock generic = (IBundledRedstoneBlock) block; if( generic.getBundledRedstoneConnectivity( world, pos, side ) ) { return generic.getBundledRedstoneOutput( world, pos, side ); diff --git a/src/main/java/dan200/computercraft/shared/common/IColouredItem.java b/src/main/java/dan200/computercraft/shared/common/IColouredItem.java index 50597e449..f6be5c387 100644 --- a/src/main/java/dan200/computercraft/shared/common/IColouredItem.java +++ b/src/main/java/dan200/computercraft/shared/common/IColouredItem.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.common; import net.minecraft.nbt.CompoundTag; @@ -18,12 +17,6 @@ default int getColour( ItemStack stack ) return getColourBasic( stack ); } - static int getColourBasic( ItemStack stack ) - { - CompoundTag tag = stack.getTag(); - return tag != null && tag.contains( NBT_COLOUR ) ? tag.getInt( NBT_COLOUR ) : -1; - } - default ItemStack withColour( ItemStack stack, int colour ) { ItemStack copy = stack.copy(); @@ -31,20 +24,22 @@ default ItemStack withColour( ItemStack stack, int colour ) return copy; } + static int getColourBasic( ItemStack stack ) + { + CompoundTag tag = stack.getTag(); + return tag != null && tag.contains( NBT_COLOUR ) ? tag.getInt( NBT_COLOUR ) : -1; + } + static void setColourBasic( ItemStack stack, int colour ) { if( colour == -1 ) { CompoundTag tag = stack.getTag(); - if( tag != null ) - { - tag.remove( NBT_COLOUR ); - } + if( tag != null ) tag.remove( NBT_COLOUR ); } else { - stack.getOrCreateTag() - .putInt( NBT_COLOUR, colour ); + stack.getOrCreateTag().putInt( NBT_COLOUR, colour ); } } } diff --git a/src/main/java/dan200/computercraft/shared/common/ServerTerminal.java b/src/main/java/dan200/computercraft/shared/common/ServerTerminal.java index 6a95c9e5b..760a13152 100644 --- a/src/main/java/dan200/computercraft/shared/common/ServerTerminal.java +++ b/src/main/java/dan200/computercraft/shared/common/ServerTerminal.java @@ -3,20 +3,18 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.common; import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.shared.network.client.TerminalState; -import net.minecraft.nbt.CompoundTag; import java.util.concurrent.atomic.AtomicBoolean; public class ServerTerminal implements ITerminal { private final boolean colour; - private final AtomicBoolean terminalChanged = new AtomicBoolean( false ); private Terminal terminal; + private final AtomicBoolean terminalChanged = new AtomicBoolean( false ); private boolean terminalChangedLastFrame = false; public ServerTerminal( boolean colour ) @@ -31,11 +29,6 @@ public ServerTerminal( boolean colour, int terminalWidth, int terminalHeight ) terminal = new Terminal( terminalWidth, terminalHeight, this::markTerminalChanged ); } - protected void markTerminalChanged() - { - terminalChanged.set( true ); - } - protected void resize( int width, int height ) { if( terminal == null ) @@ -58,6 +51,11 @@ public void delete() } } + protected void markTerminalChanged() + { + terminalChanged.set( true ); + } + public void update() { terminalChangedLastFrame = terminalChanged.getAndSet( false ); @@ -84,17 +82,4 @@ public TerminalState write() { return new TerminalState( colour, terminal ); } - - public void writeDescription( CompoundTag nbt ) - { - nbt.putBoolean( "colour", colour ); - if( terminal != null ) - { - CompoundTag terminal = new CompoundTag(); - terminal.putInt( "term_width", this.terminal.getWidth() ); - terminal.putInt( "term_height", this.terminal.getHeight() ); - this.terminal.writeToNBT( terminal ); - nbt.put( "terminal", terminal ); - } - } } diff --git a/src/main/java/dan200/computercraft/shared/common/TileGeneric.java b/src/main/java/dan200/computercraft/shared/common/TileGeneric.java index d78411b21..31508a198 100644 --- a/src/main/java/dan200/computercraft/shared/common/TileGeneric.java +++ b/src/main/java/dan200/computercraft/shared/common/TileGeneric.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.common; import net.minecraft.core.BlockPos; @@ -14,6 +13,7 @@ import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; @@ -33,6 +33,7 @@ public void destroy() { } + // This is called by fabric event hook in ComputerCraftProxyCommon public void onChunkUnloaded() { } @@ -42,7 +43,7 @@ public final void updateBlock() setChanged(); BlockPos pos = getBlockPos(); BlockState state = getBlockState(); - getLevel().sendBlockUpdated( pos, state, state, 3 ); + getLevel().sendBlockUpdated( pos, state, state, Block.UPDATE_ALL ); } @Nonnull @@ -70,26 +71,19 @@ protected double getInteractRange( Player player ) public boolean isUsable( Player player, boolean ignoreRange ) { - if( player == null || !player.isAlive() || getLevel().getBlockEntity( getBlockPos() ) != this ) - { - return false; - } - if( ignoreRange ) - { - return true; - } + if( player == null || !player.isAlive() || getLevel().getBlockEntity( getBlockPos() ) != this ) return false; + if( ignoreRange ) return true; double range = getInteractRange( player ); BlockPos pos = getBlockPos(); - return player.getCommandSenderWorld() == getLevel() && player.distanceToSqr( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 ) <= range * range; + return player.getCommandSenderWorld() == getLevel() && + player.distanceToSqr( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 ) <= range * range; } @Override public CompoundTag getUpdateTag() { - CompoundTag nbt = new CompoundTag(); - writeDescription( nbt ); - return nbt; + return this.saveWithoutMetadata(); } @Nullable 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 ca9d8175b..cc8423f80 100644 --- a/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java +++ b/src/main/java/dan200/computercraft/shared/computer/apis/CommandAPI.java @@ -3,31 +3,32 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.computer.apis; -import com.google.common.collect.ImmutableMap; import com.mojang.brigadier.tree.CommandNode; import com.mojang.brigadier.tree.LiteralCommandNode; import dan200.computercraft.ComputerCraft; import dan200.computercraft.api.lua.*; import dan200.computercraft.shared.computer.blocks.TileCommandComputer; +import dan200.computercraft.shared.peripheral.generic.data.BlockData; import dan200.computercraft.shared.util.NBTUtil; import net.minecraft.commands.CommandSourceStack; import net.minecraft.commands.Commands; import net.minecraft.core.BlockPos; import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceKey; +import net.minecraft.resources.ResourceLocation; import net.minecraft.server.MinecraftServer; import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.Property; +import javax.annotation.Nonnull; import java.util.*; /** * @cc.module commands + * @cc.since 1.7 */ public class CommandAPI implements ILuaAPI { @@ -44,29 +45,14 @@ public String[] getNames() return new String[] { "commands" }; } - /** - * Execute a specific command. - * - * @param command The command to execute. - * @return See {@code cc.treturn}. - * @cc.treturn boolean Whether the command executed successfully. - * @cc.treturn { string... } The output of this command, as a list of lines. - * @cc.treturn number|nil The number of "affected" objects, or `nil` if the command failed. The definition of this varies from command to command. - * @cc.usage Set the block above the command computer to stone. - *
-     *     commands.exec("setblock ~ ~1 ~ minecraft:stone")
-     *     
- */ - @LuaFunction( mainThread = true ) - public final Object[] exec( String command ) + private static Object createOutput( String output ) { - return doCommand( command ); + return new Object[] { output }; } private Object[] doCommand( String command ) { - MinecraftServer server = computer.getLevel() - .getServer(); + MinecraftServer server = computer.getLevel().getServer(); if( server == null || !server.isCommandBlockEnabled() ) { return new Object[] { false, createOutput( "Command blocks disabled by server" ) }; @@ -82,36 +68,64 @@ private Object[] doCommand( String command ) } catch( Throwable t ) { - if( ComputerCraft.logComputerErrors ) - { - ComputerCraft.log.error( "Error running command.", t ); - } + if( ComputerCraft.logComputerErrors ) ComputerCraft.log.error( "Error running command.", t ); return new Object[] { false, createOutput( "Java Exception Thrown: " + t ) }; } } - private static Object createOutput( String output ) + private static Map getBlockInfo( Level world, BlockPos pos ) { - return new Object[] { output }; + // Get the details of the block + BlockState state = world.getBlockState( pos ); + Map table = BlockData.fill( new HashMap<>(), state ); + + BlockEntity tile = world.getBlockEntity( pos ); + if( tile != null ) table.put( "nbt", NBTUtil.toLua( tile.saveWithFullMetadata() ) ); + + return table; + } + + /** + * Execute a specific command. + * + * @param command The command to execute. + * @return See {@code cc.treturn}. + * @cc.treturn boolean Whether the command executed successfully. + * @cc.treturn { string... } The output of this command, as a list of lines. + * @cc.treturn number|nil The number of "affected" objects, or `nil` if the command failed. The definition of this + * varies from command to command. + * @cc.changed 1.71 Added return value with command output. + * @cc.changed 1.85.0 Added return value with the number of affected objects. + * @cc.usage Set the block above the command computer to stone. + *
{@code
+     * commands.exec("setblock ~ ~1 ~ minecraft:stone")
+     * }
+ */ + @LuaFunction( mainThread = true ) + public final Object[] exec( String command ) + { + return doCommand( command ); } /** * Asynchronously execute a command. * - * Unlike {@link #exec}, this will immediately return, instead of waiting for the command to execute. This allows you to run multiple commands at the - * same time. + * Unlike {@link #exec}, this will immediately return, instead of waiting for the + * command to execute. This allows you to run multiple commands at the same + * time. * - * When this command has finished executing, it will queue a `task_complete` event containing the result of executing this command (what {@link #exec} - * would return). + * When this command has finished executing, it will queue a `task_complete` + * event containing the result of executing this command (what {@link #exec} would + * return). * * @param context The context this command executes under. * @param command The command to execute. * @return The "task id". When this command has been executed, it will queue a `task_complete` event with a matching id. * @throws LuaException (hidden) If the task cannot be created. * @cc.usage Asynchronously sets the block above the computer to stone. - *
-     *     commands.execAsync("~ ~1 ~ minecraft:stone")
-     *     
+ *
{@code
+     * commands.execAsync("setblock ~ ~1 ~ minecraft:stone")
+     * }
* @cc.see parallel One may also use the parallel API to run multiple commands at once. */ @LuaFunction @@ -131,33 +145,21 @@ public final long execAsync( ILuaContext context, String command ) throws LuaExc @LuaFunction( mainThread = true ) public final List list( IArguments args ) throws LuaException { - MinecraftServer server = computer.getLevel() - .getServer(); + MinecraftServer server = computer.getLevel().getServer(); - if( server == null ) - { - return Collections.emptyList(); - } - CommandNode node = server.getCommands() - .getDispatcher() - .getRoot(); + if( server == null ) return Collections.emptyList(); + CommandNode node = server.getCommands().getDispatcher().getRoot(); for( int j = 0; j < args.count(); j++ ) { String name = args.getString( j ); node = node.getChild( name ); - if( !(node instanceof LiteralCommandNode) ) - { - return Collections.emptyList(); - } + if( !(node instanceof LiteralCommandNode) ) return Collections.emptyList(); } List result = new ArrayList<>(); for( CommandNode child : node.getChildren() ) { - if( child instanceof LiteralCommandNode ) - { - result.add( child.getName() ); - } + if( child instanceof LiteralCommandNode ) result.add( child.getName() ); } return result; } @@ -182,37 +184,47 @@ public final Object[] getBlockPosition() /** * Get information about a range of blocks. * - * This returns the same information as @{getBlockInfo}, just for multiple blocks at once. + * This returns the same information as @{getBlockInfo}, just for multiple + * blocks at once. * - * Blocks are traversed by ascending y level, followed by z and x - the returned table may be indexed using `x + z*width + y*depth*depth`. + * Blocks are traversed by ascending y level, followed by z and x - the returned + * table may be indexed using `x + z*width + y*depth*depth`. * - * @param minX The start x coordinate of the range to query. - * @param minY The start y coordinate of the range to query. - * @param minZ The start z coordinate of the range to query. - * @param maxX The end x coordinate of the range to query. - * @param maxY The end y coordinate of the range to query. - * @param maxZ The end z coordinate of the range to query. + * @param minX The start x coordinate of the range to query. + * @param minY The start y coordinate of the range to query. + * @param minZ The start z coordinate of the range to query. + * @param maxX The end x coordinate of the range to query. + * @param maxY The end y coordinate of the range to query. + * @param maxZ The end z coordinate of the range to query. + * @param dimension The dimension to query (e.g. "minecraft:overworld"). Defaults to the current dimension. * @return A list of information about each block. * @throws LuaException If the coordinates are not within the world. * @throws LuaException If trying to get information about more than 4096 blocks. + * @cc.since 1.76 + * @cc.changed 1.99 Added {@code dimension} argument. */ @LuaFunction( mainThread = true ) - public final List> getBlockInfos( int minX, int minY, int minZ, int maxX, int maxY, int maxZ ) throws LuaException + public final List> getBlockInfos( int minX, int minY, int minZ, int maxX, int maxY, int maxZ, Optional dimension ) throws LuaException { // Get the details of the block - Level world = computer.getLevel(); - 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.isInWorldBounds( min ) || !world.isInWorldBounds( max ) ) + Level world = getLevel( dimension ); + 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 == null || !world.isInWorldBounds( min ) || !world.isInWorldBounds( max ) ) { throw new LuaException( "Co-ordinates out of range" ); } int blocks = (max.getX() - min.getX() + 1) * (max.getY() - min.getY() + 1) * (max.getZ() - min.getZ() + 1); - if( blocks > 4096 ) - { - throw new LuaException( "Too many blocks" ); - } + if( blocks > 4096 ) throw new LuaException( "Too many blocks" ); List> results = new ArrayList<>( blocks ); for( int y = min.getY(); y <= max.getY(); y++ ) @@ -230,71 +242,45 @@ public final Object[] getBlockPosition() return results; } - private static Map getBlockInfo( Level world, BlockPos pos ) - { - // Get the details of the block - BlockState state = world.getBlockState( pos ); - Block block = state.getBlock(); - - Map table = new HashMap<>(); - table.put( "name", Registry.BLOCK.getKey( block ).toString() ); - table.put( "world", world.dimension() ); - - Map stateTable = new HashMap<>(); - for( ImmutableMap.Entry, Comparable> entry : state.getValues().entrySet() ) - { - Property property = entry.getKey(); - stateTable.put( property.getName(), getPropertyValue( property, entry.getValue() ) ); - } - table.put( "state", stateTable ); - - BlockEntity tile = world.getBlockEntity( pos ); - if( tile != null ) - { - table.put( "nbt", NBTUtil.toLua( tile.saveWithFullMetadata() ) ); - } - - return table; - } - - @SuppressWarnings( { - "unchecked", - "rawtypes" - } ) - private static Object getPropertyValue( Property property, Comparable value ) - { - if( value instanceof String || value instanceof Number || value instanceof Boolean ) - { - return value; - } - return property.getName( value ); - } - /** * Get some basic information about a block. * - * The returned table contains the current name, metadata and block state (as with @{turtle.inspect}). If there is a tile entity for that block, its NBT + * The returned table contains the current name, metadata and block state (as + * with @{turtle.inspect}). If there is a tile entity for that block, its NBT * will also be returned. * - * @param x The x position of the block to query. - * @param y The y position of the block to query. - * @param z The z position of the block to query. + * @param x The x position of the block to query. + * @param y The y position of the block to query. + * @param z The z position of the block to query. + * @param dimension The dimension to query (e.g. "minecraft:overworld"). Defaults to the current dimension. * @return The given block's information. * @throws LuaException If the coordinates are not within the world, or are not currently loaded. + * @cc.changed 1.76 Added block state info to return value + * @cc.changed 1.99 Added {@code dimension} argument. */ @LuaFunction( mainThread = true ) - public final Map getBlockInfo( int x, int y, int z ) throws LuaException + public final Map getBlockInfo( int x, int y, int z, Optional dimension ) throws LuaException { - // Get the details of the block - Level world = computer.getLevel(); + Level level = getLevel( dimension ); BlockPos position = new BlockPos( x, y, z ); - if( world.isInWorldBounds( position ) ) - { - return getBlockInfo( world, position ); - } - else - { - throw new LuaException( "Co-ordinates out of range" ); - } + if( !level.isInWorldBounds( position ) ) throw new LuaException( "Co-ordinates out of range" ); + return getBlockInfo( level, position ); + } + + @Nonnull + private Level getLevel( @Nonnull Optional id ) throws LuaException + { + Level currentLevel = computer.getLevel(); + if( currentLevel == null ) throw new LuaException( "No world exists" ); + + if( !id.isPresent() ) return currentLevel; + + ResourceLocation dimensionId = ResourceLocation.tryParse( id.get() ); + if( dimensionId == null ) throw new LuaException( "Invalid dimension name" ); + + Level level = currentLevel.getServer().getLevel( ResourceKey.create( Registry.DIMENSION_REGISTRY, dimensionId ) ); + if( level == null ) throw new LuaException( "Unknown dimension" ); + + return level; } } diff --git a/src/main/java/dan200/computercraft/shared/computer/blocks/BlockComputer.java b/src/main/java/dan200/computercraft/shared/computer/blocks/BlockComputer.java index 5e5f2ceda..2f3a87a93 100644 --- a/src/main/java/dan200/computercraft/shared/computer/blocks/BlockComputer.java +++ b/src/main/java/dan200/computercraft/shared/computer/blocks/BlockComputer.java @@ -3,19 +3,15 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - 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.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.StateDefinition; @@ -25,32 +21,33 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.util.function.Supplier; -public class BlockComputer extends BlockComputerBase +public class BlockComputer extends BlockComputerBase { public static final EnumProperty STATE = EnumProperty.create( "state", ComputerState.class ); public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING; - public BlockComputer( Properties settings, ComputerFamily family, BlockEntityType type ) + public BlockComputer( Properties settings, ComputerFamily family, Supplier> type ) { super( settings, family, type ); - registerDefaultState( defaultBlockState().setValue( FACING, Direction.NORTH ) - .setValue( STATE, ComputerState.OFF ) ); + registerDefaultState( defaultBlockState() + .setValue( FACING, Direction.NORTH ) + .setValue( STATE, ComputerState.OFF ) + ); } - @Nullable @Override - public BlockState getStateForPlacement( BlockPlaceContext placement ) + protected void createBlockStateDefinition( StateDefinition.Builder builder ) { - return defaultBlockState().setValue( FACING, - placement.getHorizontalDirection() - .getOpposite() ); + builder.add( FACING, STATE ); } + @Nullable @Override - protected void createBlockStateDefinition( StateDefinition.Builder builder ) + public BlockState getStateForPlacement( BlockPlaceContext placement ) { - builder.add( FACING, STATE ); + return defaultBlockState().setValue( FACING, placement.getHorizontalDirection().getOpposite() ); } @Nonnull @@ -59,21 +56,4 @@ protected ItemStack getItem( TileComputerBase tile ) { 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 newBlockEntity( 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 00dd5a4ea..dae541159 100644 --- a/src/main/java/dan200/computercraft/shared/computer/blocks/BlockComputerBase.java +++ b/src/main/java/dan200/computercraft/shared/computer/blocks/BlockComputerBase.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.computer.blocks; import dan200.computercraft.ComputerCraft; @@ -23,6 +22,7 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.BaseEntityBlock; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityTicker; import net.minecraft.world.level.block.entity.BlockEntityType; @@ -33,17 +33,21 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.util.function.Supplier; public abstract class BlockComputerBase extends BlockGeneric implements IBundledRedstoneBlock { private static final ResourceLocation DROP = new ResourceLocation( ComputerCraft.MOD_ID, "computer" ); private final ComputerFamily family; + protected final Supplier> type; + private final BlockEntityTicker serverTicker = ( level, pos, state, computer ) -> computer.serverTick(); - protected BlockComputerBase( Properties settings, ComputerFamily family, BlockEntityType type ) + protected BlockComputerBase( Properties settings, ComputerFamily family, Supplier> type ) { super( settings, type ); this.family = family; + this.type = type; } @Override @@ -53,10 +57,7 @@ public void onPlace( @Nonnull BlockState state, @Nonnull Level world, @Nonnull B super.onPlace( state, world, pos, oldState, isMoving ); BlockEntity tile = world.getBlockEntity( pos ); - if( tile instanceof TileComputerBase ) - { - ((TileComputerBase) tile).updateInput(); - } + if( tile instanceof TileComputerBase computer ) computer.updateInputsImmediately(); } @Override @@ -66,39 +67,35 @@ public boolean isSignalSource( @Nonnull BlockState state ) return true; } - @Override - @Deprecated - public int getSignal( @Nonnull BlockState state, @Nonnull BlockGetter world, @Nonnull BlockPos pos, @Nonnull Direction incomingSide ) - { - return getDirectSignal( state, world, pos, incomingSide ); - } - @Override @Deprecated public int getDirectSignal( @Nonnull BlockState state, BlockGetter world, @Nonnull BlockPos pos, @Nonnull Direction incomingSide ) { BlockEntity entity = world.getBlockEntity( pos ); - if( !(entity instanceof TileComputerBase) ) - { - return 0; - } + if( !(entity instanceof TileComputerBase computerEntity) ) return 0; - TileComputerBase computerEntity = (TileComputerBase) entity; ServerComputer computer = computerEntity.getServerComputer(); - if( computer == null ) - { - return 0; - } + if( computer == null ) return 0; ComputerSide localSide = computerEntity.remapToLocalSide( incomingSide.getOpposite() ); return computer.getRedstoneOutput( localSide ); } + @Nonnull + protected abstract ItemStack getItem( TileComputerBase tile ); + public ComputerFamily getFamily() { return family; } + @Override + @Deprecated + public int getSignal( @Nonnull BlockState state, @Nonnull BlockGetter world, @Nonnull BlockPos pos, @Nonnull Direction incomingSide ) + { + return getDirectSignal( state, world, pos, incomingSide ); + } + @Override public boolean getBundledRedstoneConnectivity( Level world, BlockPos pos, Direction side ) { @@ -109,56 +106,15 @@ public boolean getBundledRedstoneConnectivity( Level world, BlockPos pos, Direct public int getBundledRedstoneOutput( Level world, BlockPos pos, Direction side ) { BlockEntity entity = world.getBlockEntity( pos ); - if( !(entity instanceof TileComputerBase) ) - { - return 0; - } + if( !(entity instanceof TileComputerBase computerEntity) ) return 0; - TileComputerBase computerEntity = (TileComputerBase) entity; ServerComputer computer = computerEntity.getServerComputer(); - if( computer == null ) - { - return 0; - } + if( computer == null ) return 0; ComputerSide localSide = computerEntity.remapToLocalSide( side ); return computer.getBundledRedstoneOutput( localSide ); } - @Override - public void playerDestroy( @Nonnull Level world, Player player, @Nonnull BlockPos pos, @Nonnull BlockState state, @Nullable BlockEntity tile, - @Nonnull ItemStack tool ) - { - // Don't drop blocks here - see onBlockHarvested. - player.awardStat( Stats.BLOCK_MINED.get( this ) ); - player.causeFoodExhaustion( 0.005F ); - } - - @Override - public void setPlacedBy( @Nonnull Level world, @Nonnull BlockPos pos, @Nonnull BlockState state, LivingEntity placer, @Nonnull ItemStack stack ) - { - super.setPlacedBy( world, pos, state, placer, stack ); - - BlockEntity tile = world.getBlockEntity( pos ); - if( !world.isClientSide && tile instanceof IComputerTile && stack.getItem() instanceof IComputerItem ) - { - IComputerTile computer = (IComputerTile) tile; - IComputerItem item = (IComputerItem) stack.getItem(); - - int id = item.getComputerID( stack ); - if( id != -1 ) - { - computer.setComputerID( id ); - } - - String label = item.getLabel( stack ); - if( label != null ) - { - computer.setLabel( label ); - } - } - } - @Nonnull @Override public ItemStack getCloneItemStack( BlockGetter world, BlockPos pos, BlockState state ) @@ -167,38 +123,33 @@ public ItemStack getCloneItemStack( BlockGetter world, BlockPos pos, BlockState if( tile instanceof TileComputerBase ) { ItemStack result = getItem( (TileComputerBase) tile ); - if( !result.isEmpty() ) - { - return result; - } + if( !result.isEmpty() ) return result; } return super.getCloneItemStack( world, pos, state ); } - @Nonnull - protected abstract ItemStack getItem( TileComputerBase tile ); + @Override + public void playerDestroy( @Nonnull Level world, Player player, @Nonnull BlockPos pos, @Nonnull BlockState state, @Nullable BlockEntity tile, @Nonnull ItemStack tool ) + { + // Don't drop blocks here - see onBlockHarvested. + player.awardStat( Stats.BLOCK_MINED.get( this ) ); + player.causeFoodExhaustion( 0.005F ); + } @Override public void playerWillDestroy( @Nonnull Level world, @Nonnull BlockPos pos, @Nonnull BlockState state, @Nonnull Player player ) { - // Call super as it is what provides sound and block break particles. Does not do anything else. - super.playerWillDestroy( world, pos, state, player ); - - if( !(world instanceof ServerLevel) ) - { - return; - } - ServerLevel serverWorld = (ServerLevel) world; + if( !(world instanceof ServerLevel serverWorld) ) return; // We drop the item here instead of doing it in the harvest method, as we should // drop computers for creative players too. BlockEntity tile = world.getBlockEntity( pos ); - if( tile instanceof TileComputerBase ) + if( tile instanceof TileComputerBase computer ) { - TileComputerBase computer = (TileComputerBase) tile; - LootContext.Builder context = new LootContext.Builder( serverWorld ).withRandom( world.random ) + LootContext.Builder context = new LootContext.Builder( serverWorld ) + .withRandom( world.random ) .withParameter( LootContextParams.ORIGIN, Vec3.atCenterOf( pos ) ) .withParameter( LootContextParams.TOOL, player.getMainHandItem() ) .withParameter( LootContextParams.THIS_ENTITY, player ) @@ -213,15 +164,27 @@ public void playerWillDestroy( @Nonnull Level world, @Nonnull BlockPos pos, @Non } } - @Nullable @Override - public BlockEntityTicker getTicker( Level world, BlockState state, BlockEntityType type ) + public void setPlacedBy( @Nonnull Level world, @Nonnull BlockPos pos, @Nonnull BlockState state, LivingEntity placer, @Nonnull ItemStack stack ) { - return world.isClientSide ? null : ( world1, pos, state1, tile ) -> { - if( tile instanceof TileComputerBase computer ) - { - computer.serverTick(); - } - }; + super.setPlacedBy( world, pos, state, placer, stack ); + + BlockEntity tile = world.getBlockEntity( pos ); + if( !world.isClientSide && tile instanceof IComputerTile computer && stack.getItem() instanceof IComputerItem item ) + { + + int id = item.getComputerID( stack ); + if( id != -1 ) computer.setComputerID( id ); + + String label = item.getLabel( stack ); + if( label != null ) computer.setLabel( label ); + } + } + + @Override + @Nullable + public BlockEntityTicker getTicker( @Nonnull Level level, @Nonnull BlockState state, @Nonnull BlockEntityType type ) + { + return level.isClientSide ? null : BaseEntityBlock.createTickerHelper( type, this.type.get(), serverTicker ); } } diff --git a/src/main/java/dan200/computercraft/shared/computer/blocks/ComputerPeripheral.java b/src/main/java/dan200/computercraft/shared/computer/blocks/ComputerPeripheral.java index 23b1cd9d7..e06db68f2 100644 --- a/src/main/java/dan200/computercraft/shared/computer/blocks/ComputerPeripheral.java +++ b/src/main/java/dan200/computercraft/shared/computer/blocks/ComputerPeripheral.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.computer.blocks; import dan200.computercraft.api.lua.LuaFunction; @@ -16,8 +15,8 @@ /** * A computer or turtle wrapped as a peripheral. * - * This allows for basic interaction with adjacent computers. Computers wrapped as peripherals will have the type {@code computer} while turtles will be - * {@code turtle}. + * This allows for basic interaction with adjacent computers. Computers wrapped as peripherals will have the type + * {@code computer} while turtles will be {@code turtle}. * * @cc.module computer */ @@ -39,19 +38,6 @@ public String getType() return type; } - @Nonnull - @Override - public Object getTarget() - { - return computer.getTile(); - } - - @Override - public boolean equals( IPeripheral other ) - { - return other instanceof ComputerPeripheral && computer == ((ComputerPeripheral) other).computer; - } - /** * Turn the other computer on. */ @@ -114,4 +100,17 @@ public final String getLabel() { return computer.getLabel(); } + + @Override + public boolean equals( IPeripheral other ) + { + return other instanceof ComputerPeripheral && computer == ((ComputerPeripheral) other).computer; + } + + @Nonnull + @Override + public Object getTarget() + { + return computer.getTile(); + } } diff --git a/src/main/java/dan200/computercraft/shared/computer/blocks/ComputerProxy.java b/src/main/java/dan200/computercraft/shared/computer/blocks/ComputerProxy.java index 2eee9af0d..1b95cd78d 100644 --- a/src/main/java/dan200/computercraft/shared/computer/blocks/ComputerProxy.java +++ b/src/main/java/dan200/computercraft/shared/computer/blocks/ComputerProxy.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.computer.blocks; import dan200.computercraft.shared.computer.core.IComputer; @@ -14,7 +13,7 @@ /** * A proxy object for computer objects, delegating to {@link IComputer} or {@link TileComputer} where appropriate. */ -public class ComputerProxy +public final class ComputerProxy { private final Supplier get; @@ -23,6 +22,11 @@ public ComputerProxy( Supplier get ) this.get = get; } + TileComputerBase getTile() + { + return get.get(); + } + public void turnOn() { TileComputerBase tile = getTile(); @@ -37,11 +41,6 @@ public void turnOn() } } - protected TileComputerBase getTile() - { - return get.get(); - } - public void shutdown() { TileComputerBase tile = getTile(); 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 75dffb0b9..4c00dff5a 100644 --- a/src/main/java/dan200/computercraft/shared/computer/blocks/TileCommandComputer.java +++ b/src/main/java/dan200/computercraft/shared/computer/blocks/TileCommandComputer.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.computer.blocks; import dan200.computercraft.ComputerCraft; @@ -32,11 +31,55 @@ public class TileCommandComputer extends TileComputer { + public class CommandReceiver implements CommandSource + { + private final Map output = new HashMap<>(); + + public void clearOutput() + { + output.clear(); + } + + public Map getOutput() + { + return output; + } + + public Map copyOutput() + { + return new HashMap<>( output ); + } + + @Override + public void sendMessage( @Nonnull Component textComponent, @Nonnull UUID id ) + { + output.put( output.size() + 1, textComponent.getString() ); + } + + @Override + public boolean acceptsSuccess() + { + return true; + } + + @Override + public boolean acceptsFailure() + { + return true; + } + + @Override + public boolean shouldInformAdmins() + { + return getLevel().getGameRules().getBoolean( GameRules.RULE_COMMANDBLOCKOUTPUT ); + } + } + private final CommandReceiver receiver; - public TileCommandComputer( ComputerFamily family, BlockEntityType type, BlockPos pos, BlockState state ) + public TileCommandComputer( BlockEntityType type, BlockPos pos, BlockState state ) { - super( family, type, pos, state ); + super( type, pos, state, ComputerFamily.COMMAND ); receiver = new CommandReceiver(); } @@ -52,21 +95,15 @@ public CommandSourceStack getSource() if( computer != null ) { String label = computer.getLabel(); - if( label != null ) - { - name = label; - } + if( label != null ) name = label; } return new CommandSourceStack( receiver, - new Vec3( worldPosition.getX() + 0.5, worldPosition.getY() + 0.5, worldPosition.getZ() + 0.5 ), - Vec2.ZERO, - (ServerLevel) getLevel(), - 2, - name, - new TextComponent( name ), - getLevel().getServer(), - null ); + new Vec3( worldPosition.getX() + 0.5, worldPosition.getY() + 0.5, worldPosition.getZ() + 0.5 ), Vec2.ZERO, + (ServerLevel) getLevel(), 2, + name, new TextComponent( name ), + getLevel().getServer(), null + ); } @Override @@ -91,8 +128,7 @@ public static boolean isUsable( Player player ) player.displayClientMessage( new TranslatableComponent( "advMode.notEnabled" ), true ); return false; } - else if( ComputerCraft.commandRequireCreative ? !player.canUseGameMasterBlocks() : !server.getPlayerList() - .isOp( player.getGameProfile() ) ) + else if( ComputerCraft.commandRequireCreative ? !player.canUseGameMasterBlocks() : !server.getPlayerList().isOp( player.getGameProfile() ) ) { player.displayClientMessage( new TranslatableComponent( "advMode.notAllowed" ), true ); return false; @@ -100,50 +136,4 @@ else if( ComputerCraft.commandRequireCreative ? !player.canUseGameMasterBlocks() return true; } - - public class CommandReceiver implements CommandSource - { - private final Map output = new HashMap<>(); - - public void clearOutput() - { - output.clear(); - } - - public Map getOutput() - { - return output; - } - - public Map copyOutput() - { - return new HashMap<>( output ); - } - - @Override - public void sendMessage( @Nonnull Component textComponent, @Nonnull UUID id ) - { - output.put( output.size() + 1, textComponent.getString() ); - } - - @Override - public boolean acceptsSuccess() - { - return getLevel().getGameRules() - .getBoolean( GameRules.RULE_SENDCOMMANDFEEDBACK ); - } - - @Override - public boolean acceptsFailure() - { - return true; - } - - @Override - public boolean shouldInformAdmins() - { - return getLevel().getGameRules() - .getBoolean( GameRules.RULE_COMMANDBLOCKOUTPUT ); - } - } } 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 82fbdeac4..fe58f8bdd 100644 --- a/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputer.java +++ b/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputer.java @@ -3,12 +3,11 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - 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.Registry; import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ComputerState; import dan200.computercraft.shared.computer.core.ServerComputer; @@ -28,24 +27,27 @@ public class TileComputer extends TileComputerBase { private ComputerProxy proxy; - public TileComputer( ComputerFamily family, BlockEntityType type, BlockPos pos, BlockState state ) + public TileComputer( BlockEntityType type, BlockPos pos, BlockState state, ComputerFamily family ) { - super( type, family, pos, state ); + super( type, pos, state, family ); } - public boolean isUsableByPlayer( Player player ) + @Override + protected ServerComputer createComputer( int instanceID, int id ) { - return isUsable( player, false ); + ComputerFamily family = getFamily(); + ServerComputer computer = new ServerComputer( + getLevel(), id, label, instanceID, family, + ComputerCraft.computerTermWidth, + ComputerCraft.computerTermHeight + ); + computer.setPosition( getBlockPos() ); + return computer; } - @Override - protected void updateBlockState( ComputerState newState ) + protected boolean isUsableByPlayer( Player player ) { - BlockState existing = getBlockState(); - if( existing.getValue( BlockComputer.STATE ) != newState ) - { - getLevel().setBlock( getBlockPos(), existing.setValue( BlockComputer.STATE, newState ), 3 ); - } + return isUsable( player, false ); } @Override @@ -55,57 +57,30 @@ public Direction getDirection() } @Override - protected ComputerSide remapLocalSide( ComputerSide localSide ) + protected void updateBlockState( ComputerState newState ) { - // For legacy reasons, computers invert the meaning of "left" and "right". A computer's front is facing - // towards you, but a turtle's front is facing the other way. - if( localSide == ComputerSide.RIGHT ) - { - return ComputerSide.LEFT; - } - if( localSide == ComputerSide.LEFT ) + BlockState existing = getBlockState(); + if( existing.getValue( BlockComputer.STATE ) != newState ) { - return ComputerSide.RIGHT; + getLevel().setBlock( getBlockPos(), existing.setValue( BlockComputer.STATE, newState ), 3 ); } - return localSide; - } - - @Override - protected ServerComputer createComputer( int instanceID, int id ) - { - ComputerFamily family = getFamily(); - ServerComputer computer = new ServerComputer( getLevel(), - id, label, - instanceID, - family, - ComputerCraft.computerTermWidth, - ComputerCraft.computerTermHeight ); - computer.setPosition( getBlockPos() ); - return computer; } @Override - public ComputerProxy createProxy() + protected ComputerSide remapLocalSide( ComputerSide localSide ) { - if( proxy == null ) - { - proxy = new ComputerProxy( () -> this ) - { - @Override - protected TileComputerBase getTile() - { - return TileComputer.this; - } - }; - } - return proxy; + // For legacy reasons, computers invert the meaning of "left" and "right". A computer's front is facing + // towards you, but a turtle's front is facing the other way. + if( localSide == ComputerSide.RIGHT ) return ComputerSide.LEFT; + if( localSide == ComputerSide.LEFT ) return ComputerSide.RIGHT; + return localSide; } @Nullable @Override public AbstractContainerMenu createMenu( int id, @Nonnull Inventory inventory, @Nonnull Player player ) { - return new ComputerMenuWithoutInventory( ComputerCraftRegistry.ModContainers.COMPUTER, id, inventory, this::isUsableByPlayer, createServerComputer(), getFamily() ); + return new ComputerMenuWithoutInventory( Registry.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 8b0169e1a..55d77855b 100644 --- a/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputerBase.java +++ b/src/main/java/dan200/computercraft/shared/computer/blocks/TileComputerBase.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.computer.blocks; import dan200.computercraft.ComputerCraft; @@ -28,6 +27,7 @@ import net.minecraft.network.chat.Component; import net.minecraft.network.chat.TextComponent; import net.minecraft.network.chat.TranslatableComponent; +import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; @@ -35,9 +35,6 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; -import net.minecraft.world.level.Level; -import net.minecraft.world.level.block.Blocks; -import net.minecraft.world.level.block.RedStoneWireBlock; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.BlockHitResult; @@ -46,26 +43,40 @@ import javax.annotation.Nullable; import java.util.Objects; -public abstract class TileComputerBase extends TileGeneric implements IComputerTile, IPeripheralTile, Nameable, - ExtendedScreenHandlerFactory +public abstract class TileComputerBase extends TileGeneric implements IComputerTile, IPeripheralTile, Nameable, ExtendedScreenHandlerFactory { private static final String NBT_ID = "ComputerId"; private static final String NBT_LABEL = "Label"; private static final String NBT_ON = "On"; - private final ComputerFamily family; - protected String label = null; - boolean startOn = false; + private int instanceID = -1; private int computerID = -1; + protected String label = null; private boolean on = false; + boolean startOn = false; private boolean fresh = false; - public TileComputerBase( BlockEntityType type, ComputerFamily family, BlockPos pos, BlockState state ) + private int invalidSides = 0; + + private final ComputerFamily family; + + private ComputerProxy proxy; + + public TileComputerBase( BlockEntityType type, BlockPos pos, BlockState state, ComputerFamily family ) { super( type, pos, state ); this.family = family; } + protected void unload() + { + if( instanceID >= 0 ) + { + if( !getLevel().isClientSide ) ComputerCraft.serverComputerRegistry.remove( instanceID ); + instanceID = -1; + } + } + @Override public void destroy() { @@ -82,16 +93,16 @@ public void onChunkUnloaded() unload(); } - protected void unload() + @Override + public void setRemoved() { - if( instanceID >= 0 ) - { - if( !getLevel().isClientSide ) - { - ComputerCraft.serverComputerRegistry.remove( instanceID ); - } - instanceID = -1; - } + unload(); + super.setRemoved(); + } + + protected boolean canNameWithTag( Player player ) + { + return false; } @Nonnull @@ -104,8 +115,7 @@ public InteractionResult onActivate( Player player, InteractionHand hand, BlockH // Label to rename computer if( !getLevel().isClientSide ) { - setLabel( currentItem.getHoverName() - .getString() ); + setLabel( currentItem.getHoverName().getString() ); currentItem.shrink( 1 ); } return InteractionResult.SUCCESS; @@ -124,104 +134,75 @@ else if( !player.isCrouching() ) return InteractionResult.PASS; } - protected boolean canNameWithTag( Player player ) + @Override + public void onNeighbourChange( @Nonnull BlockPos neighbour ) { - return false; + updateInputAt( neighbour ); } - public ServerComputer createServerComputer() + @Override + public void onNeighbourTileEntityChange( @Nonnull BlockPos neighbour ) { - if( getLevel().isClientSide ) - { - return null; - } - - boolean changed = false; - if( instanceID < 0 ) - { - instanceID = ComputerCraft.serverComputerRegistry.getUnusedInstanceID(); - changed = true; - } - if( !ComputerCraft.serverComputerRegistry.contains( instanceID ) ) - { - ServerComputer computer = createComputer( instanceID, computerID ); - ComputerCraft.serverComputerRegistry.add( instanceID, computer ); - fresh = true; - changed = true; - } - if( changed ) - { - updateBlock(); - updateInput(); - } - return ComputerCraft.serverComputerRegistry.get( instanceID ); + updateInputAt( neighbour ); } - public ServerComputer getServerComputer() + protected void serverTick() { - return getLevel().isClientSide ? null : ComputerCraft.serverComputerRegistry.get( instanceID ); - } - - protected abstract ServerComputer createComputer( int instanceID, int id ); + ServerComputer computer = createServerComputer(); - public void updateInput() - { - if( getLevel() == null || getLevel().isClientSide ) + if( invalidSides != 0 ) { - return; + for( Direction direction : DirectionUtil.FACINGS ) + { + if( (invalidSides & (1 << direction.ordinal())) != 0 ) refreshPeripheral( computer, direction ); + } } - // Update all sides - ServerComputer computer = getServerComputer(); - if( computer == null ) + // If the computer isn't on and should be, then turn it on + if( startOn || (fresh && on) ) { - return; + computer.turnOn(); + startOn = false; } - BlockPos pos = computer.getPosition(); - for( Direction dir : DirectionUtil.FACINGS ) - { - updateSideInput( computer, dir, pos.relative( dir ) ); - } - } + computer.keepAlive(); - private void updateSideInput( ServerComputer computer, Direction dir, BlockPos offset ) - { - Direction offsetSide = dir.getOpposite(); - ComputerSide localDir = remapToLocalSide( dir ); + fresh = false; + computerID = computer.getID(); + label = computer.getLabel(); + on = computer.isOn(); - computer.setRedstoneInput( localDir, getRedstoneInput( level, offset, dir ) ); - computer.setBundledRedstoneInput( localDir, BundledRedstone.getOutput( getLevel(), offset, offsetSide ) ); - if( !isPeripheralBlockedOnSide( localDir ) ) - { - IPeripheral peripheral = Peripherals.getPeripheral( getLevel(), offset, offsetSide ); - computer.setPeripheral( localDir, peripheral ); - } + // 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() ); + + // TODO: This should ideally be split up into label/id/on (which should save NBT and sync to client) and + // redstone (which should update outputs) + if( computer.hasOutputChanged() ) updateOutput(); } - protected ComputerSide remapToLocalSide( Direction globalSide ) + protected abstract void updateBlockState( ComputerState newState ); + + @Override + public void saveAdditional( @Nonnull CompoundTag nbt ) { - return remapLocalSide( DirectionUtil.toLocal( getDirection(), globalSide ) ); + // Save ID, label and power state + if( computerID >= 0 ) nbt.putInt( NBT_ID, computerID ); + if( label != null ) nbt.putString( NBT_LABEL, label ); + nbt.putBoolean( NBT_ON, on ); + + super.saveAdditional( nbt ); } - /** - * Gets the redstone input for an adjacent block. - * - * @param world The world we exist in - * @param pos The position of the neighbour - * @param side The side we are reading from - * @return The effective redstone power - */ - protected static int getRedstoneInput( Level world, BlockPos pos, Direction side ) + @Override + public void load( @Nonnull CompoundTag nbt ) { - int power = world.getSignal( pos, side ); - if( power >= 15 ) - { - return power; - } + super.load( nbt ); - BlockState neighbour = world.getBlockState( pos ); - return neighbour.getBlock() == Blocks.REDSTONE_WIRE ? Math.max( power, neighbour.getValue( RedStoneWireBlock.POWER ) ) : power; + // Load ID, label and power state + computerID = nbt.contains( NBT_ID ) ? nbt.getInt( NBT_ID ) : -1; + label = nbt.contains( NBT_LABEL ) ? nbt.getString( NBT_LABEL ) : null; + on = startOn = nbt.getBoolean( NBT_ON ); } protected boolean isPeripheralBlockedOnSide( ComputerSide localSide ) @@ -229,87 +210,92 @@ protected boolean isPeripheralBlockedOnSide( ComputerSide localSide ) return false; } + protected abstract Direction getDirection(); + + protected ComputerSide remapToLocalSide( Direction globalSide ) + { + return remapLocalSide( DirectionUtil.toLocal( getDirection(), globalSide ) ); + } + protected ComputerSide remapLocalSide( ComputerSide localSide ) { return localSide; } - protected abstract Direction getDirection(); - - @Override - public void onNeighbourChange( @Nonnull BlockPos neighbour ) + private void updateRedstoneInput( @Nonnull ServerComputer computer, Direction dir, BlockPos targetPos ) { - updateInput( neighbour ); + Direction offsetSide = dir.getOpposite(); + ComputerSide localDir = remapToLocalSide( dir ); + + computer.setRedstoneInput( localDir, RedstoneUtil.getRedstoneInput( level, targetPos, dir ) ); + computer.setBundledRedstoneInput( localDir, BundledRedstone.getOutput( getLevel(), targetPos, offsetSide ) ); } - @Override - public void onNeighbourTileEntityChange( @Nonnull BlockPos neighbour ) + private void refreshPeripheral( @Nonnull ServerComputer computer, Direction dir ) { - updateInput( neighbour ); + invalidSides &= ~(1 << dir.ordinal()); + + ComputerSide localDir = remapToLocalSide( dir ); + if( isPeripheralBlockedOnSide( localDir ) ) return; + + Direction offsetSide = dir.getOpposite(); + IPeripheral peripheral = Peripherals.getPeripheral( getLevel(), getBlockPos().relative( dir ), offsetSide ); + computer.setPeripheral( localDir, peripheral ); } - @Override - protected void readDescription( @Nonnull CompoundTag nbt ) + public void updateInputsImmediately() { - super.readDescription( nbt ); - label = nbt.contains( NBT_LABEL ) ? nbt.getString( NBT_LABEL ) : null; - computerID = nbt.contains( NBT_ID ) ? nbt.getInt( NBT_ID ) : -1; + ServerComputer computer = getServerComputer(); + if( computer != null ) updateInputsImmediately( computer ); } - @Override - protected void writeDescription( @Nonnull CompoundTag nbt ) + /** + * Update all redstone and peripherals. + * + * This should only be really be called when the computer is being ticked (though there are some cases where it + * won't be), as peripheral scanning requires adjacent tiles to be in a "correct" state - which may not be the case + * if they're still updating! + * + * @param computer The current computer instance. + */ + private void updateInputsImmediately( @Nonnull ServerComputer computer ) { - super.writeDescription( nbt ); - if( label != null ) - { - nbt.putString( NBT_LABEL, label ); - } - if( computerID >= 0 ) + BlockPos pos = getBlockPos(); + for( Direction dir : DirectionUtil.FACINGS ) { - nbt.putInt( NBT_ID, computerID ); + updateRedstoneInput( computer, dir, pos.relative( dir ) ); + refreshPeripheral( computer, dir ); } } - public void serverTick() + private void updateInputAt( @Nonnull BlockPos neighbour ) { - ServerComputer computer = createServerComputer(); - if( computer == null ) - { - return; - } - - // If the computer isn't on and should be, then turn it on - if( startOn || fresh && on ) - { - computer.turnOn(); - startOn = false; - } - - computer.keepAlive(); - - fresh = false; - computerID = computer.getID(); - label = computer.getLabel(); - on = computer.isOn(); + ServerComputer computer = getServerComputer(); + if( computer == null ) return; - if( computer.hasOutputChanged() ) + for( Direction dir : DirectionUtil.FACINGS ) { - updateOutput(); + BlockPos offset = getBlockPos().relative( dir ); + if( offset.equals( neighbour ) ) + { + updateRedstoneInput( computer, dir, offset ); + invalidSides |= 1 << dir.ordinal(); + return; + } } - // 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 the position is not any adjacent one, update all inputs. This is pretty terrible, but some redstone mods + // handle this incorrectly. + BlockPos pos = getBlockPos(); + for( Direction dir : DirectionUtil.FACINGS ) updateRedstoneInput( computer, dir, pos.relative( dir ) ); + invalidSides = (1 << 6) - 1; // Mark all peripherals as dirty. } + /** + * Update the block's state and propagate redstone output. + */ public void updateOutput() { - // Update redstone updateBlock(); for( Direction dir : DirectionUtil.FACINGS ) { @@ -317,138 +303,106 @@ public void updateOutput() } } - protected abstract void updateBlockState( ComputerState newState ); + protected abstract ServerComputer createComputer( int instanceID, int id ); @Override - public void load( @Nonnull CompoundTag nbt ) + public final int getComputerID() { - super.load( nbt ); - - // Load ID, label and power state - computerID = nbt.contains( NBT_ID ) ? nbt.getInt( NBT_ID ) : -1; - label = nbt.contains( NBT_LABEL ) ? nbt.getString( NBT_LABEL ) : null; - on = startOn = nbt.getBoolean( NBT_ON ); + return computerID; } @Override - public void saveAdditional( @Nonnull CompoundTag nbt ) + public final String getLabel() { - // Save ID, label and power state - if( computerID >= 0 ) - { - nbt.putInt( NBT_ID, computerID ); - } - if( label != null ) - { - nbt.putString( NBT_LABEL, label ); - } - nbt.putBoolean( NBT_ON, on ); + return label; } @Override - public void setRemoved() - { - unload(); - super.setRemoved(); - } - - private void updateInput( BlockPos neighbour ) + public final void setComputerID( int id ) { - if( getLevel() == null || this.level.isClientSide ) - { - return; - } + if( getLevel().isClientSide || computerID == id ) return; + computerID = id; ServerComputer computer = getServerComputer(); - if( computer == null ) - { - return; - } - - for( Direction dir : DirectionUtil.FACINGS ) - { - BlockPos offset = worldPosition.relative( dir ); - if( offset.equals( neighbour ) ) - { - updateSideInput( computer, dir, offset ); - return; - } - } - - // If the position is not any adjacent one, update all inputs. - this.updateInput(); + if( computer != null ) computer.setID( computerID ); + setChanged(); } - private void updateInput( Direction dir ) + @Override + public final void setLabel( String label ) { - if( getLevel() == null || this.level.isClientSide ) - { - return; - } + if( getLevel().isClientSide || Objects.equals( this.label, label ) ) return; + this.label = label; ServerComputer computer = getServerComputer(); - if( computer == null ) - { - return; - } - - updateSideInput( computer, dir, worldPosition.relative( dir ) ); + if( computer != null ) computer.setLabel( label ); + setChanged(); } @Override - public final int getComputerID() + public ComputerFamily getFamily() { - return computerID; + return family; } - @Override - public final void setComputerID( int id ) + @Nonnull + public ServerComputer createServerComputer() { - if( this.level.isClientSide || computerID == id ) + if( getLevel().isClientSide ) throw new IllegalStateException( "Cannot access server computer on the client." ); + + boolean changed = false; + if( instanceID < 0 ) { - return; + instanceID = ComputerCraft.serverComputerRegistry.getUnusedInstanceID(); + changed = true; } - computerID = id; - ServerComputer computer = getServerComputer(); - if( computer != null ) + ServerComputer computer = ComputerCraft.serverComputerRegistry.get( instanceID ); + if( computer == null ) { - computer.setID( computerID ); + computer = createComputer( instanceID, computerID ); + ComputerCraft.serverComputerRegistry.add( instanceID, computer ); + fresh = true; + changed = true; } - setChanged(); + + if( changed ) updateInputsImmediately( computer ); + return computer; } - @Override - public final String getLabel() + @Nullable + public ServerComputer getServerComputer() { - return label; + return getLevel().isClientSide ? null : ComputerCraft.serverComputerRegistry.get( instanceID ); } // Networking stuff + @Nonnull @Override - public final void setLabel( String label ) + public final ClientboundBlockEntityDataPacket getUpdatePacket() { - if( this.level.isClientSide || Objects.equals( this.label, label ) ) - { - return; - } - - this.label = label; - ServerComputer computer = getServerComputer(); - if( computer != null ) - { - computer.setLabel( label ); - } - setChanged(); + return ClientboundBlockEntityDataPacket.create( this ); } + @Nonnull @Override - public ComputerFamily getFamily() + public CompoundTag getUpdateTag() { - return family; + // We need this for pick block on the client side. + CompoundTag nbt = super.getUpdateTag(); + if( label != null ) nbt.putString( NBT_LABEL, label ); + if( computerID >= 0 ) nbt.putInt( NBT_ID, computerID ); + return nbt; } + // @Override + // public void handleUpdateTag( @Nonnull CompoundTag nbt ) + // { + // label = nbt.contains( NBT_LABEL ) ? nbt.getString( NBT_LABEL ) : null; + // computerID = nbt.contains( NBT_ID ) ? nbt.getInt( NBT_ID ) : -1; + // } + protected void transferStateFrom( TileComputerBase copy ) { if( copy.computerID != computerID || copy.instanceID != instanceID ) @@ -468,17 +422,17 @@ protected void transferStateFrom( TileComputerBase copy ) @Override public IPeripheral getPeripheral( Direction side ) { - return new ComputerPeripheral( "computer", createProxy() ); + if( proxy == null ) proxy = new ComputerProxy( () -> this ); + return new ComputerPeripheral( "computer", proxy ); } - public abstract ComputerProxy createProxy(); - @Nonnull @Override public Component getName() { - return hasCustomName() ? new TextComponent( label ) : new TranslatableComponent( getBlockState().getBlock() - .getDescriptionId() ); + return hasCustomName() + ? new TextComponent( label ) + : new TranslatableComponent( getBlockState().getBlock().getDescriptionId() ); } @Override @@ -487,18 +441,18 @@ public boolean hasCustomName() return !Strings.isNullOrEmpty( label ); } - @Nonnull + @Nullable @Override - public Component getDisplayName() + public Component getCustomName() { - return Nameable.super.getDisplayName(); + return hasCustomName() ? new TextComponent( label ) : null; } - @Nullable + @Nonnull @Override - public Component getCustomName() + public Component getDisplayName() { - return hasCustomName() ? new TextComponent( label ) : null; + return Nameable.super.getDisplayName(); } @Override diff --git a/src/main/java/dan200/computercraft/shared/computer/core/ClientComputer.java b/src/main/java/dan200/computercraft/shared/computer/core/ClientComputer.java index df0a18625..32aa113b5 100644 --- a/src/main/java/dan200/computercraft/shared/computer/core/ClientComputer.java +++ b/src/main/java/dan200/computercraft/shared/computer/core/ClientComputer.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.computer.core; import dan200.computercraft.shared.common.ClientTerminal; @@ -19,7 +18,6 @@ public class ClientComputer extends ClientTerminal implements IComputer private boolean blinking = false; private CompoundTag userData = null; - public ClientComputer( int instanceID ) { super( false ); @@ -45,6 +43,18 @@ public int getInstanceID() return instanceID; } + @Override + public boolean isOn() + { + return on; + } + + @Override + public boolean isCursorDisplayed() + { + return on && blinking; + } + @Override public void turnOn() { @@ -73,24 +83,10 @@ public void queueEvent( String event, Object[] arguments ) NetworkHandler.sendToServer( new QueueEventServerMessage( instanceID, event, arguments ) ); } - @Override - public boolean isOn() - { - return on; - } - - @Override - public boolean isCursorDisplayed() - { - return on && blinking; - } - @Override public void keyDown( int key, boolean repeat ) { - NetworkHandler.sendToServer( new KeyEventServerMessage( instanceID, - repeat ? KeyEventServerMessage.TYPE_REPEAT : KeyEventServerMessage.TYPE_DOWN, - key ) ); + NetworkHandler.sendToServer( new KeyEventServerMessage( instanceID, repeat ? KeyEventServerMessage.TYPE_REPEAT : KeyEventServerMessage.TYPE_DOWN, key ) ); } @Override diff --git a/src/main/java/dan200/computercraft/shared/computer/core/ComputerFamily.java b/src/main/java/dan200/computercraft/shared/computer/core/ComputerFamily.java index 5964b3dbd..a68f770d9 100644 --- a/src/main/java/dan200/computercraft/shared/computer/core/ComputerFamily.java +++ b/src/main/java/dan200/computercraft/shared/computer/core/ComputerFamily.java @@ -3,10 +3,11 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.computer.core; public enum ComputerFamily { - NORMAL, ADVANCED, COMMAND + NORMAL, + ADVANCED, + COMMAND } diff --git a/src/main/java/dan200/computercraft/shared/computer/core/ComputerRegistry.java b/src/main/java/dan200/computercraft/shared/computer/core/ComputerRegistry.java index 29451df8f..24120048d 100644 --- a/src/main/java/dan200/computercraft/shared/computer/core/ComputerRegistry.java +++ b/src/main/java/dan200/computercraft/shared/computer/core/ComputerRegistry.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.computer.core; import java.util.Collection; @@ -13,23 +12,15 @@ public class ComputerRegistry { - private final Map computers; + private final Map computers = new HashMap<>(); private int nextUnusedInstanceID; private int sessionID; protected ComputerRegistry() { - computers = new HashMap<>(); reset(); } - public void reset() - { - computers.clear(); - nextUnusedInstanceID = 0; - sessionID = new Random().nextInt(); - } - public int getSessionID() { return sessionID; @@ -76,4 +67,11 @@ public void remove( int instanceID ) { computers.remove( instanceID ); } + + public void reset() + { + computers.clear(); + nextUnusedInstanceID = 0; + sessionID = new Random().nextInt(); + } } 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 35f815cde..917264053 100644 --- a/src/main/java/dan200/computercraft/shared/computer/core/ComputerState.java +++ b/src/main/java/dan200/computercraft/shared/computer/core/ComputerState.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.computer.core; import net.minecraft.util.StringRepresentable; @@ -12,7 +11,9 @@ public enum ComputerState implements StringRepresentable { - OFF( "off", "" ), ON( "on", "_on" ), BLINKING( "blinking", "_blink" ); + OFF( "off", "" ), + ON( "on", "_on" ), + BLINKING( "blinking", "_blink" ); private final String name; private final String texture; diff --git a/src/main/java/dan200/computercraft/shared/computer/core/IComputer.java b/src/main/java/dan200/computercraft/shared/computer/core/IComputer.java index 149e3a057..7398776dc 100644 --- a/src/main/java/dan200/computercraft/shared/computer/core/IComputer.java +++ b/src/main/java/dan200/computercraft/shared/computer/core/IComputer.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.computer.core; import dan200.computercraft.shared.common.ITerminal; @@ -12,30 +11,27 @@ public interface IComputer extends ITerminal, InputHandler { int getInstanceID(); + boolean isOn(); + + boolean isCursorDisplayed(); + void turnOn(); void shutdown(); void reboot(); + @Override + void queueEvent( String event, Object[] arguments ); + default void queueEvent( String event ) { queueEvent( event, null ); } - @Override - void queueEvent( String event, Object[] arguments ); - default ComputerState getState() { - if( !isOn() ) - { - return ComputerState.OFF; - } + if( !isOn() ) return ComputerState.OFF; return isCursorDisplayed() ? ComputerState.BLINKING : ComputerState.ON; } - - boolean isOn(); - - boolean isCursorDisplayed(); } 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 f33461ab6..31dc660a7 100644 --- a/src/main/java/dan200/computercraft/shared/computer/core/IContainerComputer.java +++ b/src/main/java/dan200/computercraft/shared/computer/core/IContainerComputer.java @@ -3,12 +3,12 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.computer.core; import dan200.computercraft.shared.computer.upload.FileSlice; import dan200.computercraft.shared.computer.upload.FileUpload; import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.inventory.AbstractContainerMenu; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -16,9 +16,9 @@ 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. + * An instance of {@link AbstractContainerMenu} which provides a computer. You should implement this + * if you provide custom computers/GUIs to interact with them. */ -//@FunctionalInterface public interface IContainerComputer { /** diff --git a/src/main/java/dan200/computercraft/shared/computer/core/InputState.java b/src/main/java/dan200/computercraft/shared/computer/core/InputState.java index 111faee60..b1a9d77af 100644 --- a/src/main/java/dan200/computercraft/shared/computer/core/InputState.java +++ b/src/main/java/dan200/computercraft/shared/computer/core/InputState.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.computer.core; import it.unimi.dsi.fastutil.ints.IntIterator; @@ -11,7 +10,8 @@ import it.unimi.dsi.fastutil.ints.IntSet; /** - * An {@link InputHandler} which keeps track of the current key and mouse state, and releases them when the container is closed. + * An {@link InputHandler} which keeps track of the current key and mouse state, and releases them when the container + * is closed. */ public class InputState implements InputHandler { @@ -31,10 +31,7 @@ public InputState( IContainerComputer owner ) public void queueEvent( String event, Object[] arguments ) { IComputer computer = owner.getComputer(); - if( computer != null ) - { - computer.queueEvent( event, arguments ); - } + if( computer != null ) computer.queueEvent( event, arguments ); } @Override @@ -42,10 +39,7 @@ public void keyDown( int key, boolean repeat ) { keysDown.add( key ); IComputer computer = owner.getComputer(); - if( computer != null ) - { - computer.keyDown( key, repeat ); - } + if( computer != null ) computer.keyDown( key, repeat ); } @Override @@ -53,10 +47,7 @@ public void keyUp( int key ) { keysDown.remove( key ); IComputer computer = owner.getComputer(); - if( computer != null ) - { - computer.keyUp( key ); - } + if( computer != null ) computer.keyUp( key ); } @Override @@ -67,10 +58,7 @@ public void mouseClick( int button, int x, int y ) lastMouseDown = button; IComputer computer = owner.getComputer(); - if( computer != null ) - { - computer.mouseClick( button, x, y ); - } + if( computer != null ) computer.mouseClick( button, x, y ); } @Override @@ -81,10 +69,7 @@ public void mouseUp( int button, int x, int y ) lastMouseDown = -1; IComputer computer = owner.getComputer(); - if( computer != null ) - { - computer.mouseUp( button, x, y ); - } + if( computer != null ) computer.mouseUp( button, x, y ); } @Override @@ -95,10 +80,7 @@ public void mouseDrag( int button, int x, int y ) lastMouseDown = button; IComputer computer = owner.getComputer(); - if( computer != null ) - { - computer.mouseDrag( button, x, y ); - } + if( computer != null ) computer.mouseDrag( button, x, y ); } @Override @@ -108,10 +90,7 @@ public void mouseScroll( int direction, int x, int y ) lastMouseY = y; IComputer computer = owner.getComputer(); - if( computer != null ) - { - computer.mouseScroll( direction, x, y ); - } + if( computer != null ) computer.mouseScroll( direction, x, y ); } public void close() @@ -120,15 +99,9 @@ public void close() if( computer != null ) { IntIterator keys = keysDown.iterator(); - while( keys.hasNext() ) - { - computer.keyUp( keys.nextInt() ); - } - - if( lastMouseDown != -1 ) - { - computer.mouseUp( lastMouseDown, lastMouseX, lastMouseY ); - } + while( keys.hasNext() ) computer.keyUp( keys.nextInt() ); + + if( lastMouseDown != -1 ) computer.mouseUp( lastMouseDown, lastMouseX, lastMouseY ); } keysDown.clear(); diff --git a/src/main/java/dan200/computercraft/shared/computer/core/ServerComputer.java b/src/main/java/dan200/computercraft/shared/computer/core/ServerComputer.java index cdc9fdcad..e63bf35e5 100644 --- a/src/main/java/dan200/computercraft/shared/computer/core/ServerComputer.java +++ b/src/main/java/dan200/computercraft/shared/computer/core/ServerComputer.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.computer.core; import dan200.computercraft.ComputerCraft; @@ -24,6 +23,7 @@ import dan200.computercraft.shared.network.client.ComputerDeletedClientMessage; import dan200.computercraft.shared.network.client.ComputerTerminalClientMessage; import me.shedaniel.cloth.api.utils.v1.GameInstanceUtils; +import net.minecraft.SharedConstants; import net.minecraft.core.BlockPos; import net.minecraft.nbt.CompoundTag; import net.minecraft.server.MinecraftServer; @@ -38,32 +38,27 @@ public class ServerComputer extends ServerTerminal implements IComputer, IComputerEnvironment { private final int instanceID; + + private Level level; + private BlockPos position; + private final ComputerFamily family; private final Computer computer; - private Level world; - private BlockPos position; private CompoundTag userData; private boolean changed; private boolean changedLastFrame; private int ticksSincePing; - public ServerComputer( Level world, int computerID, String label, int instanceID, ComputerFamily family, int terminalWidth, int terminalHeight ) + public ServerComputer( Level level, int computerID, String label, int instanceID, ComputerFamily family, int terminalWidth, int terminalHeight ) { super( family != ComputerFamily.NORMAL, terminalWidth, terminalHeight ); this.instanceID = instanceID; - this.world = world; - position = null; - + this.level = level; this.family = family; computer = new Computer( this, getTerminal(), computerID ); computer.setLabel( label ); - userData = null; - changed = false; - - changedLastFrame = false; - ticksSincePing = 0; } public ComputerFamily getFamily() @@ -71,14 +66,14 @@ public ComputerFamily getFamily() return family; } - public Level getWorld() + public Level getLevel() { - return world; + return level; } - public void setWorld( Level world ) + public void setLevel( Level level ) { - this.world = world; + this.level = level; } public BlockPos getPosition() @@ -123,6 +118,11 @@ public boolean hasTimedOut() return ticksSincePing > 100; } + public boolean hasOutputChanged() + { + return changedLastFrame; + } + public void unload() { computer.unload(); @@ -142,84 +142,92 @@ public void updateUserData() changed = true; } + private NetworkMessage createComputerPacket() + { + return new ComputerDataClientMessage( this ); + } + + protected NetworkMessage createTerminalPacket() + { + return new ComputerTerminalClientMessage( getInstanceID(), write() ); + } + public void broadcastState( boolean force ) { if( hasOutputChanged() || force ) { // Send computer state to all clients - MinecraftServer server = GameInstanceUtils.getServer(); - if( server != null ) - { - NetworkHandler.sendToAllPlayers( server, createComputerPacket() ); - } + NetworkHandler.sendToAllPlayers( createComputerPacket() ); } if( hasTerminalChanged() || force ) { + // Send terminal state to clients who are currently interacting with the computer. MinecraftServer server = GameInstanceUtils.getServer(); - if( server != null ) - { - // Send terminal state to clients who are currently interacting with the computer. - NetworkMessage packet = null; - for( Player player : server.getPlayerList() - .getPlayers() ) + NetworkMessage packet = null; + for( Player player : server.getPlayerList().getPlayers() ) + { + if( isInteracting( player ) ) { - if( isInteracting( player ) ) - { - if( packet == null ) - { - packet = createTerminalPacket(); - } - NetworkHandler.sendToPlayer( player, packet ); - } + if( packet == null ) packet = createTerminalPacket(); + NetworkHandler.sendToPlayer( player, packet ); } } } } - public boolean hasOutputChanged() + public void sendComputerState( Player player ) { - return changedLastFrame; + // Send state to client + NetworkHandler.sendToPlayer( player, createComputerPacket() ); } - private NetworkMessage createComputerPacket() + public void sendTerminalState( Player player ) { - return new ComputerDataClientMessage( this ); + // Send terminal state to client + NetworkHandler.sendToPlayer( player, createTerminalPacket() ); } - protected boolean isInteracting( Player player ) + public void broadcastDelete() { - return getContainer( player ) != null; + // Send deletion to client + NetworkHandler.sendToAllPlayers( new ComputerDeletedClientMessage( getInstanceID() ) ); } - protected NetworkMessage createTerminalPacket() + public void setID( int id ) { - return new ComputerTerminalClientMessage( getInstanceID(), write() ); + computer.setID( id ); } - @Nullable - public IContainerComputer getContainer( Player player ) + // IComputer + + @Override + public int getInstanceID() { - if( player == null ) - { - return null; - } + return instanceID; + } - AbstractContainerMenu container = player.containerMenu; - if( !(container instanceof IContainerComputer) ) - { - return null; - } + public int getID() + { + return computer.getID(); + } - IContainerComputer computerContainer = (IContainerComputer) container; - return computerContainer.getComputer() != this ? null : computerContainer; + public String getLabel() + { + return computer.getLabel(); } @Override - public int getInstanceID() + public boolean isOn() { - return instanceID; + return computer.isOn(); + } + + @Override + public boolean isCursorDisplayed() + { + return computer.isOn() && computer.isBlinking(); } @Override @@ -229,8 +237,6 @@ public void turnOn() computer.turnOn(); } - // IComputer - @Override public void shutdown() { @@ -252,53 +258,39 @@ public void queueEvent( String event, Object[] arguments ) computer.queueEvent( event, arguments ); } - @Override - public boolean isOn() - { - return computer.isOn(); - } - - @Override - public boolean isCursorDisplayed() + public int getRedstoneOutput( ComputerSide side ) { - return computer.isOn() && computer.isBlinking(); + return computer.getEnvironment().getExternalRedstoneOutput( side ); } - public void sendComputerState( Player player ) + public void setRedstoneInput( ComputerSide side, int level ) { - // Send state to client - NetworkHandler.sendToPlayer( player, createComputerPacket() ); + computer.getEnvironment().setRedstoneInput( side, level ); } - public void sendTerminalState( Player player ) + public int getBundledRedstoneOutput( ComputerSide side ) { - // Send terminal state to client - NetworkHandler.sendToPlayer( player, createTerminalPacket() ); + return computer.getEnvironment().getExternalBundledRedstoneOutput( side ); } - public void broadcastDelete() + public void setBundledRedstoneInput( ComputerSide side, int combination ) { - // Send deletion to client - MinecraftServer server = GameInstanceUtils.getServer(); - if( server != null ) - { - NetworkHandler.sendToAllPlayers( server, new ComputerDeletedClientMessage( getInstanceID() ) ); - } + computer.getEnvironment().setBundledRedstoneInput( side, combination ); } - public int getID() + public void addAPI( ILuaAPI api ) { - return computer.getID(); + computer.addApi( api ); } - public void setID( int id ) + public void setPeripheral( ComputerSide side, IPeripheral peripheral ) { - computer.setID( id ); + computer.getEnvironment().setPeripheral( side, peripheral ); } - public String getLabel() + public IPeripheral getPeripheral( ComputerSide side ) { - return computer.getLabel(); + return computer.getEnvironment().getPeripheral( side ); } public void setLabel( String label ) @@ -306,59 +298,36 @@ public void setLabel( String label ) computer.setLabel( label ); } - public int getRedstoneOutput( ComputerSide side ) - { - return computer.getEnvironment() - .getExternalRedstoneOutput( side ); - } - - public void setRedstoneInput( ComputerSide side, int level ) - { - computer.getEnvironment() - .setRedstoneInput( side, level ); - } - - public int getBundledRedstoneOutput( ComputerSide side ) - { - return computer.getEnvironment() - .getExternalBundledRedstoneOutput( side ); - } - - public void setBundledRedstoneInput( ComputerSide side, int combination ) - { - computer.getEnvironment() - .setBundledRedstoneInput( side, combination ); - } + // IComputerEnvironment implementation - public void addAPI( ILuaAPI api ) + @Override + public double getTimeOfDay() { - computer.addApi( api ); + return (level.getDayTime() + 6000) % 24000 / 1000.0; } - // IComputerEnvironment implementation - - public void setPeripheral( ComputerSide side, IPeripheral peripheral ) + @Override + public int getDay() { - computer.getEnvironment() - .setPeripheral( side, peripheral ); + return (int) ((level.getDayTime() + 6000) / 24000) + 1; } - public IPeripheral getPeripheral( ComputerSide side ) + @Override + public IWritableMount createSaveDirMount( String subPath, long capacity ) { - return computer.getEnvironment() - .getPeripheral( side ); + return ComputerCraftAPI.createSaveDirMount( level, subPath, capacity ); } @Override - public int getDay() + public IMount createResourceMount( String domain, String subPath ) { - return (int) ((world.getDayTime() + 6000) / 24000) + 1; + return ComputerCraftAPI.createResourceMount( domain, subPath ); } @Override - public double getTimeOfDay() + public InputStream createResourceFile( String domain, String subPath ) { - return (world.getDayTime() + 6000) % 24000 / 1000.0; + return ComputerCraftAPIImpl.getResourceFile( domain, subPath ); } @Override @@ -371,7 +340,7 @@ public long getComputerSpaceLimit() @Override public String getHostString() { - return String.format( "ComputerCraft %s (Minecraft %s)", ComputerCraftAPI.getInstalledVersion(), "1.16.4" ); + return String.format( "ComputerCraft %s (Minecraft %s)", ComputerCraftAPI.getInstalledVersion(), SharedConstants.getCurrentVersion() ); } @Nonnull @@ -384,24 +353,22 @@ public String getUserAgent() @Override public int assignNewID() { - return ComputerCraftAPI.createUniqueNumberedSaveDir( world, "computer" ); + return ComputerCraftAPI.createUniqueNumberedSaveDir( level, "computer" ); } - @Override - public IWritableMount createSaveDirMount( String subPath, long capacity ) + @Nullable + public IContainerComputer getContainer( Player player ) { - return ComputerCraftAPI.createSaveDirMount( world, subPath, capacity ); - } + if( player == null ) return null; - @Override - public IMount createResourceMount( String domain, String subPath ) - { - return ComputerCraftAPI.createResourceMount( domain, subPath ); + AbstractContainerMenu container = player.containerMenu; + if( !(container instanceof IContainerComputer computerContainer) ) return null; + + return computerContainer.getComputer() != this ? null : computerContainer; } - @Override - public InputStream createResourceFile( String domain, String subPath ) + protected boolean isInteracting( Player player ) { - return ComputerCraftAPIImpl.getResourceFile( domain, subPath ); + return getContainer( player ) != null; } } diff --git a/src/main/java/dan200/computercraft/shared/computer/core/ServerComputerRegistry.java b/src/main/java/dan200/computercraft/shared/computer/core/ServerComputerRegistry.java index 448a43571..fa5a92424 100644 --- a/src/main/java/dan200/computercraft/shared/computer/core/ServerComputerRegistry.java +++ b/src/main/java/dan200/computercraft/shared/computer/core/ServerComputerRegistry.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.computer.core; import java.util.Iterator; @@ -35,18 +34,6 @@ public void update() } } - @Override - public void reset() - { - //System.out.println( "RESET SERVER COMPUTERS" ); - for( ServerComputer computer : getComputers() ) - { - computer.unload(); - } - super.reset(); - //System.out.println( getComputers().size() + " SERVER COMPUTERS" ); - } - @Override public void add( int instanceID, ServerComputer computer ) { @@ -70,19 +57,25 @@ public void remove( int instanceID ) //System.out.println( getComputers().size() + " SERVER COMPUTERS" ); } - public ServerComputer lookup( int computerID ) + @Override + public void reset() { - if( computerID < 0 ) + //System.out.println( "RESET SERVER COMPUTERS" ); + for( ServerComputer computer : getComputers() ) { - return null; + computer.unload(); } + super.reset(); + //System.out.println( getComputers().size() + " SERVER COMPUTERS" ); + } + + public ServerComputer lookup( int computerID ) + { + if( computerID < 0 ) return null; for( ServerComputer computer : getComputers() ) { - if( computer.getID() == computerID ) - { - return computer; - } + if( computer.getID() == computerID ) return computer; } return null; } 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 3579669e6..738f47e32 100644 --- a/src/main/java/dan200/computercraft/shared/computer/inventory/ContainerComputerBase.java +++ b/src/main/java/dan200/computercraft/shared/computer/inventory/ContainerComputerBase.java @@ -3,7 +3,6 @@ * 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.ComputerCraft; @@ -28,7 +27,10 @@ import javax.annotation.Nullable; import java.io.IOException; import java.nio.channels.WritableByteChannel; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.StringJoiner; +import java.util.UUID; import java.util.function.Function; import java.util.function.Predicate; @@ -44,40 +46,35 @@ public abstract class ContainerComputerBase extends AbstractContainerMenu implem private UUID toUploadId; private List toUpload; - public ContainerComputerBase( MenuType type, int id, Inventory player, ComputerContainerData data ) - { - this( type, - id, - x -> true, - getComputer( player, data ), - data.getFamily() ); - } - - public ContainerComputerBase( MenuType type, int id, Predicate canUse, IComputer computer, - ComputerFamily family ) + public ContainerComputerBase( MenuType type, int id, Predicate canUse, IComputer computer, ComputerFamily family ) { super( type, id ); this.canUse = canUse; - this.computer = Objects.requireNonNull( computer ); + this.computer = computer; this.family = family; } + public ContainerComputerBase( MenuType type, int id, Inventory player, ComputerContainerData data ) + { + this( type, id, x -> true, getComputer( player, data ), data.getFamily() ); + } + protected static IComputer getComputer( Inventory player, ComputerContainerData data ) { int id = data.getInstanceId(); - if( !player.player.level.isClientSide ) - { - return ComputerCraft.serverComputerRegistry.get( id ); - } + if( !player.player.level.isClientSide ) return ComputerCraft.serverComputerRegistry.get( id ); ClientComputer computer = ComputerCraft.clientComputerRegistry.get( id ); - if( computer == null ) - { - ComputerCraft.clientComputerRegistry.add( id, computer = new ClientComputer( id ) ); - } + if( computer == null ) ComputerCraft.clientComputerRegistry.add( id, computer = new ClientComputer( id ) ); return computer; } + @Override + public boolean stillValid( @Nonnull Player player ) + { + return canUse.test( player ); + } + @Nonnull public ComputerFamily getFamily() { @@ -98,19 +95,6 @@ public InputState getInput() return input; } - @Override - public void removed( @Nonnull Player player ) - { - super.removed( player ); - input.close(); - } - - @Override - public boolean stillValid( @Nonnull Player player ) - { - return canUse.test( player ); - } - @Override public void startUpload( @Nonnull UUID uuid, @Nonnull List files ) { @@ -213,7 +197,6 @@ private UploadResultMessage finishUpload( boolean forceOverwrite ) { try( FileSystemWrapper channel = fs.openForWrite( file.getName(), false, Function.identity() ) ) { - file.getBytes().rewind(); channel.get().write( file.getBytes() ); } } @@ -228,4 +211,11 @@ UploadResult.SUCCESS, new TranslatableComponent( "gui.computercraft.upload.succe return new UploadResultMessage( UploadResult.ERROR, new TranslatableComponent( "gui.computercraft.upload.failed.generic", e.getMessage() ) ); } } + + @Override + public void removed( @Nonnull Player player ) + { + super.removed( player ); + input.close(); + } } 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 3f7013dd2..395903160 100644 --- a/src/main/java/dan200/computercraft/shared/computer/inventory/ContainerViewComputer.java +++ b/src/main/java/dan200/computercraft/shared/computer/inventory/ContainerViewComputer.java @@ -3,11 +3,10 @@ * 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.ComputerCraft; -import dan200.computercraft.shared.ComputerCraftRegistry; +import dan200.computercraft.shared.Registry; import dan200.computercraft.shared.computer.blocks.TileCommandComputer; import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ServerComputer; @@ -24,13 +23,13 @@ public class ContainerViewComputer extends ComputerMenuWithoutInventory public ContainerViewComputer( int id, Inventory player, ServerComputer computer ) { - super( ComputerCraftRegistry.ModContainers.VIEW_COMPUTER, id, player, p -> canInteractWith( computer, p ), computer, computer.getFamily() ); + super( Registry.ModContainers.VIEW_COMPUTER, id, player, p -> canInteractWith( computer, p ), computer, computer.getFamily() ); width = height = 0; } public ContainerViewComputer( int id, Inventory player, ViewComputerContainerData data ) { - super( ComputerCraftRegistry.ModContainers.VIEW_COMPUTER, id, player, data ); + super( Registry.ModContainers.VIEW_COMPUTER, id, player, data ); width = data.getWidth(); height = data.getHeight(); } @@ -44,7 +43,12 @@ private static boolean canInteractWith( @Nonnull ServerComputer computer, @Nonnu } // If we're a command computer then ensure we're in creative - return computer.getFamily() != ComputerFamily.COMMAND || TileCommandComputer.isUsable( player ); + if( computer.getFamily() == ComputerFamily.COMMAND && !TileCommandComputer.isUsable( player ) ) + { + return false; + } + + return true; } public int getWidth() diff --git a/src/main/java/dan200/computercraft/shared/computer/items/ComputerItemFactory.java b/src/main/java/dan200/computercraft/shared/computer/items/ComputerItemFactory.java index 30dd6286d..03a91ab33 100644 --- a/src/main/java/dan200/computercraft/shared/computer/items/ComputerItemFactory.java +++ b/src/main/java/dan200/computercraft/shared/computer/items/ComputerItemFactory.java @@ -3,10 +3,9 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.computer.items; -import dan200.computercraft.shared.ComputerCraftRegistry; +import dan200.computercraft.shared.Registry; import dan200.computercraft.shared.computer.blocks.TileComputer; import dan200.computercraft.shared.computer.core.ComputerFamily; import net.minecraft.world.item.ItemStack; @@ -29,11 +28,11 @@ public static ItemStack create( int id, String label, ComputerFamily family ) switch( family ) { case NORMAL: - return ComputerCraftRegistry.ModItems.COMPUTER_NORMAL.create( id, label ); + return Registry.ModItems.COMPUTER_NORMAL.create( id, label ); case ADVANCED: - return ComputerCraftRegistry.ModItems.COMPUTER_ADVANCED.create( id, label ); + return Registry.ModItems.COMPUTER_ADVANCED.create( id, label ); case COMMAND: - return ComputerCraftRegistry.ModItems.COMPUTER_COMMAND.create( id, label ); + return Registry.ModItems.COMPUTER_COMMAND.create( id, label ); default: return ItemStack.EMPTY; } 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 06a0e46ae..d9937d572 100644 --- a/src/main/java/dan200/computercraft/shared/computer/items/IComputerItem.java +++ b/src/main/java/dan200/computercraft/shared/computer/items/IComputerItem.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.computer.items; import dan200.computercraft.shared.computer.core.ComputerFamily; @@ -24,8 +23,7 @@ default int getComputerID( @Nonnull ItemStack stack ) default String getLabel( @Nonnull ItemStack stack ) { - return stack.hasCustomHoverName() ? stack.getHoverName() - .getString() : null; + return stack.hasCustomHoverName() ? stack.getHoverName().getString() : null; } ComputerFamily getFamily(); diff --git a/src/main/java/dan200/computercraft/shared/computer/items/ItemComputer.java b/src/main/java/dan200/computercraft/shared/computer/items/ItemComputer.java index 26b6c26f9..c3b0fbffb 100644 --- a/src/main/java/dan200/computercraft/shared/computer/items/ItemComputer.java +++ b/src/main/java/dan200/computercraft/shared/computer/items/ItemComputer.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.computer.items; import dan200.computercraft.shared.computer.blocks.BlockComputer; @@ -15,7 +14,7 @@ public class ItemComputer extends ItemComputerBase { - public ItemComputer( BlockComputer block, Properties settings ) + public ItemComputer( BlockComputer block, Properties settings ) { super( block, settings ); } @@ -23,15 +22,8 @@ public ItemComputer( BlockComputer block, Properties settings ) public ItemStack create( int id, String label ) { ItemStack result = new ItemStack( this ); - if( id >= 0 ) - { - result.getOrCreateTag() - .putInt( NBT_ID, id ); - } - if( label != null ) - { - result.setHoverName( new TextComponent( label ) ); - } + if( id >= 0 ) result.getOrCreateTag().putInt( NBT_ID, id ); + if( label != null ) result.setHoverName( new TextComponent( label ) ); return result; } @@ -39,10 +31,7 @@ public ItemStack create( int id, String label ) public ItemStack withFamily( @Nonnull ItemStack stack, @Nonnull ComputerFamily family ) { ItemStack result = ComputerItemFactory.create( getComputerID( stack ), null, family ); - if( stack.hasCustomHoverName() ) - { - result.setHoverName( stack.getHoverName() ); - } + if( stack.hasCustomHoverName() ) result.setHoverName( stack.getHoverName() ); return result; } } diff --git a/src/main/java/dan200/computercraft/shared/computer/items/ItemComputerBase.java b/src/main/java/dan200/computercraft/shared/computer/items/ItemComputerBase.java index 93bd3bc03..c95eb2af9 100644 --- a/src/main/java/dan200/computercraft/shared/computer/items/ItemComputerBase.java +++ b/src/main/java/dan200/computercraft/shared/computer/items/ItemComputerBase.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.computer.items; import dan200.computercraft.ComputerCraft; @@ -43,7 +42,8 @@ public void appendHoverText( @Nonnull ItemStack stack, @Nullable Level world, @N int id = getComputerID( stack ); if( id >= 0 ) { - list.add( new TranslatableComponent( "gui.computercraft.tooltip.computer_id", id ).withStyle( ChatFormatting.GRAY ) ); + list.add( new TranslatableComponent( "gui.computercraft.tooltip.computer_id", id ) + .withStyle( ChatFormatting.GRAY ) ); } } } diff --git a/src/main/java/dan200/computercraft/shared/computer/recipe/ComputerConvertRecipe.java b/src/main/java/dan200/computercraft/shared/computer/recipe/ComputerConvertRecipe.java index 1ac0f93e1..0363815e2 100644 --- a/src/main/java/dan200/computercraft/shared/computer/recipe/ComputerConvertRecipe.java +++ b/src/main/java/dan200/computercraft/shared/computer/recipe/ComputerConvertRecipe.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.computer.recipe; import dan200.computercraft.shared.computer.items.IComputerItem; @@ -31,27 +30,16 @@ public ComputerConvertRecipe( ResourceLocation identifier, String group, int wid } @Nonnull - @Override - public String getGroup() - { - return group; - } + protected abstract ItemStack convert( @Nonnull IComputerItem item, @Nonnull ItemStack stack ); @Override public boolean matches( @Nonnull CraftingContainer inventory, @Nonnull Level world ) { - if( !super.matches( inventory, world ) ) - { - return false; - } + if( !super.matches( inventory, world ) ) return false; for( int i = 0; i < inventory.getContainerSize(); i++ ) { - if( inventory.getItem( i ) - .getItem() instanceof IComputerItem ) - { - return true; - } + if( inventory.getItem( i ).getItem() instanceof IComputerItem ) return true; } return false; @@ -65,15 +53,16 @@ public ItemStack assemble( @Nonnull CraftingContainer inventory ) for( int i = 0; i < inventory.getContainerSize(); i++ ) { ItemStack stack = inventory.getItem( i ); - if( stack.getItem() instanceof IComputerItem ) - { - return convert( (IComputerItem) stack.getItem(), stack ); - } + if( stack.getItem() instanceof IComputerItem ) return convert( (IComputerItem) stack.getItem(), stack ); } return ItemStack.EMPTY; } @Nonnull - protected abstract ItemStack convert( @Nonnull IComputerItem item, @Nonnull ItemStack stack ); + @Override + public String getGroup() + { + return group; + } } 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 148d5967a..76ca89ba7 100644 --- a/src/main/java/dan200/computercraft/shared/computer/recipe/ComputerFamilyRecipe.java +++ b/src/main/java/dan200/computercraft/shared/computer/recipe/ComputerFamilyRecipe.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.computer.recipe; import com.google.gson.JsonObject; @@ -23,8 +22,7 @@ public abstract class ComputerFamilyRecipe extends ComputerConvertRecipe { private final ComputerFamily family; - public ComputerFamilyRecipe( ResourceLocation identifier, String group, int width, int height, NonNullList ingredients, ItemStack result, - ComputerFamily family ) + public ComputerFamilyRecipe( ResourceLocation identifier, String group, int width, int height, NonNullList ingredients, ItemStack result, ComputerFamily family ) { super( identifier, group, width, height, ingredients, result ); this.family = family; @@ -37,6 +35,8 @@ public ComputerFamily getFamily() public abstract static class Serializer implements RecipeSerializer { + protected abstract T create( ResourceLocation identifier, String group, int width, int height, NonNullList ingredients, ItemStack result, ComputerFamily family ); + @Nonnull @Override public T fromJson( @Nonnull ResourceLocation identifier, @Nonnull JsonObject json ) @@ -50,9 +50,6 @@ public T fromJson( @Nonnull ResourceLocation identifier, @Nonnull JsonObject jso return create( identifier, group, template.width, template.height, template.ingredients, result, family ); } - protected abstract T create( ResourceLocation identifier, String group, int width, int height, NonNullList ingredients, ItemStack result, - ComputerFamily family ); - @Nonnull @Override public T fromNetwork( @Nonnull ResourceLocation identifier, @Nonnull FriendlyByteBuf buf ) @@ -62,10 +59,7 @@ public T fromNetwork( @Nonnull ResourceLocation identifier, @Nonnull FriendlyByt String group = buf.readUtf( Short.MAX_VALUE ); NonNullList ingredients = NonNullList.withSize( width * height, Ingredient.EMPTY ); - for( int i = 0; i < ingredients.size(); i++ ) - { - ingredients.set( i, Ingredient.fromNetwork( buf ) ); - } + for( int i = 0; i < ingredients.size(); i++ ) ingredients.set( i, Ingredient.fromNetwork( buf ) ); ItemStack result = buf.readItem(); ComputerFamily family = buf.readEnum( ComputerFamily.class ); @@ -78,10 +72,7 @@ public void toNetwork( @Nonnull FriendlyByteBuf buf, @Nonnull T recipe ) buf.writeVarInt( recipe.getWidth() ); buf.writeVarInt( recipe.getHeight() ); buf.writeUtf( recipe.getGroup() ); - for( Ingredient ingredient : recipe.getIngredients() ) - { - ingredient.toNetwork( buf ); - } + for( Ingredient ingredient : recipe.getIngredients() ) ingredient.toNetwork( buf ); buf.writeItem( recipe.getResultItem() ); buf.writeEnum( recipe.getFamily() ); } diff --git a/src/main/java/dan200/computercraft/shared/computer/recipe/ComputerUpgradeRecipe.java b/src/main/java/dan200/computercraft/shared/computer/recipe/ComputerUpgradeRecipe.java index 7f10134eb..b0705effa 100644 --- a/src/main/java/dan200/computercraft/shared/computer/recipe/ComputerUpgradeRecipe.java +++ b/src/main/java/dan200/computercraft/shared/computer/recipe/ComputerUpgradeRecipe.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.computer.recipe; import dan200.computercraft.shared.computer.core.ComputerFamily; @@ -18,19 +17,7 @@ public class ComputerUpgradeRecipe extends ComputerFamilyRecipe { - public static final RecipeSerializer SERIALIZER = - new ComputerFamilyRecipe.Serializer() - { - @Override - protected ComputerUpgradeRecipe create( ResourceLocation identifier, String group, int width, int height, NonNullList ingredients, - ItemStack result, ComputerFamily family ) - { - return new ComputerUpgradeRecipe( identifier, group, width, height, ingredients, result, family ); - } - }; - - public ComputerUpgradeRecipe( ResourceLocation identifier, String group, int width, int height, NonNullList ingredients, ItemStack result, - ComputerFamily family ) + public ComputerUpgradeRecipe( ResourceLocation identifier, String group, int width, int height, NonNullList ingredients, ItemStack result, ComputerFamily family ) { super( identifier, group, width, height, ingredients, result, family ); } @@ -48,4 +35,13 @@ public RecipeSerializer getSerializer() { return SERIALIZER; } + + public static final RecipeSerializer SERIALIZER = new Serializer<>() + { + @Override + protected ComputerUpgradeRecipe create( ResourceLocation identifier, String group, int width, int height, NonNullList ingredients, ItemStack result, ComputerFamily family ) + { + return new ComputerUpgradeRecipe( identifier, group, width, height, ingredients, result, family ); + } + }; } diff --git a/src/main/java/dan200/computercraft/shared/data/BlockNamedEntityLootCondition.java b/src/main/java/dan200/computercraft/shared/data/BlockNamedEntityLootCondition.java index e25f4f891..be39084f6 100644 --- a/src/main/java/dan200/computercraft/shared/data/BlockNamedEntityLootCondition.java +++ b/src/main/java/dan200/computercraft/shared/data/BlockNamedEntityLootCondition.java @@ -34,7 +34,7 @@ private BlockNamedEntityLootCondition() public boolean test( LootContext lootContext ) { BlockEntity tile = lootContext.getParamOrNull( LootContextParams.BLOCK_ENTITY ); - return tile instanceof Nameable && ((Nameable) tile).hasCustomName(); + return tile instanceof Nameable nameable && nameable.hasCustomName(); } @Nonnull diff --git a/src/main/java/dan200/computercraft/shared/data/HasComputerIdLootCondition.java b/src/main/java/dan200/computercraft/shared/data/HasComputerIdLootCondition.java index 4127ec6ef..c93f075c8 100644 --- a/src/main/java/dan200/computercraft/shared/data/HasComputerIdLootCondition.java +++ b/src/main/java/dan200/computercraft/shared/data/HasComputerIdLootCondition.java @@ -34,7 +34,7 @@ private HasComputerIdLootCondition() public boolean test( LootContext lootContext ) { BlockEntity tile = lootContext.getParamOrNull( LootContextParams.BLOCK_ENTITY ); - return tile instanceof IComputerTile && ((IComputerTile) tile).getComputerID() >= 0; + return tile instanceof IComputerTile computer && computer.getComputerID() >= 0; } @Nonnull diff --git a/src/main/java/dan200/computercraft/shared/data/PlayerCreativeLootCondition.java b/src/main/java/dan200/computercraft/shared/data/PlayerCreativeLootCondition.java index 3666ffbb6..465280d18 100644 --- a/src/main/java/dan200/computercraft/shared/data/PlayerCreativeLootCondition.java +++ b/src/main/java/dan200/computercraft/shared/data/PlayerCreativeLootCondition.java @@ -34,7 +34,7 @@ private PlayerCreativeLootCondition() public boolean test( LootContext lootContext ) { Entity entity = lootContext.getParamOrNull( LootContextParams.THIS_ENTITY ); - return entity instanceof Player && ((Player) entity).getAbilities().instabuild; + return entity instanceof Player player && player.getAbilities().instabuild; } @Nonnull diff --git a/src/main/java/dan200/computercraft/shared/media/items/ItemDisk.java b/src/main/java/dan200/computercraft/shared/media/items/ItemDisk.java index f7b5731b9..3f70e1b06 100644 --- a/src/main/java/dan200/computercraft/shared/media/items/ItemDisk.java +++ b/src/main/java/dan200/computercraft/shared/media/items/ItemDisk.java @@ -3,14 +3,13 @@ * 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.ComputerCraft; import dan200.computercraft.api.ComputerCraftAPI; import dan200.computercraft.api.filesystem.IMount; import dan200.computercraft.api.media.IMedia; -import dan200.computercraft.shared.ComputerCraftRegistry; +import dan200.computercraft.shared.Registry; import dan200.computercraft.shared.common.IColouredItem; import dan200.computercraft.shared.util.Colour; import net.minecraft.ChatFormatting; @@ -38,62 +37,44 @@ public ItemDisk( Properties settings ) super( settings ); } - @Override - public void appendHoverText( @Nonnull ItemStack stack, @Nullable Level world, @Nonnull List list, TooltipFlag options ) + @Nonnull + public static ItemStack createFromIDAndColour( int id, String label, int colour ) { - if( options.isAdvanced() ) - { - int id = getDiskID( stack ); - if( id >= 0 ) - { - list.add( new TranslatableComponent( "gui.computercraft.tooltip.disk_id", id ).withStyle( ChatFormatting.GRAY ) ); - } - } + ItemStack stack = new ItemStack( Registry.ModItems.DISK ); + setDiskID( stack, id ); + Registry.ModItems.DISK.setLabel( stack, label ); + IColouredItem.setColourBasic( stack, colour ); + return stack; } @Override public void fillItemCategory( @Nonnull CreativeModeTab tabs, @Nonnull NonNullList list ) { - if( !allowdedIn( tabs ) ) - { - return; - } + if( !allowdedIn( tabs ) ) return; for( int colour = 0; colour < 16; colour++ ) { list.add( createFromIDAndColour( -1, null, Colour.VALUES[colour].getHex() ) ); } } - @Nonnull - public static ItemStack createFromIDAndColour( int id, String label, int colour ) - { - ItemStack stack = new ItemStack( ComputerCraftRegistry.ModItems.DISK ); - setDiskID( stack, id ); - ComputerCraftRegistry.ModItems.DISK.setLabel( stack, label ); - IColouredItem.setColourBasic( stack, colour ); - return stack; - } - - private static void setDiskID( @Nonnull ItemStack stack, int id ) + @Override + public void appendHoverText( @Nonnull ItemStack stack, @Nullable Level world, @Nonnull List list, TooltipFlag options ) { - if( id >= 0 ) + if( options.isAdvanced() ) { - stack.getOrCreateTag() - .putInt( NBT_ID, id ); + int id = getDiskID( stack ); + if( id >= 0 ) + { + list.add( new TranslatableComponent( "gui.computercraft.tooltip.disk_id", id ) + .withStyle( ChatFormatting.GRAY ) ); + } } } - public static int getDiskID( @Nonnull ItemStack stack ) - { - CompoundTag nbt = stack.getTag(); - return nbt != null && nbt.contains( NBT_ID ) ? nbt.getInt( NBT_ID ) : -1; - } - @Override public String getLabel( @Nonnull ItemStack stack ) { - return stack.hasCustomHoverName() ? stack.getHoverName() - .getString() : null; + return stack.hasCustomHoverName() ? stack.getHoverName().getString() : null; } @Override @@ -122,6 +103,17 @@ public IMount createDataMount( @Nonnull ItemStack stack, @Nonnull Level world ) return ComputerCraftAPI.createSaveDirMount( world, "disk/" + diskID, ComputerCraft.floppySpaceLimit ); } + public static int getDiskID( @Nonnull ItemStack stack ) + { + CompoundTag nbt = stack.getTag(); + return nbt != null && nbt.contains( NBT_ID ) ? nbt.getInt( NBT_ID ) : -1; + } + + private static void setDiskID( @Nonnull ItemStack stack, int id ) + { + if( id >= 0 ) stack.getOrCreateTag().putInt( NBT_ID, id ); + } + @Override public int getColour( @Nonnull ItemStack stack ) { diff --git a/src/main/java/dan200/computercraft/shared/media/items/ItemPrintout.java b/src/main/java/dan200/computercraft/shared/media/items/ItemPrintout.java index b27525b76..9052b9973 100644 --- a/src/main/java/dan200/computercraft/shared/media/items/ItemPrintout.java +++ b/src/main/java/dan200/computercraft/shared/media/items/ItemPrintout.java @@ -3,10 +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.shared.ComputerCraftRegistry; +import dan200.computercraft.shared.Registry; import dan200.computercraft.shared.common.ContainerHeldItem; import dan200.computercraft.shared.network.container.HeldItemContainerData; import net.minecraft.nbt.CompoundTag; @@ -26,13 +25,22 @@ public class ItemPrintout extends Item { - public static final int LINES_PER_PAGE = 21; - public static final int LINE_MAX_LENGTH = 25; - public static final int MAX_PAGES = 16; private static final String NBT_TITLE = "Title"; private static final String NBT_PAGES = "Pages"; private static final String NBT_LINE_TEXT = "Text"; private static final String NBT_LINE_COLOUR = "Color"; + + public static final int LINES_PER_PAGE = 21; + public static final int LINE_MAX_LENGTH = 25; + public static final int MAX_PAGES = 16; + + public enum Type + { + PAGE, + PAGES, + BOOK + } + private final Type type; public ItemPrintout( Properties settings, Type type ) @@ -41,10 +49,23 @@ public ItemPrintout( Properties settings, Type type ) this.type = type; } + @Override + public void appendHoverText( @Nonnull ItemStack stack, Level world, @Nonnull List list, @Nonnull TooltipFlag options ) + { + String title = getTitle( stack ); + if( title != null && !title.isEmpty() ) list.add( new TextComponent( title ) ); + } + @Nonnull - public static ItemStack createSingleFromTitleAndText( String title, String[] text, String[] colours ) + @Override + public InteractionResultHolder use( Level world, @Nonnull Player player, @Nonnull InteractionHand hand ) { - return ComputerCraftRegistry.ModItems.PRINTED_PAGE.createFromTitleAndText( title, text, colours ); + if( !world.isClientSide ) + { + new HeldItemContainerData( hand ) + .open( player, new ContainerHeldItem.Factory( Registry.ModContainers.PRINTOUT, player.getItemInHand( hand ), hand ) ); + } + return new InteractionResultHolder<>( InteractionResult.SUCCESS, player.getItemInHand( hand ) ); } @Nonnull @@ -53,21 +74,14 @@ private ItemStack createFromTitleAndText( String title, String[] text, String[] ItemStack stack = new ItemStack( this ); // Build NBT - if( title != null ) - { - stack.getOrCreateTag() - .putString( NBT_TITLE, title ); - } + if( title != null ) stack.getOrCreateTag().putString( NBT_TITLE, title ); if( text != null ) { CompoundTag tag = stack.getOrCreateTag(); tag.putInt( NBT_PAGES, text.length / LINES_PER_PAGE ); for( int i = 0; i < text.length; i++ ) { - if( text[i] != null ) - { - tag.putString( NBT_LINE_TEXT + i, text[i] ); - } + if( text[i] != null ) tag.putString( NBT_LINE_TEXT + i, text[i] ); } } if( colours != null ) @@ -75,10 +89,7 @@ private ItemStack createFromTitleAndText( String title, String[] text, String[] CompoundTag tag = stack.getOrCreateTag(); for( int i = 0; i < colours.length; i++ ) { - if( colours[i] != null ) - { - tag.putString( NBT_LINE_COLOUR + i, colours[i] ); - } + if( colours[i] != null ) tag.putString( NBT_LINE_COLOUR + i, colours[i] ); } } @@ -86,33 +97,33 @@ private ItemStack createFromTitleAndText( String title, String[] text, String[] return stack; } + @Nonnull + public static ItemStack createSingleFromTitleAndText( String title, String[] text, String[] colours ) + { + return Registry.ModItems.PRINTED_PAGE.createFromTitleAndText( title, text, colours ); + } + @Nonnull public static ItemStack createMultipleFromTitleAndText( String title, String[] text, String[] colours ) { - return ComputerCraftRegistry.ModItems.PRINTED_PAGES.createFromTitleAndText( title, text, colours ); + return Registry.ModItems.PRINTED_PAGES.createFromTitleAndText( title, text, colours ); } @Nonnull public static ItemStack createBookFromTitleAndText( String title, String[] text, String[] colours ) { - return ComputerCraftRegistry.ModItems.PRINTED_BOOK.createFromTitleAndText( title, text, colours ); + return Registry.ModItems.PRINTED_BOOK.createFromTitleAndText( title, text, colours ); } - public static String[] getText( @Nonnull ItemStack stack ) + public Type getType() { - return getLines( stack, NBT_LINE_TEXT ); + return type; } - private static String[] getLines( @Nonnull ItemStack stack, String prefix ) + public static String getTitle( @Nonnull ItemStack stack ) { CompoundTag nbt = stack.getTag(); - int numLines = getPageCount( stack ) * LINES_PER_PAGE; - String[] lines = new String[numLines]; - for( int i = 0; i < lines.length; i++ ) - { - lines[i] = nbt != null ? nbt.getString( prefix + i ) : ""; - } - return lines; + return nbt != null && nbt.contains( NBT_TITLE ) ? nbt.getString( NBT_TITLE ) : null; } public static int getPageCount( @Nonnull ItemStack stack ) @@ -121,48 +132,25 @@ public static int getPageCount( @Nonnull ItemStack stack ) return nbt != null && nbt.contains( NBT_PAGES ) ? nbt.getInt( NBT_PAGES ) : 1; } - public static String[] getColours( @Nonnull ItemStack stack ) + public static String[] getText( @Nonnull ItemStack stack ) { - return getLines( stack, NBT_LINE_COLOUR ); + return getLines( stack, NBT_LINE_TEXT ); } - @Nonnull - @Override - public InteractionResultHolder use( Level world, @Nonnull Player player, @Nonnull InteractionHand hand ) + public static String[] getColours( @Nonnull ItemStack stack ) { - if( !world.isClientSide ) - { - new HeldItemContainerData( hand ).open( player, - new ContainerHeldItem.Factory( ComputerCraftRegistry.ModContainers.PRINTOUT, - player.getItemInHand( hand ), - hand ) ); - } - return new InteractionResultHolder<>( InteractionResult.SUCCESS, player.getItemInHand( hand ) ); + return getLines( stack, NBT_LINE_COLOUR ); } - @Override - public void appendHoverText( @Nonnull ItemStack stack, Level world, @Nonnull List list, @Nonnull TooltipFlag options ) + private static String[] getLines( @Nonnull ItemStack stack, String prefix ) { - String title = getTitle( stack ); - if( title != null && !title.isEmpty() ) + CompoundTag nbt = stack.getTag(); + int numLines = getPageCount( stack ) * LINES_PER_PAGE; + String[] lines = new String[numLines]; + for( int i = 0; i < lines.length; i++ ) { - list.add( new TextComponent( title ) ); + lines[i] = nbt != null ? nbt.getString( prefix + i ) : ""; } - } - - public static String getTitle( @Nonnull ItemStack stack ) - { - CompoundTag nbt = stack.getTag(); - return nbt != null && nbt.contains( NBT_TITLE ) ? nbt.getString( NBT_TITLE ) : null; - } - - public Type getType() - { - return type; - } - - public enum Type - { - PAGE, PAGES, BOOK + return lines; } } diff --git a/src/main/java/dan200/computercraft/shared/media/items/ItemTreasureDisk.java b/src/main/java/dan200/computercraft/shared/media/items/ItemTreasureDisk.java index 5c0fbfceb..44be365c6 100644 --- a/src/main/java/dan200/computercraft/shared/media/items/ItemTreasureDisk.java +++ b/src/main/java/dan200/computercraft/shared/media/items/ItemTreasureDisk.java @@ -3,14 +3,13 @@ * 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.ComputerCraftAPI; import dan200.computercraft.api.filesystem.IMount; import dan200.computercraft.api.media.IMedia; import dan200.computercraft.core.filesystem.SubMount; -import dan200.computercraft.shared.ComputerCraftRegistry; +import dan200.computercraft.shared.Registry; import dan200.computercraft.shared.util.Colour; import net.minecraft.core.NonNullList; import net.minecraft.nbt.CompoundTag; @@ -38,54 +37,16 @@ public ItemTreasureDisk( Properties settings ) super( settings ); } - public static ItemStack create( String subPath, int colourIndex ) - { - ItemStack result = new ItemStack( ComputerCraftRegistry.ModItems.TREASURE_DISK ); - CompoundTag nbt = result.getOrCreateTag(); - nbt.putString( NBT_SUB_PATH, subPath ); - - int slash = subPath.indexOf( '/' ); - if( slash >= 0 ) - { - String author = subPath.substring( 0, slash ); - String title = subPath.substring( slash + 1 ); - nbt.putString( NBT_TITLE, "\"" + title + "\" by " + author ); - } - else - { - nbt.putString( NBT_TITLE, "untitled" ); - } - nbt.putInt( NBT_COLOUR, Colour.values()[colourIndex].getHex() ); - - return result; - } - - public static int getColour( @Nonnull ItemStack stack ) + @Override + public void fillItemCategory( @Nonnull CreativeModeTab group, @Nonnull NonNullList stacks ) { - CompoundTag nbt = stack.getTag(); - return nbt != null && nbt.contains( NBT_COLOUR ) ? nbt.getInt( NBT_COLOUR ) : Colour.BLUE.getHex(); } @Override public void appendHoverText( @Nonnull ItemStack stack, @Nullable Level world, @Nonnull List list, @Nonnull TooltipFlag tooltipOptions ) { String label = getTitle( stack ); - if( !label.isEmpty() ) - { - list.add( new TextComponent( label ) ); - } - } - - @Override - public void fillItemCategory( @Nonnull CreativeModeTab group, @Nonnull NonNullList stacks ) - { - } - - @Nonnull - private static String getTitle( @Nonnull ItemStack stack ) - { - CompoundTag nbt = stack.getTag(); - return nbt != null && nbt.contains( NBT_TITLE ) ? nbt.getString( NBT_TITLE ) : "'alongtimeago' by dan200"; + if( !label.isEmpty() ) list.add( new TextComponent( label ) ); } @Override @@ -98,6 +59,8 @@ public String getLabel( @Nonnull ItemStack stack ) public IMount createDataMount( @Nonnull ItemStack stack, @Nonnull Level world ) { IMount rootTreasure = getTreasureMount(); + if( rootTreasure == null ) return null; + String subPath = getSubPath( stack ); try { @@ -120,15 +83,50 @@ else if( rootTreasure.exists( "deprecated/" + subPath ) ) } } + public static ItemStack create( String subPath, int colourIndex ) + { + ItemStack result = new ItemStack( Registry.ModItems.TREASURE_DISK ); + CompoundTag nbt = result.getOrCreateTag(); + nbt.putString( NBT_SUB_PATH, subPath ); + + int slash = subPath.indexOf( '/' ); + if( slash >= 0 ) + { + String author = subPath.substring( 0, slash ); + String title = subPath.substring( slash + 1 ); + nbt.putString( NBT_TITLE, "\"" + title + "\" by " + author ); + } + else + { + nbt.putString( NBT_TITLE, "untitled" ); + } + nbt.putInt( NBT_COLOUR, Colour.values()[colourIndex].getHex() ); + + return result; + } + private static IMount getTreasureMount() { return ComputerCraftAPI.createResourceMount( "computercraft", "lua/treasure" ); } + @Nonnull + private static String getTitle( @Nonnull ItemStack stack ) + { + CompoundTag nbt = stack.getTag(); + return nbt != null && nbt.contains( NBT_TITLE ) ? nbt.getString( NBT_TITLE ) : "'missingno' by how did you get this anyway?"; + } + @Nonnull private static String getSubPath( @Nonnull ItemStack stack ) { CompoundTag nbt = stack.getTag(); return nbt != null && nbt.contains( NBT_SUB_PATH ) ? nbt.getString( NBT_SUB_PATH ) : "dan200/alongtimeago"; } + + public static int getColour( @Nonnull ItemStack stack ) + { + CompoundTag nbt = stack.getTag(); + return nbt != null && nbt.contains( NBT_COLOUR ) ? nbt.getInt( NBT_COLOUR ) : Colour.BLUE.getHex(); + } } 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 ff63c44a1..7b1384178 100644 --- a/src/main/java/dan200/computercraft/shared/media/items/RecordMedia.java +++ b/src/main/java/dan200/computercraft/shared/media/items/RecordMedia.java @@ -35,10 +35,7 @@ public String getLabel( @Nonnull ItemStack stack ) public String getAudioTitle( @Nonnull ItemStack stack ) { Item item = stack.getItem(); - if( !(item instanceof RecordItem) ) - { - return null; - } + if( !(item instanceof RecordItem) ) return null; return new TranslatableComponent( item.getDescriptionId() + ".desc" ).getString(); } @@ -47,10 +44,8 @@ public String getAudioTitle( @Nonnull ItemStack stack ) public SoundEvent getAudio( @Nonnull ItemStack stack ) { Item item = stack.getItem(); - if( !(item instanceof RecordItem) ) - { - return null; - } + if( !(item instanceof RecordItem) ) return null; + return ((RecordItem) item).getSound(); } } diff --git a/src/main/java/dan200/computercraft/shared/media/recipes/DiskRecipe.java b/src/main/java/dan200/computercraft/shared/media/recipes/DiskRecipe.java index d2f479581..7c6db581d 100644 --- a/src/main/java/dan200/computercraft/shared/media/recipes/DiskRecipe.java +++ b/src/main/java/dan200/computercraft/shared/media/recipes/DiskRecipe.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.media.recipes; import dan200.computercraft.shared.media.items.ItemDisk; @@ -25,9 +24,7 @@ public class DiskRecipe extends CustomRecipe { - public static final RecipeSerializer SERIALIZER = new SimpleRecipeSerializer<>( DiskRecipe::new ); private final Ingredient paper = Ingredient.of( Items.PAPER ); - // TODO: Ingredient.fromTag( Tags.Items.DUSTS_REDSTONE ); private final Ingredient redstone = Ingredient.of( Items.REDSTONE ); public DiskRecipe( ResourceLocation id ) @@ -49,18 +46,12 @@ public boolean matches( @Nonnull CraftingContainer inv, @Nonnull Level world ) { if( paper.test( stack ) ) { - if( paperFound ) - { - return false; - } + if( paperFound ) return false; paperFound = true; } else if( redstone.test( stack ) ) { - if( redstoneFound ) - { - return false; - } + if( redstoneFound ) return false; redstoneFound = true; } else if( ColourUtils.getStackColour( stack ) == null ) @@ -83,10 +74,7 @@ public ItemStack assemble( @Nonnull CraftingContainer inv ) { ItemStack stack = inv.getItem( i ); - if( stack.isEmpty() ) - { - continue; - } + if( stack.isEmpty() ) continue; if( !paper.test( stack ) && !redstone.test( stack ) ) { @@ -106,15 +94,17 @@ public boolean canCraftInDimensions( int x, int y ) @Nonnull @Override - public RecipeSerializer getSerializer() + public ItemStack getResultItem() { - return SERIALIZER; + return ItemDisk.createFromIDAndColour( -1, null, Colour.BLUE.getHex() ); } @Nonnull @Override - public ItemStack getResultItem() + public RecipeSerializer getSerializer() { - return ItemDisk.createFromIDAndColour( -1, null, Colour.BLUE.getHex() ); + return SERIALIZER; } + + public static final SimpleRecipeSerializer SERIALIZER = new SimpleRecipeSerializer<>( DiskRecipe::new ); } diff --git a/src/main/java/dan200/computercraft/shared/media/recipes/PrintoutRecipe.java b/src/main/java/dan200/computercraft/shared/media/recipes/PrintoutRecipe.java index 5c9b48cde..e332d2970 100644 --- a/src/main/java/dan200/computercraft/shared/media/recipes/PrintoutRecipe.java +++ b/src/main/java/dan200/computercraft/shared/media/recipes/PrintoutRecipe.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.media.recipes; import dan200.computercraft.shared.media.items.ItemPrintout; @@ -21,7 +20,6 @@ public final class PrintoutRecipe extends CustomRecipe { - public static final RecipeSerializer SERIALIZER = new SimpleRecipeSerializer<>( PrintoutRecipe::new ); private final Ingredient paper = Ingredient.of( Items.PAPER ); private final Ingredient leather = Ingredient.of( Items.LEATHER ); private final Ingredient string = Ingredient.of( Items.STRING ); @@ -31,6 +29,12 @@ private PrintoutRecipe( ResourceLocation id ) super( id ); } + @Override + public boolean canCraftInDimensions( int x, int y ) + { + return x >= 3 && y >= 3; + } + @Nonnull @Override public ItemStack getResultItem() @@ -62,12 +66,9 @@ public ItemStack assemble( @Nonnull CraftingContainer inventory ) ItemStack stack = inventory.getItem( x + y * inventory.getWidth() ); if( !stack.isEmpty() ) { - if( stack.getItem() instanceof ItemPrintout && ((ItemPrintout) stack.getItem()).getType() != ItemPrintout.Type.BOOK ) + if( stack.getItem() instanceof ItemPrintout printout && printout.getType() != ItemPrintout.Type.BOOK ) { - if( printouts == null ) - { - printouts = new ItemStack[9]; - } + if( printouts == null ) printouts = new ItemStack[9]; printouts[numPrintouts] = stack; numPages += ItemPrintout.getPageCount( stack ); numPrintouts++; @@ -152,16 +153,12 @@ else if( leather.test( stack ) && !leatherFound ) return ItemStack.EMPTY; } - @Override - public boolean canCraftInDimensions( int x, int y ) - { - return x >= 3 && y >= 3; - } - @Nonnull @Override public RecipeSerializer getSerializer() { return SERIALIZER; } + + public static final SimpleRecipeSerializer SERIALIZER = new SimpleRecipeSerializer<>( PrintoutRecipe::new ); } diff --git a/src/main/java/dan200/computercraft/shared/network/NetworkHandler.java b/src/main/java/dan200/computercraft/shared/network/NetworkHandler.java index 7d82ef22d..cde68c6e0 100644 --- a/src/main/java/dan200/computercraft/shared/network/NetworkHandler.java +++ b/src/main/java/dan200/computercraft/shared/network/NetworkHandler.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.network; import dan200.computercraft.ComputerCraft; @@ -16,7 +15,6 @@ 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; import net.fabricmc.fabric.api.network.PacketContext; import net.fabricmc.fabric.api.network.ServerSidePacketRegistry; @@ -86,24 +84,6 @@ private static void receive( PacketContext context, FriendlyByteBuf buffer ) .accept( context, buffer ); } - /** - * /** Register packet, and a thread-unsafe handler for it. - * - * @param The type of the packet to send. - * @param type The class of the type of packet to send. - * @param id The identifier for this packet type - * @param decoder The factory for this type of packet. - */ - private static void registerMainThread( int id, Class type, Function decoder ) - { - packetIds.put( type, id ); - packetReaders.put( id, ( context, buf ) -> { - T result = decoder.apply( buf ); - context.getTaskQueue() - .execute( () -> result.handle( context ) ); - } ); - } - @SuppressWarnings( "unchecked" ) private static Class getType( Supplier supplier ) { @@ -127,17 +107,9 @@ public static void sendToPlayer( Player player, NetworkMessage packet ) public static void sendToAllPlayers( NetworkMessage packet ) { MinecraftServer server = GameInstanceUtils.getServer(); - server.getPlayerList() - .broadcastAll( new ClientboundCustomPayloadPacket( ID, encode( packet ) ) ); - } - - public static void sendToAllPlayers( MinecraftServer server, NetworkMessage packet ) - { - server.getPlayerList() - .broadcastAll( new ClientboundCustomPayloadPacket( ID, encode( packet ) ) ); + server.getPlayerList().broadcastAll( new ClientboundCustomPayloadPacket( ID, encode( packet ) ) ); } - @Environment( EnvType.CLIENT ) public static void sendToServer( NetworkMessage packet ) { Minecraft.getInstance().player.connection.send( new ServerboundCustomPayloadPacket( ID, encode( packet ) ) ); @@ -145,9 +117,7 @@ public static void sendToServer( NetworkMessage packet ) public static void sendToAllAround( NetworkMessage packet, Level world, Vec3 pos, double range ) { - world.getServer() - .getPlayerList() - .broadcast( null, pos.x, pos.y, pos.z, range, world.dimension(), new ClientboundCustomPayloadPacket( ID, encode( packet ) ) ); + world.getServer().getPlayerList().broadcast( null, pos.x, pos.y, pos.z, range, world.dimension(), new ClientboundCustomPayloadPacket( ID, encode( packet ) ) ); } public static void sendToAllTracking( NetworkMessage packet, LevelChunk chunk ) @@ -160,4 +130,21 @@ public static void sendToAllTracking( NetworkMessage packet, LevelChunk chunk ) } } } + + /** + * Register packet, and a thread-unsafe handler for it. + * + * @param The type of the packet to send. + * @param type The class of the type of packet to send. + * @param id The identifier for this packet type. + * @param decoder The factory for this type of packet. + */ + private static void registerMainThread( int id, Class type, Function decoder ) + { + packetIds.put( type, id ); + packetReaders.put( id, ( context, buf ) -> { + T result = decoder.apply( buf ); + context.getTaskQueue().execute( () -> result.handle( context ) ); + } ); + } } diff --git a/src/main/java/dan200/computercraft/shared/network/NetworkMessage.java b/src/main/java/dan200/computercraft/shared/network/NetworkMessage.java index bcffe6950..545e7dc15 100644 --- a/src/main/java/dan200/computercraft/shared/network/NetworkMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/NetworkMessage.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.network; import net.fabricmc.fabric.api.network.PacketContext; 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 b3f93804c..5ac6bcad6 100644 --- a/src/main/java/dan200/computercraft/shared/network/client/ChatTableClientMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/client/ChatTableClientMessage.java @@ -3,14 +3,11 @@ * 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.ClientTableFormatter; import dan200.computercraft.shared.command.text.TableBuilder; 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.FriendlyByteBuf; import net.minecraft.network.chat.Component; @@ -23,10 +20,7 @@ public class ChatTableClientMessage implements NetworkMessage public ChatTableClientMessage( TableBuilder table ) { - if( table.getColumns() < 0 ) - { - throw new IllegalStateException( "Cannot send an empty table" ); - } + if( table.getColumns() < 0 ) throw new IllegalStateException( "Cannot send an empty table" ); this.table = table; } @@ -38,10 +32,7 @@ public ChatTableClientMessage( @Nonnull FriendlyByteBuf buf ) if( buf.readBoolean() ) { Component[] headers = new Component[columns]; - for( int i = 0; i < columns; i++ ) - { - headers[i] = buf.readComponent(); - } + for( int i = 0; i < columns; i++ ) headers[i] = buf.readComponent(); table = new TableBuilder( id, headers ); } else @@ -53,10 +44,7 @@ public ChatTableClientMessage( @Nonnull FriendlyByteBuf buf ) for( int i = 0; i < rows; i++ ) { Component[] row = new Component[columns]; - for( int j = 0; j < columns; j++ ) - { - row[j] = buf.readComponent(); - } + for( int j = 0; j < columns; j++ ) row[j] = buf.readComponent(); table.row( row ); } @@ -72,27 +60,19 @@ public void toBytes( @Nonnull FriendlyByteBuf buf ) buf.writeBoolean( table.getHeaders() != null ); if( table.getHeaders() != null ) { - for( Component header : table.getHeaders() ) - { - buf.writeComponent( header ); - } + for( Component header : table.getHeaders() ) buf.writeComponent( header ); } - buf.writeVarInt( table.getRows() - .size() ); + buf.writeVarInt( table.getRows().size() ); for( Component[] row : table.getRows() ) { - for( Component column : row ) - { - buf.writeComponent( column ); - } + for( Component column : row ) buf.writeComponent( column ); } buf.writeVarInt( table.getAdditional() ); } @Override - @Environment( EnvType.CLIENT ) public void handle( PacketContext context ) { ClientTableFormatter.INSTANCE.display( table ); 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 8092fb5c5..f42103494 100644 --- a/src/main/java/dan200/computercraft/shared/network/client/ComputerDataClientMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/client/ComputerDataClientMessage.java @@ -3,7 +3,6 @@ * 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.shared.computer.core.ComputerState; 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 322c11cc4..0c3d3cf60 100644 --- a/src/main/java/dan200/computercraft/shared/network/client/ComputerDeletedClientMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/client/ComputerDeletedClientMessage.java @@ -3,7 +3,6 @@ * 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.ComputerCraft; 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 447ab42a6..f0da6ded4 100644 --- a/src/main/java/dan200/computercraft/shared/network/client/ComputerTerminalClientMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/client/ComputerTerminalClientMessage.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.network.client; import net.fabricmc.fabric.api.network.PacketContext; diff --git a/src/main/java/dan200/computercraft/shared/network/client/MonitorClientMessage.java b/src/main/java/dan200/computercraft/shared/network/client/MonitorClientMessage.java index 00289ce75..9cf5676cb 100644 --- a/src/main/java/dan200/computercraft/shared/network/client/MonitorClientMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/client/MonitorClientMessage.java @@ -44,16 +44,10 @@ public void toBytes( @Nonnull FriendlyByteBuf buf ) public void handle( PacketContext context ) { LocalPlayer player = Minecraft.getInstance().player; - if( player == null || player.level == null ) - { - return; - } + if( player == null || player.level == null ) return; BlockEntity te = player.level.getBlockEntity( pos ); - if( !(te instanceof TileMonitor) ) - { - return; - } + if( !(te instanceof TileMonitor) ) return; ((TileMonitor) te).read( state ); } 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 4ee745101..6800dc304 100644 --- a/src/main/java/dan200/computercraft/shared/network/client/PlayRecordClientMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/client/PlayRecordClientMessage.java @@ -3,7 +3,6 @@ * 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.shared.network.NetworkMessage; @@ -83,9 +82,6 @@ public void handle( PacketContext context ) { Minecraft mc = Minecraft.getInstance(); mc.levelRenderer.playStreamingMusic( soundEvent, pos ); - if( name != null ) - { - mc.gui.setNowPlaying( new TextComponent( name ) ); - } + if( name != null ) mc.gui.setNowPlaying( new TextComponent( name ) ); } } diff --git a/src/main/java/dan200/computercraft/shared/network/client/TerminalState.java b/src/main/java/dan200/computercraft/shared/network/client/TerminalState.java index 1b7bd8504..964923c31 100644 --- a/src/main/java/dan200/computercraft/shared/network/client/TerminalState.java +++ b/src/main/java/dan200/computercraft/shared/network/client/TerminalState.java @@ -24,8 +24,9 @@ /** * A snapshot of a terminal's state. * - * This is somewhat memory inefficient (we build a buffer, only to write it elsewhere), however it means we get a complete and accurate description of a - * terminal, which avoids a lot of complexities with resizing terminals, dirty states, etc... + * This is somewhat memory inefficient (we build a buffer, only to write it elsewhere), however it means we get a + * complete and accurate description of a terminal, which avoids a lot of complexities with resizing terminals, dirty + * states, etc... */ public class TerminalState { @@ -86,44 +87,6 @@ public TerminalState( FriendlyByteBuf buf ) } } - private static ByteBuf readCompressed( ByteBuf buf, int length, boolean compress ) - { - if( compress ) - { - ByteBuf buffer = Unpooled.buffer(); - InputStream stream = null; - try - { - stream = new GZIPInputStream( new ByteBufInputStream( buf, length ) ); - byte[] swap = new byte[8192]; - while( true ) - { - int bytes = stream.read( swap ); - if( bytes == -1 ) - { - break; - } - buffer.writeBytes( swap, 0, bytes ); - } - } - catch( IOException e ) - { - throw new UncheckedIOException( e ); - } - finally - { - IoUtil.closeQuietly( stream ); - } - return buffer; - } - else - { - ByteBuf buffer = Unpooled.buffer( length ); - buf.readBytes( buffer, length ); - return buffer; - } - } - public void write( FriendlyByteBuf buf ) { buf.writeBoolean( colour ); @@ -141,22 +104,29 @@ public void write( FriendlyByteBuf buf ) } } + public boolean hasTerminal() + { + return buffer != null; + } + + public int size() + { + return buffer == null ? 0 : buffer.readableBytes(); + } + + public void apply( Terminal terminal ) + { + if( buffer == null ) throw new NullPointerException( "buffer" ); + terminal.read( new FriendlyByteBuf( buffer ) ); + } + private ByteBuf getCompressed() { - if( buffer == null ) - { - throw new NullPointerException( "buffer" ); - } - if( !compress ) - { - return buffer; - } - if( compressed != null ) - { - return compressed; - } + if( buffer == null ) throw new NullPointerException( "buffer" ); + if( !compress ) return buffer; + if( compressed != null ) return compressed; - ByteBuf compressed = Unpooled.directBuffer(); + ByteBuf compressed = Unpooled.buffer(); OutputStream stream = null; try { @@ -175,22 +145,38 @@ private ByteBuf getCompressed() return this.compressed = compressed; } - public boolean hasTerminal() - { - return buffer != null; - } - - public int size() - { - return buffer == null ? 0 : buffer.readableBytes(); - } - - public void apply( Terminal terminal ) + private static ByteBuf readCompressed( ByteBuf buf, int length, boolean compress ) { - if( buffer == null ) + if( compress ) + { + ByteBuf buffer = Unpooled.buffer(); + InputStream stream = null; + try + { + stream = new GZIPInputStream( new ByteBufInputStream( buf, length ) ); + byte[] swap = new byte[8192]; + while( true ) + { + int bytes = stream.read( swap ); + if( bytes == -1 ) break; + buffer.writeBytes( swap, 0, bytes ); + } + } + catch( IOException e ) + { + throw new UncheckedIOException( e ); + } + finally + { + IoUtil.closeQuietly( stream ); + } + return buffer; + } + else { - throw new NullPointerException( "buffer" ); + ByteBuf buffer = Unpooled.buffer( length ); + buf.readBytes( buffer, length ); + return buffer; } - terminal.read( new FriendlyByteBuf( buffer ) ); } } 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 f39a12acf..a0e3027f8 100644 --- a/src/main/java/dan200/computercraft/shared/network/container/ComputerContainerData.java +++ b/src/main/java/dan200/computercraft/shared/network/container/ComputerContainerData.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.network.container; import dan200.computercraft.shared.computer.core.ComputerFamily; @@ -21,10 +20,10 @@ public ComputerContainerData( ServerComputer computer ) family = computer.getFamily(); } - public ComputerContainerData( FriendlyByteBuf byteBuf ) + public ComputerContainerData( FriendlyByteBuf buf ) { - id = byteBuf.readInt(); - family = byteBuf.readEnum( ComputerFamily.class ); + id = buf.readInt(); + family = buf.readEnum( 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 2fd9e0406..6717b8327 100644 --- a/src/main/java/dan200/computercraft/shared/network/container/ContainerData.java +++ b/src/main/java/dan200/computercraft/shared/network/container/ContainerData.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.network.container; import net.fabricmc.fabric.api.screenhandler.v1.ScreenHandlerRegistry; @@ -23,24 +22,6 @@ */ public interface ContainerData { - static MenuType toType( ResourceLocation identifier, Function reader, - Factory factory ) - { - return ScreenHandlerRegistry.registerExtended( identifier, - ( id, playerInventory, packetByteBuf ) -> factory.create( id, - playerInventory, - reader.apply( packetByteBuf ) ) ); - } - - static MenuType toType( ResourceLocation identifier, MenuType type, Function reader, - FixedFactory factory ) - { - return ScreenHandlerRegistry.registerExtended( identifier, - ( id, playerInventory, packetByteBuf ) -> factory.create( type, id, - playerInventory, - reader.apply( packetByteBuf ) ) ); - } - void toBytes( FriendlyByteBuf buf ); default void open( Player player, MenuProvider owner ) @@ -58,4 +39,22 @@ interface FixedFactory { C create( MenuType type, int id, @Nonnull Inventory inventory, T data ); } + + static MenuType toType( ResourceLocation identifier, Function reader, + Factory factory ) + { + return ScreenHandlerRegistry.registerExtended( identifier, + ( id, playerInventory, packetByteBuf ) -> factory.create( id, + playerInventory, + reader.apply( packetByteBuf ) ) ); + } + + static MenuType toType( ResourceLocation identifier, MenuType type, Function reader, + FixedFactory factory ) + { + return ScreenHandlerRegistry.registerExtended( identifier, + ( id, playerInventory, packetByteBuf ) -> factory.create( type, id, + playerInventory, + reader.apply( packetByteBuf ) ) ); + } } 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 212dd6eae..3726b3dc3 100644 --- a/src/main/java/dan200/computercraft/shared/network/server/ComputerActionServerMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/server/ComputerActionServerMessage.java @@ -3,7 +3,6 @@ * 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; @@ -55,6 +54,8 @@ protected void handle( PacketContext context, @Nonnull ServerComputer computer, public enum Action { - TURN_ON, SHUTDOWN, REBOOT + TURN_ON, + SHUTDOWN, + REBOOT } } 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 867f5dcd4..defde0b5f 100644 --- a/src/main/java/dan200/computercraft/shared/network/server/ComputerServerMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/server/ComputerServerMessage.java @@ -3,7 +3,6 @@ * 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; @@ -18,7 +17,8 @@ /** * A packet, which performs an action on a {@link ServerComputer}. * - * This requires that the sending player is interacting with that computer via a {@link IContainerComputer}. + * This requires that the sending player is interacting with that computer via a + * {@link IContainerComputer}. */ public abstract class ComputerServerMessage implements NetworkMessage { @@ -44,16 +44,10 @@ public void toBytes( @Nonnull FriendlyByteBuf buf ) public void handle( PacketContext context ) { ServerComputer computer = ComputerCraft.serverComputerRegistry.get( instanceId ); - if( computer == null ) - { - return; - } + if( computer == null ) return; IContainerComputer container = computer.getContainer( context.getPlayer() ); - if( container == null ) - { - return; - } + if( container == null ) return; handle( context, computer, container ); } 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 99c514933..76e84c66e 100644 --- a/src/main/java/dan200/computercraft/shared/network/server/KeyEventServerMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/server/KeyEventServerMessage.java @@ -3,7 +3,6 @@ * 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; 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 024ca7547..e151c5146 100644 --- a/src/main/java/dan200/computercraft/shared/network/server/MouseEventServerMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/server/MouseEventServerMessage.java @@ -3,7 +3,6 @@ * 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; 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 04506ad0f..6a4b485d0 100644 --- a/src/main/java/dan200/computercraft/shared/network/server/QueueEventServerMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/server/QueueEventServerMessage.java @@ -3,7 +3,6 @@ * 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; 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 81151437a..f2c62fc13 100644 --- a/src/main/java/dan200/computercraft/shared/network/server/RequestComputerMessage.java +++ b/src/main/java/dan200/computercraft/shared/network/server/RequestComputerMessage.java @@ -3,7 +3,6 @@ * 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; @@ -38,9 +37,6 @@ public void toBytes( @Nonnull FriendlyByteBuf buf ) public void handle( PacketContext context ) { ServerComputer computer = ComputerCraft.serverComputerRegistry.get( instance ); - if( computer != null ) - { - computer.sendComputerState( context.getPlayer() ); - } + if( computer != null ) computer.sendComputerState( context.getPlayer() ); } } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/commandblock/CommandBlockPeripheral.java b/src/main/java/dan200/computercraft/shared/peripheral/commandblock/CommandBlockPeripheral.java index 1453aa7c7..0f371731d 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/commandblock/CommandBlockPeripheral.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/commandblock/CommandBlockPeripheral.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.peripheral.commandblock; import dan200.computercraft.ComputerCraft; @@ -18,7 +17,8 @@ /** * This peripheral allows you to interact with command blocks. * - * Command blocks are only wrapped as peripherals if the {@literal enable_command_block} option is true within the config. + * Command blocks are only wrapped as peripherals if the {@literal enable_command_block} option is true within the + * config. * * This API is not the same as the {@link CommandAPI} API, which is exposed on command computers. * @@ -42,19 +42,6 @@ public String getType() return "command"; } - @Nonnull - @Override - public Object getTarget() - { - return commandBlock; - } - - @Override - public boolean equals( IPeripheral other ) - { - return other != null && other.getClass() == getClass(); - } - /** * Get the command this command block will run. * @@ -63,8 +50,7 @@ public boolean equals( IPeripheral other ) @LuaFunction( mainThread = true ) public final String getCommand() { - return commandBlock.getCommandBlock() - .getCommand(); + return commandBlock.getCommandBlock().getCommand(); } /** @@ -75,10 +61,8 @@ public final String getCommand() @LuaFunction( mainThread = true ) public final void setCommand( String command ) { - commandBlock.getCommandBlock() - .setCommand( command ); - commandBlock.getCommandBlock() - .onUpdated(); + commandBlock.getCommandBlock().setCommand( command ); + commandBlock.getCommandBlock().onUpdated(); } /** @@ -91,10 +75,21 @@ public final void setCommand( String command ) @LuaFunction( mainThread = true ) public final Object[] runCommand() { - commandBlock.getCommandBlock() - .performCommand( commandBlock.getLevel() ); - int result = commandBlock.getCommandBlock() - .getSuccessCount(); + commandBlock.getCommandBlock().performCommand( commandBlock.getLevel() ); + int result = commandBlock.getCommandBlock().getSuccessCount(); return result > 0 ? new Object[] { true } : new Object[] { false, "Command failed" }; } + + @Override + public boolean equals( IPeripheral other ) + { + return other != null && other.getClass() == getClass(); + } + + @Nonnull + @Override + public Object getTarget() + { + return commandBlock; + } } 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 3839d391c..d2e941573 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/BlockDiskDrive.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/BlockDiskDrive.java @@ -3,10 +3,9 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.peripheral.diskdrive; -import dan200.computercraft.shared.ComputerCraftRegistry; +import dan200.computercraft.shared.Registry; import dan200.computercraft.shared.common.BlockGeneric; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -17,6 +16,7 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.BaseEntityBlock; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityTicker; @@ -33,44 +33,42 @@ public class BlockDiskDrive extends BlockGeneric { static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING; - static final EnumProperty STATE = EnumProperty.create( "state", DiskDriveState.class ); + public static final EnumProperty STATE = EnumProperty.create( "state", DiskDriveState.class ); + + private static final BlockEntityTicker serverTicker = ( level, pos, state, drive ) -> drive.serverTick(); public BlockDiskDrive( Properties settings ) { - super( settings, ComputerCraftRegistry.ModTiles.DISK_DRIVE ); + super( settings, () -> Registry.ModBlockEntities.DISK_DRIVE ); registerDefaultState( getStateDefinition().any() .setValue( FACING, Direction.NORTH ) .setValue( STATE, DiskDriveState.EMPTY ) ); } - @Nullable + @Override - public BlockState getStateForPlacement( BlockPlaceContext placement ) + protected void createBlockStateDefinition( StateDefinition.Builder properties ) { - return defaultBlockState().setValue( FACING, - placement.getHorizontalDirection() - .getOpposite() ); + properties.add( FACING, STATE ); } @Nullable @Override - public BlockEntityTicker getTicker( Level world, BlockState state, BlockEntityType type ) + public BlockState getStateForPlacement( BlockPlaceContext placement ) { - return world.isClientSide ? null : BlockDiskDrive.createTickerHelper( type, ComputerCraftRegistry.ModTiles.DISK_DRIVE, TileDiskDrive::tick ); + return defaultBlockState().setValue( FACING, placement.getHorizontalDirection().getOpposite() ); } @Override - public void playerDestroy( - @Nonnull Level world, @Nonnull Player player, @Nonnull BlockPos pos, @Nonnull BlockState state, @Nullable BlockEntity te, @Nonnull ItemStack stack - ) + public void playerDestroy( @Nonnull Level world, @Nonnull Player player, @Nonnull BlockPos pos, @Nonnull BlockState state, @Nullable BlockEntity te, @Nonnull ItemStack stack ) { - if( te instanceof Nameable && ((Nameable) te).hasCustomName() ) + if( te instanceof Nameable nameable && nameable.hasCustomName() ) { player.awardStat( Stats.BLOCK_MINED.get( this ) ); player.causeFoodExhaustion( 0.005F ); ItemStack result = new ItemStack( this ); - result.setHoverName( ((Nameable) te).getCustomName() ); + result.setHoverName( nameable.getCustomName() ); popResource( world, pos, result ); } else @@ -82,26 +80,16 @@ public void playerDestroy( @Override public void setPlacedBy( @Nonnull Level world, @Nonnull BlockPos pos, @Nonnull BlockState state, LivingEntity placer, ItemStack stack ) { - if( stack.hasCustomHoverName() ) + if( stack.hasCustomHoverName() && world.getBlockEntity( pos ) instanceof TileDiskDrive drive ) { - BlockEntity tileentity = world.getBlockEntity( pos ); - if( tileentity instanceof TileDiskDrive ) - { - ((TileDiskDrive) tileentity).customName = stack.getHoverName(); - } + drive.customName = stack.getHoverName(); } } @Override - protected void createBlockStateDefinition( StateDefinition.Builder properties ) - { - properties.add( FACING, STATE ); - } - @Nullable - @Override - public BlockEntity newBlockEntity( BlockPos pos, BlockState state ) + public BlockEntityTicker getTicker( @Nonnull Level level, @Nonnull BlockState state, @Nonnull BlockEntityType type ) { - return new TileDiskDrive( ComputerCraftRegistry.ModTiles.DISK_DRIVE, pos, state ); + return level.isClientSide ? null : BaseEntityBlock.createTickerHelper( type, Registry.ModBlockEntities.DISK_DRIVE, serverTicker ); } } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/ContainerDiskDrive.java b/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/ContainerDiskDrive.java index 5053c5f0b..dc261d215 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/ContainerDiskDrive.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/ContainerDiskDrive.java @@ -3,10 +3,9 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.peripheral.diskdrive; -import dan200.computercraft.shared.ComputerCraftRegistry; +import dan200.computercraft.shared.Registry; import net.minecraft.world.Container; import net.minecraft.world.SimpleContainer; import net.minecraft.world.entity.player.Inventory; @@ -21,14 +20,9 @@ public class ContainerDiskDrive extends AbstractContainerMenu { private final Container inventory; - public ContainerDiskDrive( int id, Inventory player ) - { - this( id, player, new SimpleContainer( 1 ) ); - } - public ContainerDiskDrive( int id, Inventory player, Container inventory ) { - super( ComputerCraftRegistry.ModContainers.DISK_DRIVE, id ); + super( Registry.ModContainers.DISK_DRIVE, id ); this.inventory = inventory; @@ -48,34 +42,35 @@ public ContainerDiskDrive( int id, Inventory player, Container inventory ) } } + public ContainerDiskDrive( int id, Inventory player ) + { + this( id, player, new SimpleContainer( 1 ) ); + } + + @Override + public boolean stillValid( @Nonnull Player player ) + { + return inventory.stillValid( player ); + } + @Nonnull @Override public ItemStack quickMoveStack( @Nonnull Player player, int slotIndex ) { Slot slot = slots.get( slotIndex ); - if( slot == null || !slot.hasItem() ) - { - return ItemStack.EMPTY; - } + if( slot == null || !slot.hasItem() ) return ItemStack.EMPTY; - ItemStack existing = slot.getItem() - .copy(); + ItemStack existing = slot.getItem().copy(); ItemStack result = existing.copy(); if( slotIndex == 0 ) { // Insert into player inventory - if( !moveItemStackTo( existing, 1, 37, true ) ) - { - return ItemStack.EMPTY; - } + if( !moveItemStackTo( existing, 1, 37, true ) ) return ItemStack.EMPTY; } else { // Insert into drive inventory - if( !moveItemStackTo( existing, 0, 1, false ) ) - { - return ItemStack.EMPTY; - } + if( !moveItemStackTo( existing, 0, 1, false ) ) return ItemStack.EMPTY; } if( existing.isEmpty() ) @@ -87,18 +82,9 @@ public ItemStack quickMoveStack( @Nonnull Player player, int slotIndex ) slot.setChanged(); } - if( existing.getCount() == result.getCount() ) - { - return ItemStack.EMPTY; - } + if( existing.getCount() == result.getCount() ) return ItemStack.EMPTY; slot.onTake( player, existing ); return result; } - - @Override - public boolean stillValid( @Nonnull Player player ) - { - return inventory.stillValid( player ); - } } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/DiskDrivePeripheral.java b/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/DiskDrivePeripheral.java index 1be070474..d5593d87f 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/DiskDrivePeripheral.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/DiskDrivePeripheral.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.peripheral.diskdrive; import dan200.computercraft.api.lua.LuaException; @@ -21,14 +20,15 @@ import java.util.Optional; /** - * Disk drives are a peripheral which allow you to read and write to floppy disks and other "mountable media" (such as computers or turtles). They also - * allow you to {@link #playAudio play records}. + * Disk drives are a peripheral which allow you to read and write to floppy disks and other "mountable media" (such as + * computers or turtles). They also allow you to {@link #playAudio play records}. * - * When a disk drive attaches some mount (such as a floppy disk or computer), it attaches a folder called {@code disk}, {@code disk2}, etc... to the root - * directory of the computer. This folder can be used to interact with the files on that disk. + * When a disk drive attaches some mount (such as a floppy disk or computer), it attaches a folder called {@code disk}, + * {@code disk2}, etc... to the root directory of the computer. This folder can be used to interact with the files on + * that disk. * - * When a disk is inserted, a {@code disk} event is fired, with the side peripheral is on. Likewise, when the disk is detached, a {@code disk_eject} event - * is fired. + * When a disk is inserted, a {@code disk} event is fired, with the side peripheral is on. Likewise, when the disk is + * detached, a {@code disk_eject} event is fired. * * @cc.module drive */ @@ -48,31 +48,6 @@ public String getType() return "drive"; } - @Override - public void attach( @Nonnull IComputerAccess computer ) - { - diskDrive.mount( computer ); - } - - @Override - public void detach( @Nonnull IComputerAccess computer ) - { - diskDrive.unmount( computer ); - } - - @Nonnull - @Override - public Object getTarget() - { - return diskDrive; - } - - @Override - public boolean equals( IPeripheral other ) - { - return this == other || other instanceof DiskDrivePeripheral && ((DiskDrivePeripheral) other).diskDrive == diskDrive; - } - /** * Returns whether a disk is currently inserted in the drive. * @@ -81,8 +56,7 @@ public boolean equals( IPeripheral other ) @LuaFunction public final boolean isDiskPresent() { - return !diskDrive.getDiskStack() - .isEmpty(); + return !diskDrive.getDiskStack().isEmpty(); } /** @@ -104,7 +78,8 @@ public final Object[] getDiskLabel() * * If no label or {@code nil} is passed, the label will be cleared. * - * If the inserted disk's label can't be changed (for example, a record), an error will be thrown. + * If the inserted disk's label can't be changed (for example, a record), + * an error will be thrown. * * @param labelA The new label of the disk, or {@code nil} to clear. * @throws LuaException If the disk's label can't be changed. @@ -115,10 +90,7 @@ public final void setDiskLabel( Optional labelA ) throws LuaException String label = labelA.orElse( null ); ItemStack stack = diskDrive.getDiskStack(); IMedia media = MediaProviders.get( stack ); - if( media == null ) - { - return; - } + if( media == null ) return; if( !media.setLabel( stack, StringUtil.normaliseLabel( label ) ) ) { @@ -214,6 +186,7 @@ public final void ejectDisk() * * @return The ID of the disk in the drive, or {@code nil} if no disk with an ID is inserted. * @cc.treturn number The The ID of the disk in the drive, or {@code nil} if no disk with an ID is inserted. + * @cc.since 1.4 */ @LuaFunction public final Object[] getDiskID() @@ -221,4 +194,29 @@ public final Object[] getDiskID() ItemStack disk = diskDrive.getDiskStack(); return disk.getItem() instanceof ItemDisk ? new Object[] { ItemDisk.getDiskID( disk ) } : null; } + + @Override + public void attach( @Nonnull IComputerAccess computer ) + { + diskDrive.mount( computer ); + } + + @Override + public void detach( @Nonnull IComputerAccess computer ) + { + diskDrive.unmount( computer ); + } + + @Override + public boolean equals( IPeripheral other ) + { + return this == other || other instanceof DiskDrivePeripheral drive && drive.diskDrive == diskDrive; + } + + @Nonnull + @Override + public Object getTarget() + { + return diskDrive; + } } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/DiskDriveState.java b/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/DiskDriveState.java index cbd06d69c..7d0638d55 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/DiskDriveState.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/DiskDriveState.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.peripheral.diskdrive; import net.minecraft.util.StringRepresentable; @@ -12,7 +11,9 @@ public enum DiskDriveState implements StringRepresentable { - EMPTY( "empty" ), FULL( "full" ), INVALID( "invalid" ); + EMPTY( "empty" ), + FULL( "full" ), + INVALID( "invalid" ); private final String name; 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 4f9b7914b..85348dfe9 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/TileDiskDrive.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/diskdrive/TileDiskDrive.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.peripheral.diskdrive; import dan200.computercraft.api.filesystem.IMount; @@ -32,10 +31,10 @@ import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.BlockHitResult; +import org.jetbrains.annotations.NotNull; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -43,15 +42,25 @@ import java.util.Map; import java.util.Set; -public final class TileDiskDrive extends TileGeneric implements DefaultInventory, IPeripheralTile, Nameable, MenuProvider +public final class TileDiskDrive extends TileGeneric implements IPeripheralTile, DefaultInventory, Nameable, MenuProvider { private static final String NBT_NAME = "CustomName"; private static final String NBT_ITEM = "Item"; - private final Map computers = new HashMap<>(); + + private static class MountInfo + { + String mountPath; + } + Component customName; + + private final Map computers = new HashMap<>(); + @Nonnull private ItemStack diskStack = ItemStack.EMPTY; + private DiskDrivePeripheral peripheral; private IMount diskMount = null; + private boolean recordQueued = false; private boolean recordPlaying = false; private boolean restartRecord = false; @@ -66,10 +75,7 @@ public TileDiskDrive( BlockEntityType type, BlockPos pos, BlockSt public void destroy() { ejectContents( true ); - if( recordPlaying ) - { - stopRecord(); - } + if( recordPlaying ) stopRecord(); } @Nonnull @@ -80,10 +86,7 @@ public InteractionResult onActivate( Player player, InteractionHand hand, BlockH { // Try to put a disk into the drive ItemStack disk = player.getItemInHand( hand ); - if( disk.isEmpty() ) - { - return InteractionResult.PASS; - } + if( disk.isEmpty() ) return InteractionResult.PASS; if( !getLevel().isClientSide && getItem( 0 ).isEmpty() && MediaProviders.get( disk ) != null ) { setDiskStack( disk ); @@ -94,10 +97,7 @@ public InteractionResult onActivate( Player player, InteractionHand hand, BlockH else { // Open the GUI - if( !getLevel().isClientSide ) - { - player.openMenu( this ); - } + if( !getLevel().isClientSide ) player.openMenu( this ); return InteractionResult.SUCCESS; } } @@ -123,10 +123,7 @@ public void load( @Nonnull CompoundTag nbt ) @Override public void saveAdditional( @Nonnull CompoundTag nbt ) { - if( customName != null ) - { - nbt.putString( NBT_NAME, Component.Serializer.toJson( customName ) ); - } + if( customName != null ) nbt.putString( NBT_NAME, Component.Serializer.toJson( customName ) ); if( !diskStack.isEmpty() ) { @@ -134,53 +131,42 @@ public void saveAdditional( @Nonnull CompoundTag nbt ) diskStack.save( item ); nbt.put( NBT_ITEM, item ); } - super.saveAdditional( nbt ); } - @Override - public void setChanged() - { - if( !level.isClientSide ) - { - updateBlockState(); - } - super.setChanged(); - } - - public static void tick( Level world, BlockPos pos, BlockState state, TileDiskDrive tileDiskDrive ) + void serverTick() { // Ejection - if( tileDiskDrive.ejectQueued ) + if( ejectQueued ) { - tileDiskDrive.ejectContents( false ); - tileDiskDrive.ejectQueued = false; + ejectContents( false ); + ejectQueued = false; } // Music - synchronized( tileDiskDrive ) + synchronized( this ) { - if( !world.isClientSide && tileDiskDrive.recordPlaying != tileDiskDrive.recordQueued || tileDiskDrive.restartRecord ) + if( recordPlaying != recordQueued || restartRecord ) { - tileDiskDrive.restartRecord = false; - if( tileDiskDrive.recordQueued ) + restartRecord = false; + if( recordQueued ) { - IMedia contents = tileDiskDrive.getDiskMedia(); - SoundEvent record = contents != null ? contents.getAudio( tileDiskDrive.diskStack ) : null; + IMedia contents = getDiskMedia(); + SoundEvent record = contents != null ? contents.getAudio( diskStack ) : null; if( record != null ) { - tileDiskDrive.recordPlaying = true; - tileDiskDrive.playRecord(); + recordPlaying = true; + playRecord(); } else { - tileDiskDrive.recordQueued = false; + recordQueued = false; } } else { - tileDiskDrive.stopRecord(); - tileDiskDrive.recordPlaying = false; + stopRecord(); + recordPlaying = false; } } } @@ -207,14 +193,22 @@ public ItemStack getItem( int slot ) return diskStack; } + @Nonnull + @Override + public ItemStack removeItemNoUpdate( int slot ) + { + ItemStack result = diskStack; + diskStack = ItemStack.EMPTY; + diskMount = null; + + return result; + } + @Nonnull @Override public ItemStack removeItem( int slot, int count ) { - if( diskStack.isEmpty() ) - { - return ItemStack.EMPTY; - } + if( diskStack.isEmpty() ) return ItemStack.EMPTY; if( diskStack.getCount() <= count ) { @@ -228,17 +222,6 @@ public ItemStack removeItem( int slot, int count ) return part; } - @Nonnull - @Override - public ItemStack removeItemNoUpdate( int slot ) - { - ItemStack result = diskStack; - diskStack = ItemStack.EMPTY; - diskMount = null; - - return result; - } - @Override public void setItem( int slot, @Nonnull ItemStack stack ) { @@ -263,10 +246,7 @@ public void setItem( int slot, @Nonnull ItemStack stack ) { // TODO: Is this iteration thread safe? Set computers = this.computers.keySet(); - for( IComputerAccess computer : computers ) - { - unmountDisk( computer ); - } + for( IComputerAccess computer : computers ) unmountDisk( computer ); } // Stop music @@ -286,14 +266,18 @@ public void setItem( int slot, @Nonnull ItemStack stack ) if( !diskStack.isEmpty() ) { Set computers = this.computers.keySet(); - for( IComputerAccess computer : computers ) - { - mountDisk( computer ); - } + for( IComputerAccess computer : computers ) mountDisk( computer ); } } } + @Override + public void setChanged() + { + if( !level.isClientSide ) updateBlockState(); + super.setChanged(); + } + @Override public boolean stillValid( @Nonnull Player player ) { @@ -307,10 +291,19 @@ public void clearContent() } @Nonnull - @Override - public IPeripheral getPeripheral( Direction side ) + ItemStack getDiskStack() { - return new DiskDrivePeripheral( this ); + return getItem( 0 ); + } + + void setDiskStack( @Nonnull ItemStack stack ) + { + setItem( 0, stack ); + } + + private IMedia getDiskMedia() + { + return MediaProviders.get( getDiskStack() ); } String getDiskMountPath( IComputerAccess computer ) @@ -331,6 +324,47 @@ void mount( IComputerAccess computer ) } } + void unmount( IComputerAccess computer ) + { + synchronized( this ) + { + unmountDisk( computer ); + computers.remove( computer ); + } + } + + void playDiskAudio() + { + synchronized( this ) + { + IMedia media = getDiskMedia(); + if( media != null && media.getAudioTitle( diskStack ) != null ) + { + recordQueued = true; + restartRecord = recordPlaying; + } + } + } + + void stopDiskAudio() + { + synchronized( this ) + { + recordQueued = false; + restartRecord = false; + } + } + + void ejectDisk() + { + synchronized( this ) + { + ejectQueued = true; + } + } + + // private methods + private synchronized void mountDisk( IComputerAccess computer ) { if( !diskStack.isEmpty() ) @@ -375,31 +409,6 @@ private synchronized void mountDisk( IComputerAccess computer ) } } - private IMedia getDiskMedia() - { - return MediaProviders.get( getDiskStack() ); - } - - @Nonnull - ItemStack getDiskStack() - { - return getItem( 0 ); - } - - void setDiskStack( @Nonnull ItemStack stack ) - { - setItem( 0, stack ); - } - - void unmount( IComputerAccess computer ) - { - synchronized( this ) - { - unmountDisk( computer ); - computers.remove( computer ); - } - } - private synchronized void unmountDisk( IComputerAccess computer ) { if( !diskStack.isEmpty() ) @@ -415,44 +424,9 @@ private synchronized void unmountDisk( IComputerAccess computer ) } } - void playDiskAudio() - { - synchronized( this ) - { - IMedia media = getDiskMedia(); - if( media != null && media.getAudioTitle( diskStack ) != null ) - { - recordQueued = true; - restartRecord = recordPlaying; - } - } - } - - void stopDiskAudio() - { - synchronized( this ) - { - recordQueued = false; - restartRecord = false; - } - } - - // private methods - - void ejectDisk() - { - synchronized( this ) - { - ejectQueued = true; - } - } - private void updateBlockState() { - if( remove ) - { - return; - } + if( remove || level == null ) return; if( !diskStack.isEmpty() ) { @@ -468,20 +442,14 @@ private void updateBlockState() private void updateBlockState( DiskDriveState state ) { BlockState blockState = getBlockState(); - if( blockState.getValue( BlockDiskDrive.STATE ) == state ) - { - return; - } + if( blockState.getValue( BlockDiskDrive.STATE ) == state ) return; getLevel().setBlockAndUpdate( getBlockPos(), blockState.setValue( BlockDiskDrive.STATE, state ) ); } private synchronized void ejectContents( boolean destroyed ) { - if( this.level.isClientSide || diskStack.isEmpty() ) - { - return; - } + if( getLevel().isClientSide || diskStack.isEmpty() ) return; // Remove the disks from the inventory ItemStack disks = diskStack; @@ -505,12 +473,11 @@ private synchronized void ejectContents( boolean destroyed ) entityitem.setDeltaMovement( xOff * 0.15, 0, zOff * 0.15 ); getLevel().addFreshEntity( entityitem ); - if( !destroyed ) - { - getLevel().globalLevelEvent( 1000, getBlockPos(), 0 ); - } + if( !destroyed ) getLevel().globalLevelEvent( 1000, getBlockPos(), 0 ); } + // Private methods + private void playRecord() { IMedia contents = getDiskMedia(); @@ -525,19 +492,17 @@ private void playRecord() } } - // Private methods - private void stopRecord() { RecordUtil.playRecord( null, null, getLevel(), getBlockPos() ); } - @Nonnull + @Nullable @Override - public Component getName() + public IPeripheral getPeripheral( @NotNull Direction side ) { - return customName != null ? customName : new TranslatableComponent( getBlockState().getBlock() - .getDescriptionId() ); + if( peripheral == null ) peripheral = new DiskDrivePeripheral( this ); + return peripheral; } @Override @@ -546,29 +511,31 @@ public boolean hasCustomName() return customName != null; } - @Nonnull + @Nullable @Override - public Component getDisplayName() + public Component getCustomName() { - return Nameable.super.getDisplayName(); + return customName; } - @Nullable + @Nonnull @Override - public Component getCustomName() + public Component getName() { - return customName; + return customName != null ? customName : new TranslatableComponent( getBlockState().getBlock().getDescriptionId() ); } @Nonnull @Override - public AbstractContainerMenu createMenu( int id, @Nonnull Inventory inventory, @Nonnull Player player ) + public Component getDisplayName() { - return new ContainerDiskDrive( id, inventory, this ); + return Nameable.super.getDisplayName(); } - private static class MountInfo + @Nonnull + @Override + public AbstractContainerMenu createMenu( int id, @Nonnull Inventory inventory, @Nonnull Player player ) { - String mountPath; + return new ContainerDiskDrive( id, inventory, this ); } } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheral.java b/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheral.java index 6e75ebd32..710c2efdd 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheral.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheral.java @@ -14,23 +14,26 @@ import dan200.computercraft.api.peripheral.IPeripheral; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.entity.BlockEntityType; +import net.minecraft.core.Registry; import javax.annotation.Nonnull; import javax.annotation.Nullable; import java.util.List; +import java.util.Set; class GenericPeripheral implements IDynamicPeripheral { private final String type; + private final Set additionalTypes; private final BlockEntity tile; private final List methods; - GenericPeripheral( BlockEntity tile, List methods ) + GenericPeripheral( BlockEntity tile, String name, Set additionalTypes, List methods ) { - ResourceLocation type = BlockEntityType.getKey( tile.getType() ); + ResourceLocation type = Registry.BLOCK_ENTITY_TYPE.getKey( tile.getType() ); this.tile = tile; - this.type = type == null ? "unknown" : type.toString(); + this.type = name != null ? name : (type != null ? type.toString() : "unknown"); + this.additionalTypes = additionalTypes; this.methods = methods; } @@ -57,6 +60,13 @@ public String getType() return type; } + @Nonnull + @Override + public Set getAdditionalTypes() + { + return additionalTypes; + } + @Nullable @Override public Object getTarget() @@ -68,9 +78,8 @@ public Object getTarget() public boolean equals( @Nullable IPeripheral other ) { if( other == this ) return true; - if( !(other instanceof GenericPeripheral) ) return false; + if( !(other instanceof GenericPeripheral generic) ) return false; - GenericPeripheral generic = (GenericPeripheral) other; return tile == generic.tile && methods.equals( generic.methods ); } } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheralProvider.java b/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheralProvider.java index d77203984..71a3b1e8f 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheralProvider.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/generic/GenericPeripheralProvider.java @@ -6,6 +6,7 @@ package dan200.computercraft.shared.peripheral.generic; import dan200.computercraft.api.peripheral.IPeripheral; +import dan200.computercraft.api.peripheral.PeripheralType; import dan200.computercraft.core.asm.NamedMethod; import dan200.computercraft.core.asm.PeripheralMethod; import net.minecraft.core.BlockPos; @@ -15,8 +16,7 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; -import java.util.ArrayList; -import java.util.List; +import java.util.*; public class GenericPeripheralProvider { @@ -26,20 +26,46 @@ public static IPeripheral getPeripheral( @Nonnull Level world, @Nonnull BlockPos BlockEntity tile = world.getBlockEntity( pos ); if( tile == null ) return null; - ArrayList saturated = new ArrayList<>( 0 ); + GenericPeripheralBuilder saturated = new GenericPeripheralBuilder(); List> tileMethods = PeripheralMethod.GENERATOR.getMethods( tile.getClass() ); - if( !tileMethods.isEmpty() ) addSaturated( saturated, tile, tileMethods ); + if( !tileMethods.isEmpty() ) saturated.addMethods( tile, tileMethods ); - return saturated.isEmpty() ? null : new GenericPeripheral( tile, saturated ); + return saturated.toPeripheral( tile ); } - private static void addSaturated( ArrayList saturated, Object target, List> methods ) + private static class GenericPeripheralBuilder { - saturated.ensureCapacity( saturated.size() + methods.size() ); - for( NamedMethod method : methods ) + private String name; + private final Set additionalTypes = new HashSet<>( 0 ); + private final ArrayList methods = new ArrayList<>( 0 ); + + IPeripheral toPeripheral( BlockEntity tile ) + { + if( methods.isEmpty() ) return null; + + methods.trimToSize(); + return new GenericPeripheral( tile, name, additionalTypes, methods ); + } + + void addMethods( Object target, List> methods ) { - saturated.add( new SaturatedMethod( target, method ) ); + ArrayList saturatedMethods = this.methods; + saturatedMethods.ensureCapacity( saturatedMethods.size() + methods.size() ); + for( NamedMethod method : methods ) + { + saturatedMethods.add( new SaturatedMethod( target, method ) ); + + // If we have a peripheral type, use it. Always pick the smallest one, so it's consistent (assuming mods + // don't change). + PeripheralType type = method.getGenericType(); + if( type != null && type.getPrimaryType() != null ) + { + String name = type.getPrimaryType(); + if( this.name == null || this.name.compareTo( name ) > 0 ) this.name = name; + } + if( type != null ) additionalTypes.addAll( type.getAdditionalTypes() ); + } } } } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/generic/data/BlockData.java b/src/main/java/dan200/computercraft/shared/peripheral/generic/data/BlockData.java index 352d37669..965bad990 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/generic/data/BlockData.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/generic/data/BlockData.java @@ -5,7 +5,6 @@ */ package dan200.computercraft.shared.peripheral.generic.data; -import com.google.common.collect.ImmutableMap; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.properties.Property; @@ -21,7 +20,7 @@ public static > T fill( @Nonnull T data, @ data.put( "name", DataHelpers.getId( state.getBlock() ) ); Map stateTable = new HashMap<>(); - for( ImmutableMap.Entry, ? extends Comparable> entry : state.getValues().entrySet() ) + for( Map.Entry, ? extends Comparable> entry : state.getValues().entrySet() ) { Property property = entry.getKey(); stateTable.put( property.getName(), getPropertyValue( property, entry.getValue() ) ); diff --git a/src/main/java/dan200/computercraft/shared/peripheral/generic/data/DataHelpers.java b/src/main/java/dan200/computercraft/shared/peripheral/generic/data/DataHelpers.java index 86bcd9d4f..8cdfeab0b 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/generic/data/DataHelpers.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/generic/data/DataHelpers.java @@ -5,6 +5,7 @@ */ package dan200.computercraft.shared.peripheral.generic.data; +import net.minecraft.client.Minecraft; import net.minecraft.core.Registry; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; @@ -30,6 +31,20 @@ public static Map getTags( @Nonnull Collection getTags( @Nonnull Block block ) + { + Collection tags = Minecraft.getInstance().getConnection().getTags().getOrEmpty( Registry.BLOCK_REGISTRY ).getMatchingTags( block ); + return getTags( tags ); + } + + @Nonnull + static Map getTags( @Nonnull Item item ) + { + Collection tags = Minecraft.getInstance().getConnection().getTags().getOrEmpty( Registry.ITEM_REGISTRY ).getMatchingTags( item ); + return getTags( tags ); + } + @Nullable public static String getId( @Nonnull Block block ) { 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 314a645e0..361bd2eee 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 @@ -7,15 +7,11 @@ import com.google.gson.JsonParseException; import dan200.computercraft.shared.util.NBTUtil; -import net.minecraft.core.Registry; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; import net.minecraft.nbt.Tag; import net.minecraft.network.chat.Component; -import net.minecraft.tags.SerializationTags; -import net.minecraft.tags.TagCollection; import net.minecraft.world.item.EnchantedBookItem; -import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.enchantment.Enchantment; import net.minecraft.world.item.enchantment.EnchantmentHelper; @@ -45,7 +41,6 @@ public static > T fillBasic( @Nonnull T da fillBasicSafe( data, stack ); String hash = NBTUtil.getNBTHash( stack.getTag() ); if( hash != null ) data.put( "nbt", hash ); - return data; } @@ -65,26 +60,20 @@ public static > T fill( @Nonnull T data, @ data.put( "maxDamage", stack.getMaxDamage() ); } - if( stack.isDamaged() ) + if( stack.getItem().isBarVisible( stack ) ) { - data.put( "durability", (double) stack.getDamageValue() / stack.getMaxDamage() ); + data.put( "durability", stack.getItem().getBarWidth( stack ) / 13.0 ); } - // 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 :( - TagCollection itemTags = SerializationTags.getInstance().getOrEmpty( Registry.ITEM_REGISTRY ); - data.put( "tags", DataHelpers.getTags( itemTags.getAvailableTags().stream() - .filter( id -> Objects.requireNonNull( itemTags.getTag( id ) ).contains( stack.getItem() ) ) - .collect( Collectors.toList() ) - ) ); // chaos x2 + data.put( "tags", DataHelpers.getTags( stack.getItem() ) ); CompoundTag tag = stack.getTag(); - if( tag != null && tag.contains( "display", NBTUtil.TAG_COMPOUND ) ) + if( tag != null && tag.contains( "display", Tag.TAG_COMPOUND ) ) { CompoundTag displayTag = tag.getCompound( "display" ); - if( displayTag.contains( "Lore", NBTUtil.TAG_LIST ) ) + if( displayTag.contains( "Lore", Tag.TAG_LIST ) ) { - ListTag loreTag = displayTag.getList( "Lore", NBTUtil.TAG_STRING ); + ListTag loreTag = displayTag.getList( "Lore", Tag.TAG_STRING ); data.put( "lore", loreTag.stream() .map( ItemData::parseTextComponent ) .filter( Objects::nonNull ) @@ -111,13 +100,12 @@ public static > T fill( @Nonnull T data, @ return data; } - @Nullable private static Component parseTextComponent( @Nonnull Tag x ) { try { - return Component.Serializer.fromJson( x.toString() ); + return Component.Serializer.fromJson( x.getAsString() ); } catch( JsonParseException e ) { @@ -168,7 +156,6 @@ private static void addEnchantments( @Nonnull ListTag rawEnchants, @Nonnull Arra enchants.ensureCapacity( enchants.size() + rawEnchants.size() ); - for( Map.Entry entry : EnchantmentHelper.deserializeEnchantments( rawEnchants ).entrySet() ) { Enchantment enchantment = entry.getKey(); diff --git a/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/InventoryMethods.java b/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/InventoryMethods.java index 7be7113b5..5f1aecbb3 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/InventoryMethods.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/generic/methods/InventoryMethods.java @@ -6,17 +6,18 @@ package dan200.computercraft.shared.peripheral.generic.methods; import dan200.computercraft.ComputerCraft; -import dan200.computercraft.api.lua.GenericSource; +import dan200.computercraft.api.lua.ILuaContext; import dan200.computercraft.api.lua.LuaException; import dan200.computercraft.api.lua.LuaFunction; +import dan200.computercraft.api.peripheral.GenericPeripheral; import dan200.computercraft.api.peripheral.IComputerAccess; import dan200.computercraft.api.peripheral.IPeripheral; +import dan200.computercraft.api.peripheral.PeripheralType; import dan200.computercraft.shared.peripheral.generic.data.ItemData; import dan200.computercraft.shared.util.InventoryUtil; import dan200.computercraft.shared.util.ItemStorage; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.Container; -import net.minecraft.world.Nameable; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.entity.BlockEntity; @@ -33,8 +34,15 @@ * * @cc.module inventory */ -public class InventoryMethods implements GenericSource +public class InventoryMethods implements GenericPeripheral { + @Nonnull + @Override + public PeripheralType getType() + { + return PeripheralType.ofAdditional( "inventory" ); + } + @Nonnull @Override public ResourceLocation id() @@ -54,30 +62,14 @@ public static int size( Container inventory ) return extractHandler( inventory ).size(); } - /** - * Get the name of this inventory. - * - * @param inventory The current inventory. - * @return The name of this inventory, or {@code nil} if not present. - */ - @LuaFunction( mainThread = true ) - public static String name( Container inventory ) - { - if( inventory instanceof Nameable ) - { - Nameable i = (Nameable) inventory; - return i.hasCustomName() ? i.getName().getContents() : null; - } - return null; - } - /** * List all items in this inventory. This returns a table, with an entry for each slot. * * Each item in the inventory is represented by a table containing some basic information, much like - * {@link dan200.computercraft.shared.turtle.apis.TurtleAPI#getItemDetail} includes. More information can be fetched - * with {@link #getItemDetail}. The table contains the item `name`, the `count` and an a (potentially nil) hash of - * the item's `nbt.` This NBT data doesn't contain anything useful, but allows you to distinguish identical items. + * {@link dan200.computercraft.shared.turtle.apis.TurtleAPI#getItemDetail(ILuaContext, Optional, Optional)} + * includes. More information can be fetched with {@link #getItemDetail}. The table contains the item `name`, the + * `count` and an a (potentially nil) hash of the item's `nbt.` This NBT data doesn't contain anything useful, but + * allows you to distinguish identical items. * * The returned table is sparse, and so empty slots will be `nil` - it is recommended to loop over using `pairs` * rather than `ipairs`. @@ -145,7 +137,6 @@ public static String name( Container inventory ) public static Map getItemDetail( Container inventory, int slot ) throws LuaException { ItemStorage itemStorage = extractHandler( inventory ); - assertBetween( slot, 1, itemStorage.size(), "Slot out of range (%s)" ); ItemStack stack = itemStorage.getStack( slot - 1 ); @@ -282,17 +273,16 @@ public static int pullItems( @Nullable private static ItemStorage extractHandler( @Nullable Object object ) { - if( object instanceof BlockEntity ) + if( object instanceof BlockEntity blockEntity && blockEntity.isRemoved() ) return null; + + if( object instanceof BlockEntity blockEntity ) { - Container inventory = InventoryUtil.getInventory( (BlockEntity) object ); - if( inventory != null ) - { - return ItemStorage.wrap( inventory ); - } + Container inventory = InventoryUtil.getInventory( blockEntity ); + if( inventory != null ) return ItemStorage.wrap( inventory ); } - else if( object instanceof Container ) + else if( object instanceof Container container ) { - return ItemStorage.wrap( (Container) object ); + return ItemStorage.wrap( container ); } return null; diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/ModemShapes.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/ModemShapes.java index 365abeca4..a0c314a62 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/modem/ModemShapes.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/ModemShapes.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.peripheral.modem; import net.minecraft.core.Direction; @@ -15,18 +14,12 @@ public final class ModemShapes { private static final VoxelShape[] BOXES = new VoxelShape[] { - Shapes.box( 0.125, 0.0, 0.125, 0.875, 0.1875, 0.875 ), - // Down - Shapes.box( 0.125, 0.8125, 0.125, 0.875, 1.0, 0.875 ), - // Up - Shapes.box( 0.125, 0.125, 0.0, 0.875, 0.875, 0.1875 ), - // North - Shapes.box( 0.125, 0.125, 0.8125, 0.875, 0.875, 1.0 ), - // South - Shapes.box( 0.0, 0.125, 0.125, 0.1875, 0.875, 0.875 ), - // West - Shapes.box( 0.8125, 0.125, 0.125, 1.0, 0.875, 0.875 ), - // East + Shapes.box( 0.125, 0.0, 0.125, 0.875, 0.1875, 0.875 ), // Down + Shapes.box( 0.125, 0.8125, 0.125, 0.875, 1.0, 0.875 ), // Up + Shapes.box( 0.125, 0.125, 0.0, 0.875, 0.875, 0.1875 ), // North + Shapes.box( 0.125, 0.125, 0.8125, 0.875, 0.875, 1.0 ), // South + Shapes.box( 0.0, 0.125, 0.125, 0.1875, 0.875, 0.875 ), // West + Shapes.box( 0.8125, 0.125, 0.125, 1.0, 0.875, 0.875 ), // East }; @Nonnull diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/ModemState.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/ModemState.java index 95f52550b..faea8052d 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/modem/ModemState.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/ModemState.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.peripheral.modem; import dan200.computercraft.api.lua.LuaException; @@ -16,8 +15,9 @@ public class ModemState { private final Runnable onChanged; private final AtomicBoolean changed = new AtomicBoolean( true ); - private final IntSet channels = new IntOpenHashSet(); + private boolean open = false; + private final IntSet channels = new IntOpenHashSet(); public ModemState() { @@ -29,6 +29,13 @@ public ModemState( Runnable onChanged ) this.onChanged = onChanged; } + private void setOpen( boolean open ) + { + if( this.open == open ) return; + this.open = open; + if( !changed.getAndSet( true ) && onChanged != null ) onChanged.run(); + } + public boolean pollChanged() { return changed.getAndSet( false ); @@ -39,19 +46,6 @@ public boolean isOpen() return open; } - private void setOpen( boolean open ) - { - if( this.open == open ) - { - return; - } - this.open = open; - if( !changed.getAndSet( true ) && onChanged != null ) - { - onChanged.run(); - } - } - public boolean isOpen( int channel ) { synchronized( channels ) @@ -66,10 +60,7 @@ public void open( int channel ) throws LuaException { if( !channels.contains( channel ) ) { - if( channels.size() >= 128 ) - { - throw new LuaException( "Too many open channels" ); - } + if( channels.size() >= 128 ) throw new LuaException( "Too many open channels" ); channels.add( channel ); setOpen( true ); } @@ -81,10 +72,7 @@ public void close( int channel ) synchronized( channels ) { channels.remove( channel ); - if( channels.isEmpty() ) - { - setOpen( false ); - } + if( channels.isEmpty() ) setOpen( false ); } } 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 f338e29b4..250699e7f 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 @@ -3,13 +3,13 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.peripheral.modem.wired; import com.google.common.collect.ImmutableMap; import dan200.computercraft.api.ComputerCraftAPI; -import dan200.computercraft.shared.ComputerCraftRegistry; +import dan200.computercraft.shared.Registry; import dan200.computercraft.shared.common.BlockGeneric; +import dan200.computercraft.shared.util.WaterloggableHelpers; import dan200.computercraft.shared.util.WorldUtil; import net.fabricmc.fabric.api.block.BlockPickInteractionAware; import net.minecraft.core.BlockPos; @@ -36,7 +36,8 @@ import javax.annotation.Nullable; import java.util.EnumMap; -import static dan200.computercraft.shared.util.WaterloggableHelpers.*; +import static dan200.computercraft.shared.util.WaterloggableHelpers.WATERLOGGED; +import static dan200.computercraft.shared.util.WaterloggableHelpers.getFluidStateForPlacement; public class BlockCable extends BlockGeneric implements SimpleWaterloggedBlock, BlockPickInteractionAware { @@ -50,78 +51,51 @@ public class BlockCable extends BlockGeneric implements SimpleWaterloggedBlock, private static final BooleanProperty UP = BooleanProperty.create( "up" ); private static final BooleanProperty DOWN = BooleanProperty.create( "down" ); - static final EnumMap CONNECTIONS = new EnumMap<>( new ImmutableMap.Builder().put( Direction.DOWN, - DOWN ) - .put( Direction.UP, - UP ) - .put( Direction.NORTH, - NORTH ) - .put( Direction.SOUTH, - SOUTH ) - .put( Direction.WEST, - WEST ) - .put( Direction.EAST, - EAST ) - .build() ); + static final EnumMap CONNECTIONS = + new EnumMap<>( new ImmutableMap.Builder() + .put( Direction.DOWN, DOWN ).put( Direction.UP, UP ) + .put( Direction.NORTH, NORTH ).put( Direction.SOUTH, SOUTH ) + .put( Direction.WEST, WEST ).put( Direction.EAST, EAST ) + .build() ); public BlockCable( Properties settings ) { - super( settings, ComputerCraftRegistry.ModTiles.CABLE ); + super( settings, () -> Registry.ModBlockEntities.CABLE ); registerDefaultState( getStateDefinition().any() .setValue( MODEM, CableModemVariant.None ) .setValue( CABLE, false ) - .setValue( NORTH, false ) - .setValue( SOUTH, false ) - .setValue( EAST, false ) - .setValue( WEST, false ) - .setValue( UP, false ) - .setValue( DOWN, false ) - .setValue( WATERLOGGED, false ) ); + .setValue( NORTH, false ).setValue( SOUTH, false ) + .setValue( EAST, false ).setValue( WEST, false ) + .setValue( UP, false ).setValue( DOWN, false ) + .setValue( WATERLOGGED, false ) + ); } - public static boolean canConnectIn( BlockState state, Direction direction ) + @Override + protected void createBlockStateDefinition( StateDefinition.Builder builder ) { - return state.getValue( BlockCable.CABLE ) && state.getValue( BlockCable.MODEM ) - .getFacing() != direction; + builder.add( MODEM, CABLE, NORTH, SOUTH, EAST, WEST, UP, DOWN, WATERLOGGED ); } - @Nonnull - @Override - @Deprecated - public BlockState updateShape( @Nonnull BlockState state, @Nonnull Direction side, @Nonnull BlockState otherState, - @Nonnull LevelAccessor world, @Nonnull BlockPos pos, @Nonnull BlockPos otherPos ) + public static boolean canConnectIn( BlockState state, Direction direction ) { - updateWaterloggedPostPlacement( state, world, pos ); - // Should never happen, but handle the case where we've no modem or cable. - if( !state.getValue( CABLE ) && state.getValue( MODEM ) == CableModemVariant.None ) - { - return getFluidState( state ).createLegacyBlock(); - } - - return state.setValue( CONNECTIONS.get( side ), doesConnectVisually( state, world, pos, side ) ); + return state.getValue( BlockCable.CABLE ) && state.getValue( BlockCable.MODEM ).getFacing() != direction; } public static boolean doesConnectVisually( BlockState state, BlockGetter world, BlockPos pos, Direction direction ) { - if( !state.getValue( CABLE ) ) - { - return false; - } - if( state.getValue( MODEM ) - .getFacing() == direction ) - { - return true; - } + if( !state.getValue( CABLE ) ) return false; + if( state.getValue( MODEM ).getFacing() == direction ) return true; return ComputerCraftAPI.getWiredElementAt( world, pos.relative( direction ), direction.getOpposite() ) != null; } @Nonnull @Override @Deprecated - public FluidState getFluidState( @Nonnull BlockState state ) + public VoxelShape getShape( @Nonnull BlockState state, @Nonnull BlockGetter world, @Nonnull BlockPos pos, @Nonnull CollisionContext context ) { - return getWaterloggedFluidState( state ); + return CableShapes.getShape( state ); } public boolean removedByPlayer( BlockState state, Level world, BlockPos pos, Player player, boolean willHarvest, FluidState fluid ) @@ -144,12 +118,12 @@ public boolean removedByPlayer( BlockState state, Level world, BlockPos pos, Pla if( WorldUtil.isVecInside( CableShapes.getModemShape( state ), hit.getLocation().subtract( pos.getX(), pos.getY(), pos.getZ() ) ) ) { newState = state.setValue( MODEM, CableModemVariant.None ); - item = new ItemStack( ComputerCraftRegistry.ModItems.WIRED_MODEM ); + item = new ItemStack( Registry.ModItems.WIRED_MODEM ); } else { newState = state.setValue( CABLE, false ); - item = new ItemStack( ComputerCraftRegistry.ModItems.CABLE ); + item = new ItemStack( Registry.ModItems.CABLE ); } world.setBlock( pos, correctConnections( world, pos, newState ), 3 ); @@ -169,6 +143,7 @@ public boolean removedByPlayer( BlockState state, Level world, BlockPos pos, Pla return true; } + @Nonnull @Override public ItemStack getPickedStack( BlockState state, BlockGetter world, BlockPos pos, @Nullable Player player, HitResult hit ) { @@ -176,84 +151,85 @@ public ItemStack getPickedStack( BlockState state, BlockGetter world, BlockPos p boolean cable = state.getValue( CABLE ); // If we've only got one, just use that. - if( !cable ) return new ItemStack( ComputerCraftRegistry.ModItems.WIRED_MODEM ); - if( modem == null ) return new ItemStack( ComputerCraftRegistry.ModItems.CABLE ); + if( !cable ) return new ItemStack( Registry.ModItems.WIRED_MODEM ); + if( modem == null ) return new ItemStack( Registry.ModItems.CABLE ); // We've a modem and cable, so try to work out which one we're interacting with return hit != null && WorldUtil.isVecInside( CableShapes.getModemShape( state ), hit.getLocation().subtract( pos.getX(), pos.getY(), pos.getZ() ) ) - ? new ItemStack( ComputerCraftRegistry.ModItems.WIRED_MODEM ) - : new ItemStack( ComputerCraftRegistry.ModItems.CABLE ); + ? new ItemStack( Registry.ModItems.WIRED_MODEM ) + : new ItemStack( Registry.ModItems.CABLE ); } @Override - @Deprecated - public boolean canSurvive( BlockState state, @Nonnull LevelReader world, @Nonnull BlockPos pos ) + public void setPlacedBy( Level world, @Nonnull BlockPos pos, @Nonnull BlockState state, LivingEntity placer, @Nonnull ItemStack stack ) { - Direction facing = state.getValue( MODEM ) - .getFacing(); - if( facing == null ) + BlockEntity tile = world.getBlockEntity( pos ); + if( tile instanceof TileCable cable ) { - return true; + if( cable.hasCable() ) cable.connectionsChanged(); } - return canSupportCenter( world, pos.relative( facing ), facing.getOpposite() ); + super.setPlacedBy( world, pos, state, placer, stack ); } @Nonnull @Override @Deprecated - public VoxelShape getShape( @Nonnull BlockState state, @Nonnull BlockGetter world, @Nonnull BlockPos pos, @Nonnull CollisionContext context ) + public FluidState getFluidState( @Nonnull BlockState state ) { - return CableShapes.getShape( state ); + return WaterloggableHelpers.getFluidState( state ); } - @Nullable + @Nonnull @Override - public BlockState getStateForPlacement( @Nonnull BlockPlaceContext context ) + @Deprecated + public BlockState updateShape( @Nonnull BlockState state, @Nonnull Direction side, @Nonnull BlockState otherState, @Nonnull LevelAccessor world, @Nonnull BlockPos pos, @Nonnull BlockPos otherPos ) { - BlockState state = defaultBlockState().setValue( WATERLOGGED, getWaterloggedStateForPlacement( context ) ); - - if( context.getItemInHand() - .getItem() instanceof ItemBlockCable.Cable ) - { - Level world = context.getLevel(); - BlockPos pos = context.getClickedPos(); - return correctConnections( world, pos, state.setValue( CABLE, true ) ); - } - else + WaterloggableHelpers.updateShape( state, world, pos ); + // Should never happen, but handle the case where we've no modem or cable. + if( !state.getValue( CABLE ) && state.getValue( MODEM ) == CableModemVariant.None ) { - return state.setValue( MODEM, - CableModemVariant.from( context.getClickedFace() - .getOpposite() ) ); + return getFluidState( state ).createLegacyBlock(); } + + return state.setValue( CONNECTIONS.get( side ), doesConnectVisually( state, world, pos, side ) ); } @Override - public void setPlacedBy( Level world, @Nonnull BlockPos pos, @Nonnull BlockState state, LivingEntity placer, @Nonnull ItemStack stack ) + @Deprecated + public boolean canSurvive( BlockState state, @Nonnull LevelReader world, @Nonnull BlockPos pos ) { - BlockEntity tile = world.getBlockEntity( pos ); - if( tile instanceof TileCable cable ) - { - if( cable.hasCable() ) - { - cable.connectionsChanged(); - } - } + Direction facing = state.getValue( MODEM ).getFacing(); + if( facing == null ) return true; - super.setPlacedBy( world, pos, state, placer, stack ); + return canSupportCenter( world, pos.relative( facing ), facing.getOpposite() ); } + @Nullable @Override - protected void createBlockStateDefinition( StateDefinition.Builder builder ) + public BlockState getStateForPlacement( @Nonnull BlockPlaceContext context ) { - builder.add( MODEM, CABLE, NORTH, SOUTH, EAST, WEST, UP, DOWN, WATERLOGGED ); + BlockState state = defaultBlockState() + .setValue( WATERLOGGED, getFluidStateForPlacement( context ) ); + + if( context.getItemInHand().getItem() instanceof ItemBlockCable.Cable ) + { + Level world = context.getLevel(); + BlockPos pos = context.getClickedPos(); + return correctConnections( world, pos, state.setValue( CABLE, true ) ); + } + else + { + return state.setValue( MODEM, CableModemVariant.from( context.getClickedFace().getOpposite() ) ); + } } public static BlockState correctConnections( Level world, BlockPos pos, BlockState state ) { if( state.getValue( CABLE ) ) { - return state.setValue( NORTH, doesConnectVisually( state, world, pos, Direction.NORTH ) ) + return state + .setValue( NORTH, doesConnectVisually( state, world, pos, Direction.NORTH ) ) .setValue( SOUTH, doesConnectVisually( state, world, pos, Direction.SOUTH ) ) .setValue( EAST, doesConnectVisually( state, world, pos, Direction.EAST ) ) .setValue( WEST, doesConnectVisually( state, world, pos, Direction.WEST ) ) @@ -262,19 +238,9 @@ public static BlockState correctConnections( Level world, BlockPos pos, BlockSta } else { - return state.setValue( NORTH, false ) - .setValue( SOUTH, false ) - .setValue( EAST, false ) - .setValue( WEST, false ) - .setValue( UP, false ) - .setValue( DOWN, false ); + return state + .setValue( NORTH, false ).setValue( SOUTH, false ).setValue( EAST, false ) + .setValue( WEST, false ).setValue( UP, false ).setValue( DOWN, false ); } } - - @Nullable - @Override - public BlockEntity newBlockEntity( 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 2f14df974..a208681a2 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 @@ -3,32 +3,27 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.peripheral.modem.wired; -import dan200.computercraft.shared.ComputerCraftRegistry; +import dan200.computercraft.shared.Registry; import dan200.computercraft.shared.common.BlockGeneric; -import net.minecraft.core.BlockPos; import net.minecraft.world.level.block.Block; -import net.minecraft.world.level.block.entity.BlockEntity; -import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.StateDefinition; import net.minecraft.world.level.block.state.properties.BooleanProperty; -import javax.annotation.Nullable; - public class BlockWiredModemFull extends BlockGeneric { public static final BooleanProperty MODEM_ON = BooleanProperty.create( "modem" ); public static final BooleanProperty PERIPHERAL_ON = BooleanProperty.create( "peripheral" ); - public BlockWiredModemFull( Properties settings, BlockEntityType type ) + public BlockWiredModemFull( Properties settings ) { - super( settings, type ); + super( settings, () -> Registry.ModBlockEntities.WIRED_MODEM_FULL ); registerDefaultState( getStateDefinition().any() .setValue( MODEM_ON, false ) - .setValue( PERIPHERAL_ON, false ) ); + .setValue( PERIPHERAL_ON, false ) + ); } @Override @@ -36,11 +31,4 @@ protected void createBlockStateDefinition( StateDefinition.Builder SHAPE_CABLE_ARM = - new EnumMap<>( new ImmutableMap.Builder().put( Direction.DOWN, - Shapes.box( - MIN, - 0, - MIN, - MAX, - MIN, - MAX ) ) - .put( Direction.UP, - Shapes.box( - MIN, - MAX, - MIN, - MAX, - 1, - MAX ) ) - .put( Direction.NORTH, - Shapes.box( - MIN, - MIN, - 0, - MAX, - MAX, - MIN ) ) - .put( Direction.SOUTH, - Shapes.box( - MIN, - MIN, - MAX, - MAX, - MAX, - 1 ) ) - .put( Direction.WEST, - Shapes.box( - 0, - MIN, - MIN, - MIN, - MAX, - MAX ) ) - .put( Direction.EAST, - Shapes.box( - MAX, - MIN, - MIN, - 1, - MAX, - MAX ) ) - .build() ); + new EnumMap<>( new ImmutableMap.Builder() + .put( Direction.DOWN, Shapes.box( MIN, 0, MIN, MAX, MIN, MAX ) ) + .put( Direction.UP, Shapes.box( MIN, MAX, MIN, MAX, 1, MAX ) ) + .put( Direction.NORTH, Shapes.box( MIN, MIN, 0, MAX, MAX, MIN ) ) + .put( Direction.SOUTH, Shapes.box( MIN, MIN, MAX, MAX, MAX, 1 ) ) + .put( Direction.WEST, Shapes.box( 0, MIN, MIN, MIN, MAX, MAX ) ) + .put( Direction.EAST, Shapes.box( MAX, MIN, MIN, 1, MAX, MAX ) ) + .build() + ); private static final VoxelShape[] SHAPES = new VoxelShape[(1 << 6) * 7]; private static final VoxelShape[] CABLE_SHAPES = new VoxelShape[1 << 6]; @@ -82,22 +41,21 @@ private CableShapes() { } - public static VoxelShape getCableShape( BlockState state ) + private static int getCableIndex( BlockState state ) { - if( !state.getValue( CABLE ) ) + int index = 0; + for( Direction facing : DirectionUtil.FACINGS ) { - return Shapes.empty(); + if( state.getValue( CONNECTIONS.get( facing ) ) ) index |= 1 << facing.ordinal(); } - return getCableShape( getCableIndex( state ) ); + + return index; } private static VoxelShape getCableShape( int index ) { VoxelShape shape = CABLE_SHAPES[index]; - if( shape != null ) - { - return shape; - } + if( shape != null ) return shape; shape = SHAPE_CABLE_CORE; for( Direction facing : DirectionUtil.FACINGS ) @@ -111,50 +69,31 @@ private static VoxelShape getCableShape( int index ) return CABLE_SHAPES[index] = shape; } - private static int getCableIndex( BlockState state ) + public static VoxelShape getCableShape( BlockState state ) { - int index = 0; - for( Direction facing : DirectionUtil.FACINGS ) - { - if( state.getValue( CONNECTIONS.get( facing ) ) ) - { - index |= 1 << facing.ordinal(); - } - } + if( !state.getValue( CABLE ) ) return Shapes.empty(); + return getCableShape( getCableIndex( state ) ); + } - return index; + public static VoxelShape getModemShape( BlockState state ) + { + Direction facing = state.getValue( MODEM ).getFacing(); + return facing == null ? Shapes.empty() : ModemShapes.getBounds( facing ); } public static VoxelShape getShape( BlockState state ) { - Direction facing = state.getValue( MODEM ) - .getFacing(); - if( !state.getValue( CABLE ) ) - { - return getModemShape( state ); - } + Direction facing = state.getValue( MODEM ).getFacing(); + if( !state.getValue( CABLE ) ) return getModemShape( state ); int cableIndex = getCableIndex( state ); int index = cableIndex + ((facing == null ? 0 : facing.ordinal() + 1) << 6); VoxelShape shape = SHAPES[index]; - if( shape != null ) - { - return shape; - } + if( shape != null ) return shape; shape = getCableShape( cableIndex ); - if( facing != null ) - { - shape = Shapes.or( shape, ModemShapes.getBounds( facing ) ); - } + if( facing != null ) shape = Shapes.or( shape, ModemShapes.getBounds( facing ) ); return SHAPES[index] = shape; } - - public static VoxelShape getModemShape( BlockState state ) - { - Direction facing = state.getValue( MODEM ) - .getFacing(); - return facing == null ? Shapes.empty() : ModemShapes.getBounds( facing ); - } } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/ItemBlockCable.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/ItemBlockCable.java index 4eadf5d4a..a17bfbf07 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/ItemBlockCable.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/ItemBlockCable.java @@ -3,15 +3,13 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.peripheral.modem.wired; -import dan200.computercraft.shared.ComputerCraftRegistry; +import dan200.computercraft.shared.Registry; import net.minecraft.Util; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.NonNullList; -import net.minecraft.core.Registry; import net.minecraft.sounds.SoundSource; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.player.Player; @@ -37,22 +35,13 @@ public ItemBlockCable( BlockCable block, Properties settings ) super( block, settings ); } - boolean placeAtCorrected( Level world, BlockPos pos, BlockState state ) - { - return placeAt( world, pos, correctConnections( world, pos, state ), null ); - } - boolean placeAt( Level world, BlockPos pos, BlockState state, Player player ) { // TODO: Check entity collision. - if( !state.canSurvive( world, pos ) ) - { - return false; - } + if( !state.canSurvive( world, pos ) ) return false; world.setBlock( pos, state, 3 ); - SoundType soundType = state.getBlock() - .getSoundType( state ); + SoundType soundType = state.getBlock().getSoundType( state ); world.playSound( null, pos, soundType.getPlaceSound(), SoundSource.BLOCKS, (soundType.getVolume() + 1.0F) / 2.0F, soundType.getPitch() * 0.8F ); BlockEntity tile = world.getBlockEntity( pos ); @@ -65,24 +54,26 @@ boolean placeAt( Level world, BlockPos pos, BlockState state, Player player ) return true; } - @Nonnull - @Override - public String getDescriptionId() + boolean placeAtCorrected( Level world, BlockPos pos, BlockState state ) { - if( translationKey == null ) - { - translationKey = Util.makeDescriptionId( "block", Registry.ITEM.getKey( this ) ); - } - return translationKey; + return placeAt( world, pos, correctConnections( world, pos, state ), null ); } @Override public void fillItemCategory( @Nonnull CreativeModeTab group, @Nonnull NonNullList list ) { - if( allowdedIn( group ) ) + if( allowdedIn( group ) ) list.add( new ItemStack( this ) ); + } + + @Nonnull + @Override + public String getDescriptionId() + { + if( translationKey == null ) { - list.add( new ItemStack( this ) ); + translationKey = Util.makeDescriptionId( "block", net.minecraft.core.Registry.ITEM.getKey( this ) ); } + return translationKey; } public static class WiredModem extends ItemBlockCable @@ -97,21 +88,18 @@ public WiredModem( BlockCable block, Properties settings ) public InteractionResult place( BlockPlaceContext context ) { ItemStack stack = context.getItemInHand(); - if( stack.isEmpty() ) - { - return InteractionResult.FAIL; - } + if( stack.isEmpty() ) return InteractionResult.FAIL; Level world = context.getLevel(); BlockPos pos = context.getClickedPos(); BlockState existingState = world.getBlockState( pos ); // Try to add a modem to a cable - if( existingState.getBlock() == ComputerCraftRegistry.ModBlocks.CABLE && existingState.getValue( MODEM ) == CableModemVariant.None ) + if( existingState.getBlock() == Registry.ModBlocks.CABLE && existingState.getValue( MODEM ) == CableModemVariant.None ) { - Direction side = context.getClickedFace() - .getOpposite(); - BlockState newState = existingState.setValue( MODEM, CableModemVariant.from( side ) ) + Direction side = context.getClickedFace().getOpposite(); + BlockState newState = existingState + .setValue( MODEM, CableModemVariant.from( side ) ) .setValue( CONNECTIONS.get( side ), existingState.getValue( CABLE ) ); if( placeAt( world, pos, newState, context.getPlayer() ) ) { @@ -136,23 +124,16 @@ public Cable( BlockCable block, Properties settings ) public InteractionResult place( BlockPlaceContext context ) { ItemStack stack = context.getItemInHand(); - if( stack.isEmpty() ) - { - return InteractionResult.FAIL; - } + if( stack.isEmpty() ) return InteractionResult.FAIL; Level world = context.getLevel(); BlockPos pos = context.getClickedPos(); // Try to add a cable to a modem inside the block we're clicking on. - BlockPos insidePos = pos.relative( context.getClickedFace() - .getOpposite() ); + BlockPos insidePos = pos.relative( context.getClickedFace().getOpposite() ); BlockState insideState = world.getBlockState( insidePos ); - if( insideState.getBlock() == ComputerCraftRegistry.ModBlocks.CABLE && !insideState.getValue( BlockCable.CABLE ) && placeAtCorrected( world, - insidePos, - insideState.setValue( - BlockCable.CABLE, - true ) ) ) + if( insideState.getBlock() == Registry.ModBlocks.CABLE && !insideState.getValue( BlockCable.CABLE ) + && placeAtCorrected( world, insidePos, insideState.setValue( BlockCable.CABLE, true ) ) ) { stack.shrink( 1 ); return InteractionResult.SUCCESS; @@ -160,11 +141,8 @@ public InteractionResult place( BlockPlaceContext context ) // Try to add a cable to a modem adjacent to this block BlockState existingState = world.getBlockState( pos ); - if( existingState.getBlock() == ComputerCraftRegistry.ModBlocks.CABLE && !existingState.getValue( BlockCable.CABLE ) && placeAtCorrected( world, - pos, - existingState.setValue( - BlockCable.CABLE, - true ) ) ) + if( existingState.getBlock() == Registry.ModBlocks.CABLE && !existingState.getValue( BlockCable.CABLE ) + && placeAtCorrected( world, pos, existingState.setValue( BlockCable.CABLE, true ) ) ) { stack.shrink( 1 ); return InteractionResult.SUCCESS; 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 7b57cee37..f51913ca7 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 @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.peripheral.modem.wired; import com.google.common.base.Objects; @@ -12,7 +11,7 @@ import dan200.computercraft.api.network.wired.IWiredNode; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheralTile; -import dan200.computercraft.shared.ComputerCraftRegistry; +import dan200.computercraft.shared.Registry; import dan200.computercraft.shared.command.text.ChatHelpers; import dan200.computercraft.shared.common.TileGeneric; import dan200.computercraft.shared.peripheral.modem.ModemState; @@ -41,12 +40,51 @@ public class TileCable extends TileGeneric implements IPeripheralTile { private static final String NBT_PERIPHERAL_ENABLED = "PeirpheralAccess"; - private final WiredModemLocalPeripheral peripheral = new WiredModemLocalPeripheral(); - private final WiredModemElement cable = new CableElement(); - private final IWiredNode node = cable.getNode(); + + private class CableElement extends WiredModemElement + { + @Nonnull + @Override + public Level getLevel() + { + return TileCable.this.getLevel(); + } + + @Nonnull + @Override + public Vec3 getPosition() + { + BlockPos pos = getBlockPos(); + return new Vec3( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 ); + } + + @Override + protected void attachPeripheral( String name, IPeripheral peripheral ) + { + modem.attachPeripheral( name, peripheral ); + } + + @Override + protected void detachPeripheral( String name ) + { + modem.detachPeripheral( name ); + } + } + + private boolean invalidPeripheral; private boolean peripheralAccessAllowed; + private final WiredModemLocalPeripheral peripheral = new WiredModemLocalPeripheral( this::queueRefreshPeripheral ); + private boolean destroyed = false; - private final WiredModemPeripheral modem = new WiredModemPeripheral( new ModemState( () -> TickScheduler.schedule( this ) ), cable ) + + private boolean connectionsFormed = false; + + private final WiredModemElement cable = new CableElement(); + private final IWiredNode node = cable.getNode(); + private final WiredModemPeripheral modem = new WiredModemPeripheral( + new ModemState( () -> TickScheduler.schedule( this ) ), + cable + ) { @Nonnull @Override @@ -70,13 +108,21 @@ public Object getTarget() return TileCable.this; } }; - private boolean connectionsFormed = false; public TileCable( BlockEntityType type, BlockPos pos, BlockState state ) { super( type, pos, state ); } + private void onRemove() + { + if( level == null || !level.isClientSide ) + { + node.remove(); + connectionsFormed = false; + } + } + @Override public void destroy() { @@ -95,51 +141,31 @@ public void onChunkUnloaded() onRemove(); } - private void onRemove() + @Override + public void setRemoved() { - if( level == null || !level.isClientSide ) - { - node.remove(); - connectionsFormed = false; - } + super.setRemoved(); + onRemove(); } - @Nonnull @Override - public InteractionResult onActivate( Player player, InteractionHand hand, BlockHitResult hit ) + public void clearRemoved() { - if( player.isCrouching() ) - { - return InteractionResult.PASS; - } - if( !canAttachPeripheral() ) - { - return InteractionResult.FAIL; - } - - if( this.level.isClientSide ) - { - return InteractionResult.SUCCESS; - } + super.clearRemoved(); // TODO: Replace with onLoad + TickScheduler.schedule( this ); + } - String oldName = peripheral.getConnectedName(); - togglePeripheralAccess(); - String newName = peripheral.getConnectedName(); - if( !Objects.equal( newName, oldName ) ) - { - if( oldName != null ) - { - player.displayClientMessage( new TranslatableComponent( "chat.computercraft.wired_modem.peripheral_disconnected", - ChatHelpers.copy( oldName ) ), false ); - } - if( newName != null ) - { - player.displayClientMessage( new TranslatableComponent( "chat.computercraft.wired_modem.peripheral_connected", - ChatHelpers.copy( newName ) ), false ); - } - } + @Nullable + private Direction getMaybeDirection() + { + return getBlockState().getValue( BlockCable.MODEM ).getFacing(); + } - return InteractionResult.SUCCESS; + @Nonnull + private Direction getDirection() + { + Direction direction = getMaybeDirection(); + return direction == null ? Direction.NORTH : direction; } @Override @@ -151,16 +177,15 @@ public void onNeighbourChange( @Nonnull BlockPos neighbour ) if( hasCable() ) { // Drop the modem and convert to cable - Block.popResource( getLevel(), getBlockPos(), new ItemStack( ComputerCraftRegistry.ModItems.WIRED_MODEM ) ); - getLevel().setBlockAndUpdate( getBlockPos(), - getBlockState().setValue( BlockCable.MODEM, CableModemVariant.None ) ); + Block.popResource( getLevel(), getBlockPos(), new ItemStack( Registry.ModItems.WIRED_MODEM ) ); + getLevel().setBlockAndUpdate( getBlockPos(), getBlockState().setValue( BlockCable.MODEM, CableModemVariant.None ) ); modemChanged(); connectionsChanged(); } else { // Drop everything and remove block - Block.popResource( getLevel(), getBlockPos(), new ItemStack( ComputerCraftRegistry.ModItems.WIRED_MODEM ) ); + Block.popResource( getLevel(), getBlockPos(), new ItemStack( Registry.ModItems.WIRED_MODEM ) ); getLevel().removeBlock( getBlockPos(), false ); // This'll call #destroy(), so we don't need to reset the network here. } @@ -171,40 +196,110 @@ public void onNeighbourChange( @Nonnull BlockPos neighbour ) onNeighbourTileEntityChange( neighbour ); } + @Override + public void onNeighbourTileEntityChange( @Nonnull BlockPos neighbour ) + { + super.onNeighbourTileEntityChange( neighbour ); + if( !level.isClientSide && peripheralAccessAllowed ) + { + Direction facing = getDirection(); + if( getBlockPos().relative( facing ).equals( neighbour ) ) queueRefreshPeripheral(); + } + } + + private void queueRefreshPeripheral() + { + if( invalidPeripheral ) return; + invalidPeripheral = true; + TickScheduler.schedule( this ); + } + + private void refreshPeripheral() + { + invalidPeripheral = false; + if( level != null && !isRemoved() && peripheral.attach( level, getBlockPos(), getDirection() ) ) + { + updateConnectedPeripherals(); + } + } + @Nonnull - private Direction getDirection() + @Override + public InteractionResult onActivate( Player player, InteractionHand hand, BlockHitResult hit ) { - Direction direction = getMaybeDirection(); - return direction == null ? Direction.NORTH : direction; + if( player.isCrouching() ) return InteractionResult.PASS; + if( !canAttachPeripheral() ) return InteractionResult.FAIL; + + if( getLevel().isClientSide ) return InteractionResult.SUCCESS; + + String oldName = peripheral.getConnectedName(); + togglePeripheralAccess(); + String newName = peripheral.getConnectedName(); + if( !Objects.equal( newName, oldName ) ) + { + if( oldName != null ) + { + player.displayClientMessage( new TranslatableComponent( "chat.computercraft.wired_modem.peripheral_disconnected", + ChatHelpers.copy( oldName ) ), false ); + } + if( newName != null ) + { + player.displayClientMessage( new TranslatableComponent( "chat.computercraft.wired_modem.peripheral_connected", + ChatHelpers.copy( newName ) ), false ); + } + } + + return InteractionResult.SUCCESS; } - public boolean hasModem() + @Override + public void load( @Nonnull CompoundTag nbt ) { - return getBlockState().getValue( BlockCable.MODEM ) != CableModemVariant.None; + super.load( nbt ); + peripheralAccessAllowed = nbt.getBoolean( NBT_PERIPHERAL_ENABLED ); + peripheral.read( nbt, "" ); } - boolean hasCable() + @Override + public void saveAdditional( CompoundTag nbt ) { - return getBlockState().getValue( BlockCable.CABLE ); + nbt.putBoolean( NBT_PERIPHERAL_ENABLED, peripheralAccessAllowed ); + peripheral.write( nbt, "" ); + super.saveAdditional( nbt ); } - void modemChanged() + private void updateBlockState() { - // Tell anyone who cares that the connection state has changed - if( getLevel().isClientSide ) + BlockState state = getBlockState(); + CableModemVariant oldVariant = state.getValue( BlockCable.MODEM ); + CableModemVariant newVariant = CableModemVariant + .from( oldVariant.getFacing(), modem.getModemState().isOpen(), peripheralAccessAllowed ); + + if( oldVariant != newVariant ) { - return; + level.setBlockAndUpdate( getBlockPos(), state.setValue( BlockCable.MODEM, newVariant ) ); } + } - // If we can no longer attach peripherals, then detach any - // which may have existed - if( !canAttachPeripheral() && peripheralAccessAllowed ) + @Override + public void blockTick() + { + if( getLevel().isClientSide ) return; + + if( invalidPeripheral ) refreshPeripheral(); + + if( modem.getModemState().pollChanged() ) updateBlockState(); + + if( !connectionsFormed ) { - peripheralAccessAllowed = false; - peripheral.detach(); - node.updatePeripherals( Collections.emptyMap() ); - setChanged(); - updateBlockState(); + connectionsFormed = true; + + connectionsChanged(); + if( peripheralAccessAllowed ) + { + peripheral.attach( level, worldPosition, getDirection() ); + updateConnectedPeripherals(); + } } } @@ -248,85 +343,20 @@ else if( this.node.getNetwork() == node.getNetwork() ) } } - private boolean canAttachPeripheral() - { - return hasCable() && hasModem(); - } - - private void updateBlockState() - { - BlockState state = getBlockState(); - CableModemVariant oldVariant = state.getValue( BlockCable.MODEM ); - CableModemVariant newVariant = CableModemVariant.from( oldVariant.getFacing(), modem.getModemState() - .isOpen(), peripheralAccessAllowed ); - - if( oldVariant != newVariant ) - { - level.setBlockAndUpdate( getBlockPos(), state.setValue( BlockCable.MODEM, newVariant ) ); - } - } - - private void refreshPeripheral() + void modemChanged() { - if( level != null && !isRemoved() && peripheral.attach( level, getBlockPos(), getDirection() ) ) - { - updateConnectedPeripherals(); - } - } + if( getLevel().isClientSide ) return; - private void updateConnectedPeripherals() - { - Map peripherals = peripheral.toMap(); - if( peripherals.isEmpty() ) + // If we can no longer attach peripherals, then detach any + // which may have existed + if( !canAttachPeripheral() && peripheralAccessAllowed ) { - // If there are no peripherals then disable access and update the display state. peripheralAccessAllowed = false; + peripheral.detach(); + node.updatePeripherals( Collections.emptyMap() ); + setChanged(); updateBlockState(); } - - node.updatePeripherals( peripherals ); - } - - @Override - public void onNeighbourTileEntityChange( @Nonnull BlockPos neighbour ) - { - super.onNeighbourTileEntityChange( neighbour ); - if( !level.isClientSide && peripheralAccessAllowed ) - { - Direction facing = getDirection(); - if( getBlockPos().relative( facing ) - .equals( neighbour ) ) - { - refreshPeripheral(); - } - } - } - - @Override - public void blockTick() - { - if( getLevel().isClientSide ) - { - return; - } - - if( modem.getModemState() - .pollChanged() ) - { - updateBlockState(); - } - - if( !connectionsFormed ) - { - connectionsFormed = true; - - connectionsChanged(); - if( peripheralAccessAllowed ) - { - peripheral.attach( level, worldPosition, getDirection() ); - updateConnectedPeripherals(); - } - } } private void togglePeripheralAccess() @@ -334,10 +364,7 @@ private void togglePeripheralAccess() if( !peripheralAccessAllowed ) { peripheral.attach( level, getBlockPos(), getDirection() ); - if( !peripheral.hasPeripheral() ) - { - return; - } + if( !peripheral.hasPeripheral() ) return; peripheralAccessAllowed = true; node.updatePeripherals( peripheral.toMap() ); @@ -353,52 +380,17 @@ private void togglePeripheralAccess() updateBlockState(); } - @Nullable - private Direction getMaybeDirection() - { - return getBlockState().getValue( BlockCable.MODEM ).getFacing(); - } - - @Override - public void load( @Nonnull CompoundTag nbt ) - { - super.load( nbt ); - peripheralAccessAllowed = nbt.getBoolean( NBT_PERIPHERAL_ENABLED ); - peripheral.read( nbt, "" ); - } - - @Override - public void saveAdditional( CompoundTag nbt ) - { - nbt.putBoolean( NBT_PERIPHERAL_ENABLED, peripheralAccessAllowed ); - peripheral.write( nbt, "" ); - - super.saveAdditional( nbt ); - } - - @Override - public void setRemoved() - { - super.setRemoved(); - onRemove(); - } - - @Override - public void clearRemoved() - { - super.clearRemoved(); - TickScheduler.schedule( this ); - } - - @Override - public void setBlockState( BlockState state ) + private void updateConnectedPeripherals() { - super.setBlockState( state ); - if( state != null ) return; - if( !level.isClientSide ) + Map peripherals = peripheral.toMap(); + if( peripherals.isEmpty() ) { - level.scheduleTick( worldPosition, getBlockState().getBlock(), 0 ); + // If there are no peripherals then disable access and update the display state. + peripheralAccessAllowed = false; + updateBlockState(); } + + node.updatePeripherals( peripherals ); } public IWiredElement getElement( Direction facing ) @@ -413,33 +405,18 @@ public IPeripheral getPeripheral( Direction side ) return !destroyed && hasModem() && side == getDirection() ? modem : null; } - private class CableElement extends WiredModemElement + boolean hasCable() { - @Nonnull - @Override - public Level getLevel() - { - return TileCable.this.getLevel(); - } - - @Nonnull - @Override - public Vec3 getPosition() - { - BlockPos pos = getBlockPos(); - return new Vec3( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 ); - } + return getBlockState().getValue( BlockCable.CABLE ); + } - @Override - protected void attachPeripheral( String name, IPeripheral peripheral ) - { - modem.attachPeripheral( name, peripheral ); - } + public boolean hasModem() + { + return getBlockState().getValue( BlockCable.MODEM ) != CableModemVariant.None; + } - @Override - protected void detachPeripheral( String name ) - { - modem.detachPeripheral( name ); - } + private boolean canAttachPeripheral() + { + return hasCable() && hasModem(); } } 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 d5f67ab69..1df918c31 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 @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.peripheral.modem.wired; import com.google.common.base.Objects; @@ -32,6 +31,7 @@ import net.minecraft.world.phys.Vec3; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.util.*; import static dan200.computercraft.shared.peripheral.modem.wired.BlockWiredModemFull.MODEM_ON; @@ -40,14 +40,65 @@ public class TileWiredModemFull extends TileGeneric implements IPeripheralTile { private static final String NBT_PERIPHERAL_ENABLED = "PeripheralAccess"; + + private static final class FullElement extends WiredModemElement + { + private final TileWiredModemFull entity; + + private FullElement( TileWiredModemFull entity ) + { + this.entity = entity; + } + + @Override + protected void attachPeripheral( String name, IPeripheral peripheral ) + { + for( int i = 0; i < 6; i++ ) + { + WiredModemPeripheral modem = entity.modems[i]; + if( modem != null ) modem.attachPeripheral( name, peripheral ); + } + } + + @Override + protected void detachPeripheral( String name ) + { + for( int i = 0; i < 6; i++ ) + { + WiredModemPeripheral modem = entity.modems[i]; + if( modem != null ) modem.detachPeripheral( name ); + } + } + + @Nonnull + @Override + public Level getLevel() + { + return entity.getLevel(); + } + + @Nonnull + @Override + public Vec3 getPosition() + { + BlockPos pos = entity.getBlockPos(); + return new Vec3( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 ); + } + } + private final WiredModemPeripheral[] modems = new WiredModemPeripheral[6]; + + private boolean peripheralAccessAllowed = false; private final WiredModemLocalPeripheral[] peripherals = new WiredModemLocalPeripheral[6]; + + private boolean destroyed = false; + private boolean connectionsFormed = false; + private final ModemState modemState = new ModemState( () -> TickScheduler.schedule( this ) ); private final WiredModemElement element = new FullElement( this ); private final IWiredNode node = element.getNode(); - private boolean peripheralAccessAllowed = false; - private boolean destroyed = false; - private boolean connectionsFormed = false; + + private int invalidSides = 0; public TileWiredModemFull( BlockEntityType type, BlockPos pos, BlockState state ) { @@ -55,7 +106,16 @@ public TileWiredModemFull( BlockEntityType type, BlockPos po for( int i = 0; i < peripherals.length; i++ ) { Direction facing = Direction.from3DDataValue( i ); - peripherals[i] = new WiredModemLocalPeripheral(); + peripherals[i] = new WiredModemLocalPeripheral( () -> queueRefreshPeripheral( facing ) ); + } + } + + private void doRemove() + { + if( level == null || !level.isClientSide ) + { + node.remove(); + connectionsFormed = false; } } @@ -77,12 +137,44 @@ public void onChunkUnloaded() doRemove(); } - private void doRemove() + @Override + public void setRemoved() { - if( level == null || !level.isClientSide ) + super.setRemoved(); + doRemove(); + } + + @Override + public void onNeighbourChange( @Nonnull BlockPos neighbour ) + { + onNeighbourTileEntityChange( neighbour ); + } + + @Override + public void onNeighbourTileEntityChange( @Nonnull BlockPos neighbour ) + { + if( !level.isClientSide && peripheralAccessAllowed ) { - node.remove(); - connectionsFormed = false; + for( Direction facing : DirectionUtil.FACINGS ) + { + if( getBlockPos().relative( facing ).equals( neighbour ) ) queueRefreshPeripheral( facing ); + } + } + } + + private void queueRefreshPeripheral( @Nonnull Direction facing ) + { + if( invalidSides == 0 ) TickScheduler.schedule( this ); + invalidSides |= 1 << facing.ordinal(); + } + + private void refreshPeripheral( @Nonnull Direction facing ) + { + invalidSides &= ~(1 << facing.ordinal()); + WiredModemLocalPeripheral peripheral = peripherals[facing.ordinal()]; + if( level != null && !isRemoved() && peripheral.attach( level, getBlockPos(), facing ) ) + { + updateConnectedPeripherals(); } } @@ -90,10 +182,7 @@ private void doRemove() @Override public InteractionResult onActivate( Player player, InteractionHand hand, BlockHitResult hit ) { - if( getLevel().isClientSide ) - { - return InteractionResult.SUCCESS; - } + if( getLevel().isClientSide ) return InteractionResult.SUCCESS; // On server, we interacted if a peripheral was found Set oldPeriphNames = getConnectedPeripheralNames(); @@ -109,41 +198,70 @@ public InteractionResult onActivate( Player player, InteractionHand hand, BlockH return InteractionResult.SUCCESS; } + private static void sendPeripheralChanges( Player player, String kind, Collection peripherals ) + { + if( peripherals.isEmpty() ) return; + + List names = new ArrayList<>( peripherals ); + names.sort( Comparator.naturalOrder() ); + + TextComponent base = new TextComponent( "" ); + for( int i = 0; i < names.size(); i++ ) + { + if( i > 0 ) base.append( ", " ); + base.append( ChatHelpers.copy( names.get( i ) ) ); + } + + player.displayClientMessage( new TranslatableComponent( kind, base ), false ); + } + @Override - public void onNeighbourChange( @Nonnull BlockPos neighbour ) + public void load( @Nonnull CompoundTag nbt ) { - onNeighbourTileEntityChange( neighbour ); + super.load( nbt ); + peripheralAccessAllowed = nbt.getBoolean( NBT_PERIPHERAL_ENABLED ); + for( int i = 0; i < peripherals.length; i++ ) peripherals[i].read( nbt, Integer.toString( i ) ); } @Override - public void onNeighbourTileEntityChange( @Nonnull BlockPos neighbour ) + public void saveAdditional( CompoundTag nbt ) { - if( !level.isClientSide && peripheralAccessAllowed ) - { - for( Direction facing : DirectionUtil.FACINGS ) - { - if( getBlockPos().relative( facing ) - .equals( neighbour ) ) - { - refreshPeripheral( facing ); - } - } - } + nbt.putBoolean( NBT_PERIPHERAL_ENABLED, peripheralAccessAllowed ); + for( int i = 0; i < peripherals.length; i++ ) peripherals[i].write( nbt, Integer.toString( i ) ); + super.saveAdditional( nbt ); + } + + private void updateBlockState() + { + BlockState state = getBlockState(); + boolean modemOn = modemState.isOpen(), peripheralOn = peripheralAccessAllowed; + if( state.getValue( MODEM_ON ) == modemOn && state.getValue( PERIPHERAL_ON ) == peripheralOn ) return; + + getLevel().setBlockAndUpdate( getBlockPos(), state.setValue( MODEM_ON, modemOn ).setValue( PERIPHERAL_ON, peripheralOn ) ); + } + + @Override + public void clearRemoved() + { + super.clearRemoved(); // TODO: Replace with onLoad + TickScheduler.schedule( this ); } @Override public void blockTick() { - if( getLevel().isClientSide ) - { - return; - } + if( getLevel().isClientSide ) return; - if( modemState.pollChanged() ) + if( invalidSides != 0 ) { - updateBlockState(); + for( Direction direction : DirectionUtil.FACINGS ) + { + if( (invalidSides & (1 << direction.ordinal())) != 0 ) refreshPeripheral( direction ); + } } + if( modemState.pollChanged() ) updateBlockState(); + if( !connectionsFormed ) { connectionsFormed = true; @@ -162,101 +280,22 @@ public void blockTick() private void connectionsChanged() { - if( getLevel().isClientSide ) - { - return; - } + if( getLevel().isClientSide ) return; Level world = getLevel(); BlockPos current = getBlockPos(); for( Direction facing : DirectionUtil.FACINGS ) { BlockPos offset = current.relative( facing ); - if( !world.hasChunkAt( offset ) ) - { - continue; - } + if( !world.isLoaded( offset ) ) continue; IWiredElement element = ComputerCraftAPI.getWiredElementAt( world, offset, facing.getOpposite() ); - if( element == null ) - { - continue; - } + if( element == null ) continue; node.connectTo( element.getNode() ); } } - private void refreshPeripheral( @Nonnull Direction facing ) - { - WiredModemLocalPeripheral peripheral = peripherals[facing.ordinal()]; - if( level != null && !isRemoved() && peripheral.attach( level, getBlockPos(), facing ) ) - { - updateConnectedPeripherals(); - } - } - - private void updateConnectedPeripherals() - { - Map peripherals = getConnectedPeripherals(); - if( peripherals.isEmpty() ) - { - // If there are no peripherals then disable access and update the display state. - peripheralAccessAllowed = false; - updateBlockState(); - } - - node.updatePeripherals( peripherals ); - } - - private Map getConnectedPeripherals() - { - if( !peripheralAccessAllowed ) - { - return Collections.emptyMap(); - } - - Map peripherals = new HashMap<>( 6 ); - for( WiredModemLocalPeripheral peripheral : this.peripherals ) - { - peripheral.extendMap( peripherals ); - } - return peripherals; - } - - private void updateBlockState() - { - BlockState state = getBlockState(); - boolean modemOn = modemState.isOpen(), peripheralOn = peripheralAccessAllowed; - if( state.getValue( MODEM_ON ) == modemOn && state.getValue( PERIPHERAL_ON ) == peripheralOn ) - { - return; - } - - getLevel().setBlockAndUpdate( getBlockPos(), - state.setValue( MODEM_ON, modemOn ) - .setValue( PERIPHERAL_ON, peripheralOn ) ); - } - - private Set getConnectedPeripheralNames() - { - if( !peripheralAccessAllowed ) - { - return Collections.emptySet(); - } - - Set peripherals = new HashSet<>( 6 ); - for( WiredModemLocalPeripheral peripheral : this.peripherals ) - { - String name = peripheral.getConnectedName(); - if( name != null ) - { - peripherals.add( name ); - } - } - return peripherals; - } - private void togglePeripheralAccess() { if( !peripheralAccessAllowed ) @@ -269,10 +308,7 @@ private void togglePeripheralAccess() hasAny |= peripheral.hasPeripheral(); } - if( !hasAny ) - { - return; - } + if( !hasAny ) return; peripheralAccessAllowed = true; node.updatePeripherals( getConnectedPeripherals() ); @@ -281,74 +317,49 @@ private void togglePeripheralAccess() { peripheralAccessAllowed = false; - for( WiredModemLocalPeripheral peripheral : peripherals ) - { - peripheral.detach(); - } + for( WiredModemLocalPeripheral peripheral : peripherals ) peripheral.detach(); node.updatePeripherals( Collections.emptyMap() ); } updateBlockState(); } - private static void sendPeripheralChanges( Player player, String kind, Collection peripherals ) + private Set getConnectedPeripheralNames() { - if( peripherals.isEmpty() ) - { - return; - } - - List names = new ArrayList<>( peripherals ); - names.sort( Comparator.naturalOrder() ); + if( !peripheralAccessAllowed ) return Collections.emptySet(); - TextComponent base = new TextComponent( "" ); - for( int i = 0; i < names.size(); i++ ) + Set peripherals = new HashSet<>( 6 ); + for( WiredModemLocalPeripheral peripheral : this.peripherals ) { - if( i > 0 ) - { - base.append( ", " ); - } - base.append( ChatHelpers.copy( names.get( i ) ) ); + String name = peripheral.getConnectedName(); + if( name != null ) peripherals.add( name ); } - - player.displayClientMessage( new TranslatableComponent( kind, base ), false ); + return peripherals; } - @Override - public void load( @Nonnull CompoundTag nbt ) + private Map getConnectedPeripherals() { - super.load( nbt ); - peripheralAccessAllowed = nbt.getBoolean( NBT_PERIPHERAL_ENABLED ); - for( int i = 0; i < peripherals.length; i++ ) + if( !peripheralAccessAllowed ) { - peripherals[i].read( nbt, Integer.toString( i ) ); + return Collections.emptyMap(); } + + Map peripherals = new HashMap<>( 6 ); + for( WiredModemLocalPeripheral peripheral : this.peripherals ) peripheral.extendMap( peripherals ); + return peripherals; } - @Override - public void saveAdditional( CompoundTag nbt ) + private void updateConnectedPeripherals() { - nbt.putBoolean( NBT_PERIPHERAL_ENABLED, peripheralAccessAllowed ); - for( int i = 0; i < peripherals.length; i++ ) + Map peripherals = getConnectedPeripherals(); + if( peripherals.isEmpty() ) { - peripherals[i].write( nbt, Integer.toString( i ) ); + // If there are no peripherals then disable access and update the display state. + peripheralAccessAllowed = false; + updateBlockState(); } - super.saveAdditional( nbt ); - } - - @Override - public void setRemoved() - { - super.setRemoved(); - doRemove(); - } - - @Override - public void clearRemoved() - { - super.clearRemoved(); - TickScheduler.schedule( this ); + node.updatePeripherals( peripherals ); } public IWiredElement getElement() @@ -356,15 +367,12 @@ public IWiredElement getElement() return element; } - @Nonnull + @Nullable @Override - public IPeripheral getPeripheral( Direction side ) + public IPeripheral getPeripheral( @Nonnull Direction side ) { WiredModemPeripheral peripheral = modems[side.ordinal()]; - if( peripheral != null ) - { - return peripheral; - } + if( peripheral != null ) return peripheral; WiredModemLocalPeripheral localPeripheral = peripherals[side.ordinal()]; return modems[side.ordinal()] = new WiredModemPeripheral( modemState, element ) @@ -392,55 +400,4 @@ public Object getTarget() } }; } - - private static final class FullElement extends WiredModemElement - { - private final TileWiredModemFull entity; - - private FullElement( TileWiredModemFull entity ) - { - this.entity = entity; - } - - @Override - protected void detachPeripheral( String name ) - { - for( int i = 0; i < 6; i++ ) - { - WiredModemPeripheral modem = entity.modems[i]; - if( modem != null ) - { - modem.detachPeripheral( name ); - } - } - } - - @Override - protected void attachPeripheral( String name, IPeripheral peripheral ) - { - for( int i = 0; i < 6; i++ ) - { - WiredModemPeripheral modem = entity.modems[i]; - if( modem != null ) - { - modem.attachPeripheral( name, peripheral ); - } - } - } - - @Nonnull - @Override - public Level getLevel() - { - return entity.getLevel(); - } - - @Nonnull - @Override - public Vec3 getPosition() - { - BlockPos pos = entity.getBlockPos(); - return new Vec3( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 ); - } - } } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/WiredModemElement.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/WiredModemElement.java index a5b3d3e85..eb5109803 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/WiredModemElement.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/WiredModemElement.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.peripheral.modem.wired; import dan200.computercraft.api.network.wired.IWiredElement; diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/WiredModemLocalPeripheral.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/WiredModemLocalPeripheral.java index 7f52306bf..54ce72927 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/WiredModemLocalPeripheral.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/wired/WiredModemLocalPeripheral.java @@ -3,17 +3,16 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.peripheral.modem.wired; import dan200.computercraft.api.peripheral.IPeripheral; -import dan200.computercraft.shared.ComputerCraftRegistry; import dan200.computercraft.shared.Peripherals; +import dan200.computercraft.shared.Registry; import dan200.computercraft.shared.util.IDAssigner; -import dan200.computercraft.shared.util.NBTUtil; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.Tag; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; @@ -21,21 +20,29 @@ import javax.annotation.Nullable; import java.util.Collections; import java.util.Map; +import java.util.function.Consumer; /** * Represents a local peripheral exposed on the wired network. * - * This is responsible for getting the peripheral in world, tracking id and type and determining whether it has changed. + * This is responsible for getting the peripheral in world, tracking id and type and determining whether + * it has changed. */ public final class WiredModemLocalPeripheral { private static final String NBT_PERIPHERAL_TYPE = "PeripheralType"; private static final String NBT_PERIPHERAL_ID = "PeripheralId"; - private int id; + private int id = -1; private String type; private IPeripheral peripheral; + private final Consumer invalidate; + + public WiredModemLocalPeripheral( @Nonnull Runnable invalidate ) + { + this.invalidate = x -> invalidate.run(); + } /** * Attach a new peripheral from the world. @@ -74,22 +81,6 @@ else if( id < 0 || !type.equals( this.type ) ) } } - @Nullable - private IPeripheral getPeripheralFrom( Level world, BlockPos pos, Direction direction ) - { - BlockPos offset = pos.relative( direction ); - - Block block = world.getBlockState( offset ) - .getBlock(); - if( block == ComputerCraftRegistry.ModBlocks.WIRED_MODEM_FULL || block == ComputerCraftRegistry.ModBlocks.CABLE ) - { - return null; - } - - IPeripheral peripheral = Peripherals.getPeripheral( world, offset, direction.getOpposite() ); - return peripheral instanceof WiredModemPeripheral ? null : peripheral; - } - /** * Detach the current peripheral. * @@ -97,10 +88,7 @@ private IPeripheral getPeripheralFrom( Level world, BlockPos pos, Direction dire */ public boolean detach() { - if( peripheral == null ) - { - return false; - } + if( peripheral == null ) return false; peripheral = null; return true; } @@ -124,33 +112,40 @@ public boolean hasPeripheral() public void extendMap( @Nonnull Map peripherals ) { - if( peripheral != null ) - { - peripherals.put( type + "_" + id, peripheral ); - } + if( peripheral != null ) peripherals.put( type + "_" + id, peripheral ); } public Map toMap() { - return peripheral == null ? Collections.emptyMap() : Collections.singletonMap( type + "_" + id, peripheral ); + return peripheral == null + ? Collections.emptyMap() + : Collections.singletonMap( type + "_" + id, peripheral ); } public void write( @Nonnull CompoundTag tag, @Nonnull String suffix ) { - if( id >= 0 ) - { - tag.putInt( NBT_PERIPHERAL_ID + suffix, id ); - } - if( type != null ) - { - tag.putString( NBT_PERIPHERAL_TYPE + suffix, type ); - } + if( id >= 0 ) tag.putInt( NBT_PERIPHERAL_ID + suffix, id ); + if( type != null ) tag.putString( NBT_PERIPHERAL_TYPE + suffix, type ); } public void read( @Nonnull CompoundTag tag, @Nonnull String suffix ) { - id = tag.contains( NBT_PERIPHERAL_ID + suffix, NBTUtil.TAG_ANY_NUMERIC ) ? tag.getInt( NBT_PERIPHERAL_ID + suffix ) : -1; + id = tag.contains( NBT_PERIPHERAL_ID + suffix, Tag.TAG_ANY_NUMERIC ) + ? tag.getInt( NBT_PERIPHERAL_ID + suffix ) : -1; + + type = tag.contains( NBT_PERIPHERAL_TYPE + suffix, Tag.TAG_STRING ) + ? tag.getString( NBT_PERIPHERAL_TYPE + suffix ) : null; + } - type = tag.contains( NBT_PERIPHERAL_TYPE + suffix, NBTUtil.TAG_STRING ) ? tag.getString( NBT_PERIPHERAL_TYPE + suffix ) : null; + @Nullable + private IPeripheral getPeripheralFrom( Level world, BlockPos pos, Direction direction ) + { + BlockPos offset = pos.relative( direction ); + + Block block = world.getBlockState( offset ).getBlock(); + if( block == Registry.ModBlocks.WIRED_MODEM_FULL || block == Registry.ModBlocks.CABLE ) return null; + + IPeripheral peripheral = Peripherals.getPeripheral( world, offset, direction.getOpposite() ); + return peripheral instanceof WiredModemPeripheral ? null : peripheral; } } 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 6cf699655..5ff939590 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 @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.peripheral.modem.wired; import com.google.common.collect.ImmutableMap; @@ -22,6 +21,7 @@ import dan200.computercraft.core.asm.PeripheralMethod; import dan200.computercraft.shared.peripheral.modem.ModemPeripheral; import dan200.computercraft.shared.peripheral.modem.ModemState; +import dan200.computercraft.shared.util.LuaUtil; import net.minecraft.world.level.Level; import javax.annotation.Nonnull; @@ -42,12 +42,6 @@ public WiredModemPeripheral( ModemState state, WiredModemElement modem ) this.modem = modem; } - @Override - public double getRange() - { - return 256.0; - } - //region IPacketSender implementation @Override public boolean isInterdimensional() @@ -56,68 +50,15 @@ public boolean isInterdimensional() } @Override - protected IPacketNetwork getNetwork() - { - return modem.getNode(); - } - - @Override - public void attach( @Nonnull IComputerAccess computer ) + public double getRange() { - super.attach( computer ); - - ConcurrentMap wrappers; - synchronized( peripheralWrappers ) - { - wrappers = peripheralWrappers.get( computer ); - if( wrappers == null ) - { - peripheralWrappers.put( computer, wrappers = new ConcurrentHashMap<>() ); - } - } - - synchronized( modem.getRemotePeripherals() ) - { - for( Map.Entry entry : modem.getRemotePeripherals() - .entrySet() ) - { - attachPeripheralImpl( computer, wrappers, entry.getKey(), entry.getValue() ); - } - } + return 256.0; } @Override - public void detach( @Nonnull IComputerAccess computer ) - { - Map wrappers; - synchronized( peripheralWrappers ) - { - wrappers = peripheralWrappers.remove( computer ); - } - if( wrappers != null ) - { - for( RemotePeripheralWrapper wrapper : wrappers.values() ) - { - wrapper.detach(); - } - wrappers.clear(); - } - - super.detach( computer ); - } - //endregion - - //region Peripheral methods - - private void attachPeripheralImpl( IComputerAccess computer, ConcurrentMap peripherals, String periphName, - IPeripheral peripheral ) + protected IPacketNetwork getNetwork() { - if( !peripherals.containsKey( periphName ) && !periphName.equals( getLocalPeripheral().getConnectedName() ) ) - { - RemotePeripheralWrapper wrapper = new RemotePeripheralWrapper( modem, peripheral, computer, periphName ); - peripherals.put( periphName, wrapper ); - wrapper.attach(); - } + return modem.getNode(); } @Nonnull @@ -127,10 +68,17 @@ public Level getLevel() return modem.getLevel(); } + @Nonnull + protected abstract WiredModemLocalPeripheral getLocalPeripheral(); + //endregion + + //region Peripheral methods + /** * List all remote peripherals on the wired network. * - * If this computer is attached to the network, it _will not_ be included in this list. + * If this computer is attached to the network, it _will not_ be included in + * this list. * *
Important: This function only appears on wired modems. Check {@link #isWireless} * returns false before calling it.
@@ -144,14 +92,6 @@ public final Collection getNamesRemote( IComputerAccess computer ) return getWrappers( computer ).keySet(); } - private ConcurrentMap getWrappers( IComputerAccess computer ) - { - synchronized( peripheralWrappers ) - { - return peripheralWrappers.get( computer ); - } - } - /** * Determine if a peripheral is available on this wired network. * @@ -169,12 +109,6 @@ public final boolean isPresentRemote( IComputerAccess computer, String name ) return getWrapper( computer, name ) != null; } - private RemotePeripheralWrapper getWrapper( IComputerAccess computer, String remoteName ) - { - ConcurrentMap wrappers = getWrappers( computer ); - return wrappers == null ? null : wrappers.get( remoteName ); - } - /** * Get the type of a peripheral is available on this wired network. * @@ -185,13 +119,35 @@ private RemotePeripheralWrapper getWrapper( IComputerAccess computer, String rem * @param name The peripheral's name. * @return The peripheral's name. * @cc.treturn string|nil The peripheral's type, or {@code nil} if it is not present. + * @cc.changed 1.99 Peripherals can have multiple types - this function returns multiple values. * @see PeripheralAPI#getType */ @LuaFunction public final Object[] getTypeRemote( IComputerAccess computer, String name ) { RemotePeripheralWrapper wrapper = getWrapper( computer, name ); - return wrapper != null ? new Object[] { wrapper.getType() } : null; + return wrapper == null ? null : LuaUtil.consArray( wrapper.getType(), wrapper.getAdditionalTypes() ); + } + + /** + * Check a peripheral is of a particular type. + * + *
Important: This function only appears on wired modems. Check {@link #isWireless} + * returns false before calling it.
+ * + * @param computer The calling computer. + * @param name The peripheral's name. + * @param type The type to check. + * @return The peripheral's name. + * @cc.treturn boolean|nil If a peripheral has a particular type, or {@literal nil} if it is not present. + * @cc.since 1.99 + * @see PeripheralAPI#getType + */ + @LuaFunction + public final Object[] hasTypeRemote( IComputerAccess computer, String name, String type ) + { + RemotePeripheralWrapper wrapper = getWrapper( computer, name ); + return wrapper == null ? null : new Object[] { wrapper.getType().equals( type ) || wrapper.getAdditionalTypes().contains( getType() ) }; } /** @@ -210,10 +166,7 @@ public final Object[] getTypeRemote( IComputerAccess computer, String name ) public final Object[] getMethodsRemote( IComputerAccess computer, String name ) { RemotePeripheralWrapper wrapper = getWrapper( computer, name ); - if( wrapper == null ) - { - return null; - } + if( wrapper == null ) return null; return new Object[] { wrapper.getMethodNames() }; } @@ -241,17 +194,14 @@ public final MethodResult callRemote( IComputerAccess computer, ILuaContext cont String remoteName = arguments.getString( 0 ); String methodName = arguments.getString( 1 ); RemotePeripheralWrapper wrapper = getWrapper( computer, remoteName ); - if( wrapper == null ) - { - throw new LuaException( "No peripheral: " + remoteName ); - } + if( wrapper == null ) throw new LuaException( "No peripheral: " + remoteName ); return wrapper.callMethod( context, methodName, arguments.drop( 2 ) ); } - //endregion /** - * Returns the network name of the current computer, if the modem is on. This may be used by other computers on the network to wrap this computer as a + * Returns the network name of the current computer, if the modem is on. This + * may be used by other computers on the network to wrap this computer as a * peripheral. * *
Important: This function only appears on wired modems. Check {@link #isWireless} @@ -259,6 +209,7 @@ public final MethodResult callRemote( IComputerAccess computer, ILuaContext cont * * @return The current computer's name. * @cc.treturn string|nil The current computer's name on the wired network. + * @cc.since 1.80pr1.7 */ @LuaFunction public final Object[] getNameLocal() @@ -267,19 +218,54 @@ public final Object[] getNameLocal() return local == null ? null : new Object[] { local }; } - @Nonnull - protected abstract WiredModemLocalPeripheral getLocalPeripheral(); + @Override + public void attach( @Nonnull IComputerAccess computer ) + { + super.attach( computer ); + + ConcurrentMap wrappers; + synchronized( peripheralWrappers ) + { + wrappers = peripheralWrappers.get( computer ); + if( wrappers == null ) peripheralWrappers.put( computer, wrappers = new ConcurrentHashMap<>() ); + } + + synchronized( modem.getRemotePeripherals() ) + { + for( Map.Entry entry : modem.getRemotePeripherals().entrySet() ) + { + attachPeripheralImpl( computer, wrappers, entry.getKey(), entry.getValue() ); + } + } + } + + @Override + public void detach( @Nonnull IComputerAccess computer ) + { + Map wrappers; + synchronized( peripheralWrappers ) + { + wrappers = peripheralWrappers.remove( computer ); + } + if( wrappers != null ) + { + for( RemotePeripheralWrapper wrapper : wrappers.values() ) wrapper.detach(); + wrappers.clear(); + } + + super.detach( computer ); + } @Override public boolean equals( IPeripheral other ) { - if( other instanceof WiredModemPeripheral ) + if( other instanceof WiredModemPeripheral otherModem ) { - WiredModemPeripheral otherModem = (WiredModemPeripheral) other; return otherModem.modem == modem; } return false; } + //endregion @Nonnull @Override @@ -306,15 +292,36 @@ public void detachPeripheral( String name ) for( ConcurrentMap wrappers : peripheralWrappers.values() ) { RemotePeripheralWrapper wrapper = wrappers.remove( name ); - if( wrapper != null ) - { - wrapper.detach(); - } + if( wrapper != null ) wrapper.detach(); } } } + private void attachPeripheralImpl( IComputerAccess computer, ConcurrentMap peripherals, String periphName, IPeripheral peripheral ) + { + if( !peripherals.containsKey( periphName ) && !periphName.equals( getLocalPeripheral().getConnectedName() ) ) + { + RemotePeripheralWrapper wrapper = new RemotePeripheralWrapper( modem, peripheral, computer, periphName ); + peripherals.put( periphName, wrapper ); + wrapper.attach(); + } + } + + private ConcurrentMap getWrappers( IComputerAccess computer ) + { + synchronized( peripheralWrappers ) + { + return peripheralWrappers.get( computer ); + } + } + + private RemotePeripheralWrapper getWrapper( IComputerAccess computer, String remoteName ) + { + ConcurrentMap wrappers = getWrappers( computer ); + return wrappers == null ? null : wrappers.get( remoteName ); + } + private static class RemotePeripheralWrapper implements IComputerAccess { private final WiredModemElement element; @@ -323,6 +330,7 @@ private static class RemotePeripheralWrapper implements IComputerAccess private final String name; private final String type; + private final Set additionalTypes; private final Map methodMap; private volatile boolean attached; @@ -336,6 +344,7 @@ private static class RemotePeripheralWrapper implements IComputerAccess this.name = name; type = Objects.requireNonNull( peripheral.getType(), "Peripheral type cannot be null" ); + additionalTypes = peripheral.getAdditionalTypes(); methodMap = PeripheralAPI.getMethods( peripheral ); } @@ -369,6 +378,11 @@ public String getType() return type; } + public Set getAdditionalTypes() + { + return additionalTypes; + } + public Collection getMethodNames() { return methodMap.keySet(); @@ -377,10 +391,7 @@ public Collection getMethodNames() public MethodResult callMethod( ILuaContext context, String methodName, IArguments arguments ) throws LuaException { PeripheralMethod method = methodMap.get( methodName ); - if( method == null ) - { - throw new LuaException( "No such method " + methodName ); - } + if( method == null ) throw new LuaException( "No such method " + methodName ); return method.apply( peripheral, context, this, arguments ); } @@ -404,14 +415,6 @@ public synchronized String mount( @Nonnull String desiredLocation, @Nonnull IMou return mounted; } - @Nonnull - @Override - public String getAttachmentName() - { - if( !attached ) throw new NotAttachedException(); - return name; - } - @Override public synchronized String mountWritable( @Nonnull String desiredLocation, @Nonnull IWritableMount mount ) { @@ -452,6 +455,22 @@ public void queueEvent( @Nonnull String event, Object... arguments ) computer.queueEvent( event, arguments ); } + @Nonnull + @Override + public IWorkMonitor getMainThreadMonitor() + { + if( !attached ) throw new NotAttachedException(); + return computer.getMainThreadMonitor(); + } + + @Nonnull + @Override + public String getAttachmentName() + { + if( !attached ) throw new NotAttachedException(); + return name; + } + @Nonnull @Override public Map getAvailablePeripherals() @@ -470,17 +489,8 @@ public IPeripheral getAvailablePeripheral( @Nonnull String name ) if( !attached ) throw new NotAttachedException(); synchronized( element.getRemotePeripherals() ) { - return element.getRemotePeripherals() - .get( name ); + return element.getRemotePeripherals().get( name ); } } - - @Nonnull - @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 b3a03e8a6..73b2db77c 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 @@ -3,13 +3,11 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - 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 dan200.computercraft.shared.util.WaterloggableHelpers; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.item.context.BlockPlaceContext; @@ -18,7 +16,6 @@ import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.SimpleWaterloggedBlock; -import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.StateDefinition; @@ -31,90 +28,72 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.util.function.Supplier; -import static dan200.computercraft.shared.util.WaterloggableHelpers.*; +import static dan200.computercraft.shared.util.WaterloggableHelpers.WATERLOGGED; +import static dan200.computercraft.shared.util.WaterloggableHelpers.getFluidStateForPlacement; public class BlockWirelessModem extends BlockGeneric implements SimpleWaterloggedBlock { public static final DirectionProperty FACING = BlockStateProperties.FACING; public static final BooleanProperty ON = BooleanProperty.create( "on" ); - private final ComputerFamily family; - - public BlockWirelessModem( Properties settings, BlockEntityType type, ComputerFamily family ) + public BlockWirelessModem( Properties settings, Supplier> type ) { super( settings, type ); - this.family = family; registerDefaultState( getStateDefinition().any() .setValue( FACING, Direction.NORTH ) .setValue( ON, false ) .setValue( WATERLOGGED, false ) ); } - @Nonnull @Override - @Deprecated - public BlockState updateShape( @Nonnull BlockState state, @Nonnull Direction side, @Nonnull BlockState otherState, - @Nonnull LevelAccessor world, @Nonnull BlockPos pos, @Nonnull BlockPos otherPos ) + protected void createBlockStateDefinition( StateDefinition.Builder builder ) { - updateWaterloggedPostPlacement( state, world, pos ); - return side == state.getValue( FACING ) && !state.canSurvive( world, pos ) ? state.getFluidState() - .createLegacyBlock() : state; + builder.add( FACING, ON, WATERLOGGED ); } @Nonnull @Override @Deprecated - public FluidState getFluidState( @Nonnull BlockState state ) + public VoxelShape getShape( BlockState blockState, @Nonnull BlockGetter blockView, @Nonnull BlockPos blockPos, @Nonnull CollisionContext context ) { - return getWaterloggedFluidState( state ); + return ModemShapes.getBounds( blockState.getValue( FACING ) ); } + @Nonnull @Override @Deprecated - public boolean canSurvive( BlockState state, @Nonnull LevelReader world, BlockPos pos ) + public FluidState getFluidState( @Nonnull BlockState state ) { - Direction facing = state.getValue( FACING ); - return canSupportCenter( world, pos.relative( facing ), facing.getOpposite() ); + return WaterloggableHelpers.getFluidState( state ); } @Nonnull @Override @Deprecated - public VoxelShape getShape( BlockState blockState, @Nonnull BlockGetter blockView, @Nonnull BlockPos blockPos, @Nonnull CollisionContext context ) - { - return ModemShapes.getBounds( blockState.getValue( FACING ) ); - } - - @Nullable - @Override - public BlockState getStateForPlacement( BlockPlaceContext placement ) + public BlockState updateShape( @Nonnull BlockState state, @Nonnull Direction side, @Nonnull BlockState otherState, @Nonnull LevelAccessor world, @Nonnull BlockPos pos, @Nonnull BlockPos otherPos ) { - return defaultBlockState().setValue( FACING, - placement.getClickedFace() - .getOpposite() ) - .setValue( WATERLOGGED, getWaterloggedStateForPlacement( placement ) ); + WaterloggableHelpers.updateShape( state, world, pos ); + return side == state.getValue( FACING ) && !state.canSurvive( world, pos ) + ? state.getFluidState().createLegacyBlock() + : state; } @Override - protected void createBlockStateDefinition( StateDefinition.Builder builder ) - { - builder.add( FACING, ON, WATERLOGGED ); - } - - public BlockEntityType getTypeByFamily( ComputerFamily family ) + @Deprecated + public boolean canSurvive( BlockState state, @Nonnull LevelReader world, BlockPos pos ) { - return switch( family ) - { - case ADVANCED -> ComputerCraftRegistry.ModTiles.WIRELESS_MODEM_ADVANCED; - default -> ComputerCraftRegistry.ModTiles.WIRELESS_MODEM_NORMAL; - }; + Direction facing = state.getValue( FACING ); + return canSupportCenter( world, pos.relative( facing ), facing.getOpposite() ); } @Nullable @Override - public BlockEntity newBlockEntity( BlockPos pos, BlockState state ) + public BlockState getStateForPlacement( BlockPlaceContext placement ) { - return new TileWirelessModem( getTypeByFamily( family ), family == ComputerFamily.ADVANCED, pos, state ); + return defaultBlockState() + .setValue( FACING, placement.getClickedFace().getOpposite() ) + .setValue( WATERLOGGED, getFluidStateForPlacement( placement ) ); } } 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 79719198d..40f10d42f 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 @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.peripheral.modem.wireless; import dan200.computercraft.api.peripheral.IPeripheral; @@ -20,16 +19,55 @@ import net.minecraft.world.phys.Vec3; import javax.annotation.Nonnull; +import javax.annotation.Nullable; public class TileWirelessModem extends TileGeneric implements IPeripheralTile { + private static class Peripheral extends WirelessModemPeripheral + { + private final TileWirelessModem entity; + + Peripheral( TileWirelessModem entity ) + { + super( new ModemState( () -> TickScheduler.schedule( entity ) ), entity.advanced ); + this.entity = entity; + } + + @Nonnull + @Override + public Level getLevel() + { + return entity.getLevel(); + } + + @Nonnull + @Override + public Vec3 getPosition() + { + BlockPos pos = entity.getBlockPos().relative( entity.getDirection() ); + return new Vec3( pos.getX(), pos.getY(), pos.getZ() ); + } + + @Override + public boolean equals( IPeripheral other ) + { + return this == other || (other instanceof Peripheral && entity == ((Peripheral) other).entity); + } + + @Nonnull + @Override + public Object getTarget() + { + return entity; + } + } + private final boolean advanced; + private final ModemPeripheral modem; - private boolean hasModemDirection = false; - private Direction modemDirection = Direction.DOWN; private boolean destroyed = false; - public TileWirelessModem( BlockEntityType type, boolean advanced, BlockPos pos, BlockState state ) + public TileWirelessModem( BlockEntityType type, BlockPos pos, BlockState state, boolean advanced ) { super( type, pos, state ); this.advanced = advanced; @@ -39,19 +77,10 @@ public TileWirelessModem( BlockEntityType type, boo @Override public void clearRemoved() { - super.clearRemoved(); + super.clearRemoved(); // TODO: Replace with onLoad TickScheduler.schedule( this ); } - @Override - public void setBlockState( BlockState state ) - { - super.setBlockState( state ); - if( state != null ) return; - hasModemDirection = false; - level.scheduleTick( getBlockPos(), getBlockState().getBlock(), 0 ); - } - @Override public void destroy() { @@ -65,33 +94,18 @@ public void destroy() @Override public void blockTick() { - Direction currentDirection = modemDirection; - refreshDirection(); - // Invalidate the capability if the direction has changed. I'm not 100% happy with this implementation - // - ideally we'd do it within refreshDirection or updateContainingBlockInfo, but this seems the _safest_ - // place. - if( modem.getModemState() - .pollChanged() ) - { - updateBlockState(); - } + if( modem.getModemState().pollChanged() ) updateBlockState(); } - private void refreshDirection() + @Nonnull + private Direction getDirection() { - if( hasModemDirection ) - { - return; - } - - hasModemDirection = true; - modemDirection = getBlockState().getValue( BlockWirelessModem.FACING ); + return getBlockState().getValue( BlockWirelessModem.FACING ); } private void updateBlockState() { - boolean on = modem.getModemState() - .isOpen(); + boolean on = modem.getModemState().isOpen(); BlockState state = getBlockState(); if( state.getValue( BlockWirelessModem.ON ) != on ) { @@ -99,51 +113,11 @@ private void updateBlockState() } } - @Nonnull + @Nullable @Override public IPeripheral getPeripheral( Direction side ) { - refreshDirection(); - return side == modemDirection ? modem : null; - } - - private static class Peripheral extends WirelessModemPeripheral - { - private final TileWirelessModem entity; - - Peripheral( TileWirelessModem entity ) - { - super( new ModemState( () -> TickScheduler.schedule( entity ) ), entity.advanced ); - this.entity = entity; - } - - @Nonnull - @Override - public Level getLevel() - { - return entity.getLevel(); - } - - @Nonnull - @Override - public Vec3 getPosition() - { - BlockPos pos = entity.getBlockPos() - .relative( entity.modemDirection ); - return new Vec3( pos.getX(), pos.getY(), pos.getZ() ); - } - - @Nonnull - @Override - public Object getTarget() - { - return entity; - } - - @Override - public boolean equals( IPeripheral other ) - { - return this == other || (other instanceof Peripheral && entity == ((Peripheral) other).entity); - } + if( side != null && getDirection() != side ) return null; + return modem; } } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/modem/wireless/WirelessModemPeripheral.java b/src/main/java/dan200/computercraft/shared/peripheral/modem/wireless/WirelessModemPeripheral.java index 07f685542..a62ee8734 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/modem/wireless/WirelessModemPeripheral.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/modem/wireless/WirelessModemPeripheral.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.peripheral.modem.wireless; import dan200.computercraft.ComputerCraft; @@ -15,6 +14,9 @@ public abstract class WirelessModemPeripheral extends ModemPeripheral { + public static final String NORMAL_ADJECTIVE = "upgrade.computercraft.wireless_modem_normal.adjective"; + public static final String ADVANCED_ADJECTIVE = "upgrade.computercraft.wireless_modem_advanced.adjective"; + private final boolean advanced; public WirelessModemPeripheral( ModemState state, boolean advanced ) @@ -23,6 +25,12 @@ public WirelessModemPeripheral( ModemState state, boolean advanced ) this.advanced = advanced; } + @Override + public boolean isInterdimensional() + { + return advanced; + } + @Override public double getRange() { @@ -45,7 +53,7 @@ public double getRange() } if( position.y > 96.0 && maxRange > minRange ) { - return minRange + (position.y - 96.0) * ((maxRange - minRange) / ((world.getHeight() - 1) - 96.0)); + return minRange + (position.y - 96.0) * ((maxRange - minRange) / ((world.getMaxBuildHeight() - 1) - 96.0)); } return minRange; } @@ -53,12 +61,6 @@ public double getRange() } } - @Override - public boolean isInterdimensional() - { - return advanced; - } - @Override protected IPacketNetwork getNetwork() { 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 b760dfce8..121817c83 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/monitor/BlockMonitor.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/monitor/BlockMonitor.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.peripheral.monitor; import dan200.computercraft.api.turtle.FakePlayer; -import dan200.computercraft.shared.ComputerCraftRegistry; import dan200.computercraft.shared.common.BlockGeneric; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -26,21 +24,20 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.util.function.Supplier; public class BlockMonitor extends BlockGeneric { - public static final DirectionProperty ORIENTATION = DirectionProperty.create( "orientation", Direction.UP, Direction.DOWN, Direction.NORTH ); + public static final DirectionProperty ORIENTATION = DirectionProperty.create( "orientation", + Direction.UP, Direction.DOWN, Direction.NORTH ); public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING; - static final EnumProperty STATE = EnumProperty.create( "state", MonitorEdgeState.class ); - - public boolean advanced; + public static final EnumProperty STATE = EnumProperty.create( "state", MonitorEdgeState.class ); - public BlockMonitor( Properties settings, BlockEntityType type, boolean advanced ) + public BlockMonitor( Properties settings, Supplier> type ) { super( settings, type ); - this.advanced = advanced; // TODO: Test underwater - do we need isSolid at all? registerDefaultState( getStateDefinition().any() .setValue( ORIENTATION, Direction.NORTH ) @@ -48,6 +45,12 @@ public BlockMonitor( Properties settings, BlockEntityType .setValue( STATE, MonitorEdgeState.NONE ) ); } + @Override + protected void createBlockStateDefinition( StateDefinition.Builder builder ) + { + builder.add( ORIENTATION, FACING, STATE ); + } + @Override @Nullable public BlockState getStateForPlacement( BlockPlaceContext context ) @@ -69,15 +72,13 @@ else if( pitch < -66.5f ) orientation = Direction.NORTH; } - return defaultBlockState().setValue( FACING, - context.getHorizontalDirection() - .getOpposite() ) + return defaultBlockState() + .setValue( FACING, context.getHorizontalDirection().getOpposite() ) .setValue( ORIENTATION, orientation ); } @Override - public void setPlacedBy( @Nonnull Level world, @Nonnull BlockPos pos, @Nonnull BlockState blockState, @Nullable LivingEntity livingEntity, - @Nonnull ItemStack itemStack ) + public void setPlacedBy( @Nonnull Level world, @Nonnull BlockPos pos, @Nonnull BlockState blockState, @Nullable LivingEntity livingEntity, @Nonnull ItemStack itemStack ) { super.setPlacedBy( world, pos, blockState, livingEntity, itemStack ); @@ -91,20 +92,7 @@ public void setPlacedBy( @Nonnull Level world, @Nonnull BlockPos pos, @Nonnull B return; } - monitor.updateNeighbors(); + monitor.expand(); } } - - @Override - protected void createBlockStateDefinition( StateDefinition.Builder builder ) - { - builder.add( ORIENTATION, FACING, STATE ); - } - - @Nullable - @Override - public BlockEntity newBlockEntity( 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 12eef839f..e962f6c96 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/monitor/ClientMonitor.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/monitor/ClientMonitor.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.peripheral.monitor; import com.mojang.blaze3d.platform.GlStateManager; @@ -22,7 +21,6 @@ import java.util.Iterator; import java.util.Set; -@Environment( EnvType.CLIENT ) public final class ClientMonitor extends ClientTerminal { private static final Set allMonitors = new HashSet<>(); @@ -42,21 +40,6 @@ public ClientMonitor( boolean colour, TileMonitor origin ) this.origin = origin; } - @Environment( EnvType.CLIENT ) - public static void destroyAll() - { - synchronized( allMonitors ) - { - for( Iterator iterator = allMonitors.iterator(); iterator.hasNext(); ) - { - ClientMonitor monitor = iterator.next(); - monitor.deleteBuffers(); - - iterator.remove(); - } - } - } - public TileMonitor getOrigin() { return origin; @@ -66,7 +49,8 @@ public TileMonitor getOrigin() * Create the appropriate buffer if needed. * * @param renderer The renderer to use. This can be fetched from {@link MonitorRenderer#current()}. - * @return If a buffer was created. This will return {@code false} if we already have an appropriate buffer, or this mode does not require one. + * @return If a buffer was created. This will return {@code false} if we already have an appropriate buffer, + * or this mode does not require one. */ @Environment( EnvType.CLIENT ) public boolean createBuffer( MonitorRenderer renderer ) @@ -74,10 +58,8 @@ public boolean createBuffer( MonitorRenderer renderer ) switch( renderer ) { case TBO: - if( tboBuffer != 0 ) - { - return false; - } + { + if( tboBuffer != 0 ) return false; deleteBuffers(); @@ -93,12 +75,10 @@ public boolean createBuffer( MonitorRenderer renderer ) addMonitor(); return true; + } case VBO: - if( buffer != null ) - { - return false; - } + if( buffer != null ) return false; deleteBuffers(); buffer = new VertexBuffer(); @@ -110,6 +90,14 @@ public boolean createBuffer( MonitorRenderer renderer ) } } + private void addMonitor() + { + synchronized( allMonitors ) + { + allMonitors.add( this ); + } + } + private void deleteBuffers() { @@ -132,14 +120,6 @@ private void deleteBuffers() } } - private void addMonitor() - { - synchronized( allMonitors ) - { - allMonitors.add( this ); - } - } - @Environment( EnvType.CLIENT ) public void destroy() { @@ -153,4 +133,19 @@ public void destroy() deleteBuffers(); } } + + @Environment( EnvType.CLIENT ) + public static void destroyAll() + { + synchronized( allMonitors ) + { + for( Iterator iterator = allMonitors.iterator(); iterator.hasNext(); ) + { + ClientMonitor monitor = iterator.next(); + monitor.deleteBuffers(); + + iterator.remove(); + } + } + } } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/monitor/Expander.java b/src/main/java/dan200/computercraft/shared/peripheral/monitor/Expander.java new file mode 100644 index 000000000..21290ccfb --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/peripheral/monitor/Expander.java @@ -0,0 +1,108 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.shared.peripheral.monitor; + +import dan200.computercraft.ComputerCraft; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.BlockEntity; + +import java.util.Objects; + +/** + * Expands a monitor into available space. This tries to expand in each direction until a fixed point is reached. + */ +class Expander +{ + private final Level level; + private final Direction down; + private final Direction right; + + private TileMonitor origin; + private int width; + private int height; + + Expander( TileMonitor origin ) + { + this.origin = origin; + width = origin.getWidth(); + height = origin.getHeight(); + + level = Objects.requireNonNull( origin.getLevel(), "level cannot be null" ); + down = origin.getDown(); + right = origin.getRight(); + } + + void expand() + { + int changedCount = 0; + + // Impose a limit on the number of resizes we can attempt. There's a risk of getting into an infinite loop + // if we merge right/down and the next monitor has a width/height of 0. This /should/ never happen - validation + // will catch it - but I also have a complete lack of faith in the code. + // As an aside, I think the actual limit is width+height resizes, but again - complete lack of faith. + int changeLimit = ComputerCraft.monitorWidth * ComputerCraft.monitorHeight + 1; + while( expandIn( true, false ) || expandIn( true, true ) || + expandIn( false, false ) || expandIn( false, true ) + ) + { + changedCount++; + if( changedCount > changeLimit ) + { + ComputerCraft.log.error( "Monitor has grown too much. This suggests there's an empty monitor in the world." ); + break; + } + } + + if( changedCount > 0 ) origin.resize( width, height ); + } + + /** + * Attempt to expand a monitor in a particular direction as much as possible. + * + * @param useXAxis {@literal true} if we're expanding on the X Axis, {@literal false} if on the Y. + * @param isPositive {@literal true} if we're expanding in the positive direction, {@literal false} if negative. + * @return If the monitor changed. + */ + private boolean expandIn( boolean useXAxis, boolean isPositive ) + { + BlockPos pos = origin.getBlockPos(); + int height = this.height, width = this.width; + + int otherOffset = isPositive ? (useXAxis ? width : height) : -1; + BlockPos otherPos = useXAxis ? pos.relative( right, otherOffset ) : pos.relative( down, otherOffset ); + BlockEntity other = level.getBlockEntity( otherPos ); + if( !(other instanceof TileMonitor otherMonitor) || !origin.isCompatible( otherMonitor ) ) return false; + + if( useXAxis ) + { + if( otherMonitor.getYIndex() != 0 || otherMonitor.getHeight() != height ) return false; + width += otherMonitor.getWidth(); + if( width > ComputerCraft.monitorWidth ) return false; + } + else + { + if( otherMonitor.getXIndex() != 0 || otherMonitor.getWidth() != width ) return false; + height += otherMonitor.getHeight(); + if( height > ComputerCraft.monitorHeight ) return false; + } + + if( !isPositive ) + { + BlockEntity otherOrigin = level.getBlockEntity( otherMonitor.toWorldPos( 0, 0 ) ); + if( otherOrigin == null || !origin.isCompatible( (TileMonitor) otherOrigin ) ) return false; + + origin = (TileMonitor) otherOrigin; + } + + this.width = width; + this.height = height; + + return true; + } + +} diff --git a/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorEdgeState.java b/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorEdgeState.java index d45a52a4f..e76dd95af 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorEdgeState.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorEdgeState.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.peripheral.monitor; import net.minecraft.util.StringRepresentable; @@ -34,6 +33,15 @@ public enum MonitorEdgeState implements StringRepresentable LRU( "lru", LEFT | RIGHT | UP ), LRUD( "lrud", LEFT | RIGHT | UP | DOWN ); + private final String name; + private final int flags; + + MonitorEdgeState( String name, int flags ) + { + this.name = name; + this.flags = flags; + } + private static final MonitorEdgeState[] BY_FLAG = new MonitorEdgeState[16]; static @@ -44,18 +52,15 @@ public enum MonitorEdgeState implements StringRepresentable } } - private final String name; - private final int flags; - - MonitorEdgeState( String name, int flags ) + public static MonitorEdgeState fromConnections( boolean up, boolean down, boolean left, boolean right ) { - this.name = name; - this.flags = flags; + return BY_FLAG[(up ? UP : 0) | (down ? DOWN : 0) | (left ? LEFT : 0) | (right ? RIGHT : 0)]; } - public static MonitorEdgeState fromConnections( boolean up, boolean down, boolean left, boolean right ) + @Override + public String toString() { - return BY_FLAG[(up ? UP : 0) | (down ? DOWN : 0) | (left ? LEFT : 0) | (right ? RIGHT : 0)]; + return getSerializedName(); } @Nonnull diff --git a/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorPeripheral.java b/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorPeripheral.java index 2ca3c7da9..c1a1c7ce4 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorPeripheral.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorPeripheral.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.peripheral.monitor; import dan200.computercraft.api.lua.LuaException; @@ -18,21 +17,22 @@ import javax.annotation.Nullable; /** - * Monitors are a block which act as a terminal, displaying information on one side. This allows them to be read and interacted with in-world without - * opening a GUI. + * Monitors are a block which act as a terminal, displaying information on one side. This allows them to be read and + * interacted with in-world without opening a GUI. * - * Monitors act as @{term.Redirect|terminal redirects} and so expose the same methods, as well as several additional ones, which are documented below. + * Monitors act as @{term.Redirect|terminal redirects} and so expose the same methods, as well as several additional + * ones, which are documented below. * * Like computers, monitors come in both normal (no colour) and advanced (colour) varieties. * * @cc.module monitor * @cc.usage Write "Hello, world!" to an adjacent monitor: * - *
- *     local monitor = peripheral.find("monitor")
- *     monitor.setCursorPos(1, 1)
- *     monitor.write("Hello, world!")
- *     
+ *
{@code
+ * local monitor = peripheral.find("monitor")
+ * monitor.setCursorPos(1, 1)
+ * monitor.write("Hello, world!")
+ * }
*/ public class MonitorPeripheral extends TermMethods implements IPeripheral { @@ -50,29 +50,20 @@ public String getType() return "monitor"; } - @Override - public void attach( @Nonnull IComputerAccess computer ) - { - monitor.addComputer( computer ); - } - - @Override - public void detach( @Nonnull IComputerAccess computer ) - { - monitor.removeComputer( computer ); - } - - @Nullable - @Override - public Object getTarget() - { - return monitor; - } - - @Override - public boolean equals( IPeripheral other ) + /** + * Set the scale of this monitor. A larger scale will result in the monitor having a lower resolution, but display + * text much larger. + * + * @param scaleArg The monitor's scale. This must be a multiple of 0.5 between 0.5 and 5. + * @throws LuaException If the scale is out of range. + * @see #getTextScale() + */ + @LuaFunction + public final void setTextScale( double scaleArg ) throws LuaException { - return other instanceof MonitorPeripheral && monitor == ((MonitorPeripheral) other).monitor; + int scale = (int) (LuaValues.checkFinite( 0, scaleArg ) * 2.0); + if( scale < 1 || scale > 10 ) throw new LuaException( "Expected number in range 0.5-5" ); + getMonitor().setTextScale( scale ); } /** @@ -80,6 +71,7 @@ public boolean equals( IPeripheral other ) * * @return The monitor's current scale. * @throws LuaException If the monitor cannot be found. + * @cc.since 1.81.0 */ @LuaFunction public final double getTextScale() throws LuaException @@ -87,32 +79,29 @@ public final double getTextScale() throws LuaException return getMonitor().getTextScale() / 2.0; } - /** - * Set the scale of this monitor. A larger scale will result in the monitor having a lower resolution, but display text much larger. - * - * @param scaleArg The monitor's scale. This must be a multiple of 0.5 between 0.5 and 5. - * @throws LuaException If the scale is out of range. - * @see #getTextScale() - */ - @LuaFunction - public final void setTextScale( double scaleArg ) throws LuaException + @Override + public void attach( @Nonnull IComputerAccess computer ) { - int scale = (int) (LuaValues.checkFinite( 0, scaleArg ) * 2.0); - if( scale < 1 || scale > 10 ) - { - throw new LuaException( "Expected number in range 0.5-5" ); - } - getMonitor().setTextScale( scale ); + monitor.addComputer( computer ); + } + + @Override + public void detach( @Nonnull IComputerAccess computer ) + { + monitor.removeComputer( computer ); + } + + @Override + public boolean equals( IPeripheral other ) + { + return other instanceof MonitorPeripheral && monitor == ((MonitorPeripheral) other).monitor; } @Nonnull private ServerMonitor getMonitor() throws LuaException { ServerMonitor monitor = this.monitor.getCachedServerMonitor(); - if( monitor == null ) - { - throw new LuaException( "Monitor has been detached" ); - } + if( monitor == null ) throw new LuaException( "Monitor has been detached" ); return monitor; } @@ -121,10 +110,7 @@ private ServerMonitor getMonitor() throws LuaException public Terminal getTerminal() throws LuaException { Terminal terminal = getMonitor().getTerminal(); - if( terminal == null ) - { - throw new LuaException( "Monitor has been detached" ); - } + if( terminal == null ) throw new LuaException( "Monitor has been detached" ); return terminal; } @@ -133,4 +119,11 @@ public boolean isColour() throws LuaException { return getMonitor().isColour(); } + + @Nullable + @Override + public Object getTarget() + { + return monitor; + } } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorRenderer.java b/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorRenderer.java index 14aec1173..132631f88 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorRenderer.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorRenderer.java @@ -36,15 +36,11 @@ public enum MonitorRenderer /** * Render using VBOs. + * + * @see com.mojang.blaze3d.vertex.VertexBuffer */ VBO; - private static boolean initialised = false; - private static boolean textureBuffer = false; - private static boolean shaderMod = false; - //TODO find out which shader mods do better with VBOs and add them here. - private static List shaderModIds = Arrays.asList( "optifabric" ); - /** * Get the current renderer to use. * @@ -90,9 +86,18 @@ private static MonitorRenderer best() return textureBuffer && !shaderMod ? TBO : VBO; } + private static boolean initialised = false; + private static boolean textureBuffer = false; + private static boolean shaderMod = false; + //TODO find out which shader mods do better with VBOs and add them here. + private static List shaderModIds = Arrays.asList( "optifabric" ); + private static void checkCapabilities() { + if( initialised ) return; + textureBuffer = GL.getCapabilities().OpenGL31; + initialised = true; } private static void checkForShaderMods() diff --git a/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorWatcher.java b/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorWatcher.java new file mode 100644 index 000000000..607a9d8fe --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/peripheral/monitor/MonitorWatcher.java @@ -0,0 +1,138 @@ +/* + * This file is part of ComputerCraft - http://www.computercraft.info + * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. + * Send enquiries to dratcliffe@gmail.com + */ +package dan200.computercraft.shared.peripheral.monitor; + +import dan200.computercraft.ComputerCraft; +import dan200.computercraft.shared.network.NetworkHandler; +import dan200.computercraft.shared.network.client.MonitorClientMessage; +import dan200.computercraft.shared.network.client.TerminalState; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; +import net.minecraft.core.BlockPos; +import net.minecraft.server.MinecraftServer; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.chunk.LevelChunk; + +import java.util.ArrayDeque; +import java.util.Queue; + +public final class MonitorWatcher +{ + private static final Queue watching = new ArrayDeque<>(); + private static final Queue playerUpdates = new ArrayDeque<>(); + + private MonitorWatcher() + { + } + + public static void init() + { + ServerTickEvents.END_SERVER_TICK.register( server -> { + onTick( server ); + } ); + + + } + + static void enqueue( TileMonitor monitor ) + { + if( monitor.enqueued ) return; + + monitor.enqueued = true; + monitor.cached = null; + watching.add( monitor ); + } + + // public static void onWatch( ChunkWatchEvent.Watch event ) + // { + // ChunkPos chunkPos = event.getPos(); + // LevelChunk chunk = (LevelChunk) event.getWorld().getChunk( chunkPos.x, chunkPos.z, ChunkStatus.FULL, false ); + // if( chunk == null ) return; + // + // for( BlockEntity te : chunk.getBlockEntities().values() ) + // { + // // Find all origin monitors who are not already on the queue. + // if( !(te instanceof TileMonitor monitor) ) continue; + // + // ServerMonitor serverMonitor = getMonitor( monitor ); + // if( serverMonitor == null || monitor.enqueued ) continue; + // + // // The chunk hasn't been sent to the client yet, so we can't send an update. Do it on tick end. + // playerUpdates.add( new PlayerUpdate( event.getPlayer(), monitor ) ); + // } + // } + + public static void onTick( MinecraftServer server ) + { + PlayerUpdate playerUpdate; + while( (playerUpdate = playerUpdates.poll()) != null ) + { + TileMonitor tile = playerUpdate.monitor; + if( tile.enqueued || tile.isRemoved() ) continue; + + ServerMonitor monitor = getMonitor( tile ); + if( monitor == null ) continue; + + // Some basic sanity checks to the player. It's possible they're no longer within range, but that's harder + // to track efficiently. + ServerPlayer player = playerUpdate.player; + if( !player.isAlive() || player.getLevel() != tile.getLevel() ) continue; + + NetworkHandler.sendToPlayer( playerUpdate.player, new MonitorClientMessage( tile.getBlockPos(), getState( tile, monitor ) ) ); + } + + long limit = ComputerCraft.monitorBandwidth; + boolean obeyLimit = limit > 0; + + TileMonitor tile; + while( (!obeyLimit || limit > 0) && (tile = watching.poll()) != null ) + { + tile.enqueued = false; + ServerMonitor monitor = getMonitor( tile ); + if( monitor == null ) continue; + + BlockPos pos = tile.getBlockPos(); + Level world = tile.getLevel(); + if( !(world instanceof ServerLevel) ) continue; + + LevelChunk chunk = world.getChunkAt( pos ); + if( ((ServerLevel) world).getChunkSource().chunkMap.getPlayers( chunk.getPos(), false ).isEmpty() ) + { + continue; + } + + TerminalState state = getState( tile, monitor ); + NetworkHandler.sendToAllTracking( new MonitorClientMessage( pos, state ), chunk ); + + limit -= state.size(); + } + } + + private static ServerMonitor getMonitor( TileMonitor monitor ) + { + return !monitor.isRemoved() && monitor.getXIndex() == 0 && monitor.getYIndex() == 0 ? monitor.getCachedServerMonitor() : null; + } + + private static TerminalState getState( TileMonitor tile, ServerMonitor monitor ) + { + TerminalState state = tile.cached; + if( state == null ) state = tile.cached = monitor.write(); + return state; + } + + private static final class PlayerUpdate + { + final ServerPlayer player; + final TileMonitor monitor; + + private PlayerUpdate( ServerPlayer player, TileMonitor monitor ) + { + this.player = player; + this.monitor = monitor; + } + } +} diff --git a/src/main/java/dan200/computercraft/shared/peripheral/monitor/ServerMonitor.java b/src/main/java/dan200/computercraft/shared/peripheral/monitor/ServerMonitor.java index f5225574c..7dbd2ae65 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/monitor/ServerMonitor.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/monitor/ServerMonitor.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.peripheral.monitor; import dan200.computercraft.core.terminal.Terminal; @@ -15,9 +14,9 @@ public class ServerMonitor extends ServerTerminal { private final TileMonitor origin; + private int textScale = 2; private final AtomicBoolean resized = new AtomicBoolean( false ); private final AtomicBoolean changed = new AtomicBoolean( false ); - private int textScale = 2; public ServerMonitor( boolean colour, TileMonitor origin ) { @@ -25,6 +24,31 @@ public ServerMonitor( boolean colour, TileMonitor origin ) this.origin = origin; } + public synchronized void rebuild() + { + Terminal oldTerm = getTerminal(); + int oldWidth = oldTerm == null ? -1 : oldTerm.getWidth(); + int oldHeight = oldTerm == null ? -1 : oldTerm.getHeight(); + + double textScale = this.textScale * 0.5; + int termWidth = (int) Math.max( + Math.round( (origin.getWidth() - 2.0 * (TileMonitor.RENDER_BORDER + TileMonitor.RENDER_MARGIN)) / (textScale * 6.0 * TileMonitor.RENDER_PIXEL_SCALE) ), + 1.0 + ); + int termHeight = (int) Math.max( + Math.round( (origin.getHeight() - 2.0 * (TileMonitor.RENDER_BORDER + TileMonitor.RENDER_MARGIN)) / (textScale * 9.0 * TileMonitor.RENDER_PIXEL_SCALE) ), + 1.0 + ); + + resize( termWidth, termHeight ); + if( oldWidth != termWidth || oldHeight != termHeight ) + { + getTerminal().clear(); + resized.set( true ); + markChanged(); + } + } + @Override protected void markTerminalChanged() { @@ -34,10 +58,7 @@ protected void markTerminalChanged() private void markChanged() { - if( !changed.getAndSet( true ) ) - { - TickScheduler.schedule( origin ); - } + if( !changed.getAndSet( true ) ) TickScheduler.schedule( origin ); } protected void clearChanged() @@ -52,37 +73,11 @@ public int getTextScale() public synchronized void setTextScale( int textScale ) { - if( this.textScale == textScale ) - { - return; - } + if( this.textScale == textScale ) return; this.textScale = textScale; rebuild(); } - public synchronized void rebuild() - { - Terminal oldTerm = getTerminal(); - int oldWidth = oldTerm == null ? -1 : oldTerm.getWidth(); - int oldHeight = oldTerm == null ? -1 : oldTerm.getHeight(); - - double textScale = this.textScale * 0.5; - int termWidth = - (int) Math.max( Math.round( (origin.getWidth() - 2.0 * (TileMonitor.RENDER_BORDER + TileMonitor.RENDER_MARGIN)) / (textScale * 6.0 * TileMonitor.RENDER_PIXEL_SCALE) ), - 1.0 ); - int termHeight = - (int) Math.max( Math.round( (origin.getHeight() - 2.0 * (TileMonitor.RENDER_BORDER + TileMonitor.RENDER_MARGIN)) / (textScale * 9.0 * TileMonitor.RENDER_PIXEL_SCALE) ), - 1.0 ); - - resize( termWidth, termHeight ); - if( oldWidth != termWidth || oldHeight != termHeight ) - { - getTerminal().clear(); - resized.set( true ); - markChanged(); - } - } - public boolean pollResized() { return resized.getAndSet( false ); 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 d7b8f2aa4..bcd3bae94 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/monitor/TileMonitor.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/monitor/TileMonitor.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.peripheral.monitor; import dan200.computercraft.ComputerCraft; @@ -18,6 +17,7 @@ import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; import net.minecraft.world.entity.player.Player; @@ -26,10 +26,13 @@ import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.BlockHitResult; +import org.jetbrains.annotations.NotNull; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import java.util.HashSet; import java.util.Set; +import java.util.function.Consumer; public class TileMonitor extends TileGeneric implements IPeripheralTile { @@ -43,62 +46,60 @@ public class TileMonitor extends TileGeneric implements IPeripheralTile private static final String NBT_HEIGHT = "Height"; private final boolean advanced; - private final Set computers = new HashSet<>(); - // MonitorWatcher state. - boolean enqueued; - TerminalState cached; + private ServerMonitor serverMonitor; private ClientMonitor clientMonitor; private MonitorPeripheral peripheral; + private final Set computers = new HashSet<>(); + private boolean needsUpdate = false; private boolean needsValidating = false; private boolean destroyed = false; - private boolean visiting = false; + + // MonitorWatcher state. + boolean enqueued; + TerminalState cached; + private int width = 1; private int height = 1; private int xIndex = 0; private int yIndex = 0; - public TileMonitor( BlockEntityType type, boolean advanced, BlockPos pos, BlockState state ) + public TileMonitor( BlockEntityType type, BlockPos pos, BlockState state, boolean advanced ) { super( type, pos, state ); this.advanced = advanced; } + @Override + public void clearRemoved() // TODO: Switch back to onLood + { + super.clearRemoved(); + needsValidating = true; // Same, tbh + TickScheduler.schedule( this ); + } + @Override public void destroy() { // TODO: Call this before using the block - if( destroyed ) - { - return; - } + if( destroyed ) return; destroyed = true; - if( !getLevel().isClientSide ) - { - contractNeighbours(); - } + if( !getLevel().isClientSide ) contractNeighbours(); } @Override public void setRemoved() { super.setRemoved(); - if( clientMonitor != null && xIndex == 0 && yIndex == 0 ) - { - clientMonitor.destroy(); - } + if( clientMonitor != null && xIndex == 0 && yIndex == 0 ) clientMonitor.destroy(); } @Override public void onChunkUnloaded() { super.onChunkUnloaded(); - if( clientMonitor != null && xIndex == 0 && yIndex == 0 ) - { - clientMonitor.destroy(); - } - clientMonitor = null; + if( clientMonitor != null && xIndex == 0 && yIndex == 0 ) clientMonitor.destroy(); } @Nonnull @@ -109,12 +110,11 @@ public InteractionResult onActivate( Player player, InteractionHand hand, BlockH { if( !getLevel().isClientSide ) { - monitorTouched( (float) (hit.getLocation().x - hit.getBlockPos() - .getX()), - (float) (hit.getLocation().y - hit.getBlockPos() - .getY()), - (float) (hit.getLocation().z - hit.getBlockPos() - .getZ()) ); + monitorTouched( + (float) (hit.getLocation().x - hit.getBlockPos().getX()), + (float) (hit.getLocation().y - hit.getBlockPos().getY()), + (float) (hit.getLocation().z - hit.getBlockPos().getZ()) + ); } return InteractionResult.SUCCESS; } @@ -123,254 +123,79 @@ public InteractionResult onActivate( Player player, InteractionHand hand, BlockH } @Override - public void blockTick() + public void saveAdditional( CompoundTag tag ) { - - if( needsValidating ) - { - needsValidating = false; - validate(); - } - - if( needsUpdate ) - { - needsUpdate = false; - updateNeighbors(); - } - - if( xIndex != 0 || yIndex != 0 || serverMonitor == null ) - { - return; - } - - serverMonitor.clearChanged(); - - if( serverMonitor.pollResized() ) - { - for( int x = 0; x < width; x++ ) - { - for( int y = 0; y < height; y++ ) - { - TileMonitor monitor = getNeighbour( x, y ).getMonitor(); - if( monitor == null ) - { - continue; - } - - for( IComputerAccess computer : monitor.computers ) - { - computer.queueEvent( "monitor_resize", computer.getAttachmentName() ); - } - } - } - } - - if( serverMonitor.pollTerminalChanged() ) - { - updateBlock(); - } + tag.putInt( NBT_X, xIndex ); + tag.putInt( NBT_Y, yIndex ); + tag.putInt( NBT_WIDTH, width ); + tag.putInt( NBT_HEIGHT, height ); + super.saveAdditional( tag ); } @Override - protected final void readDescription( @Nonnull CompoundTag nbt ) + public void load( @Nonnull CompoundTag nbt ) { - super.readDescription( nbt ); - - int oldXIndex = xIndex; - int oldYIndex = yIndex; - int oldWidth = width; - int oldHeight = height; + super.load( nbt ); xIndex = nbt.getInt( NBT_X ); yIndex = nbt.getInt( NBT_Y ); width = nbt.getInt( NBT_WIDTH ); height = nbt.getInt( NBT_HEIGHT ); - - if( oldXIndex != xIndex || oldYIndex != yIndex ) - { - // If our index has changed then it's possible the origin monitor has changed. Thus - // we'll clear our cache. If we're the origin then we'll need to remove the glList as well. - if( oldXIndex == 0 && oldYIndex == 0 && clientMonitor != null ) - { - clientMonitor.destroy(); - } - clientMonitor = null; - } - - if( xIndex == 0 && yIndex == 0 ) - { - // If we're the origin terminal then create it. - if( clientMonitor == null ) - { - clientMonitor = new ClientMonitor( advanced, this ); - } - clientMonitor.readDescription( nbt ); - } - - if( oldXIndex != xIndex || oldYIndex != yIndex || oldWidth != width || oldHeight != height ) - { - // One of our properties has changed, so ensure we redraw the block - updateBlock(); - } } @Override - protected void writeDescription( @Nonnull CompoundTag nbt ) - { - super.writeDescription( nbt ); - nbt.putInt( NBT_X, xIndex ); - nbt.putInt( NBT_Y, yIndex ); - nbt.putInt( NBT_WIDTH, width ); - nbt.putInt( NBT_HEIGHT, height ); - - if( xIndex == 0 && yIndex == 0 && serverMonitor != null ) - { - serverMonitor.writeDescription( nbt ); - } - } - - private MonitorState getNeighbour( int x, int y ) - { - BlockPos pos = getBlockPos(); - Direction right = getRight(); - Direction down = getDown(); - int xOffset = -xIndex + x; - int yOffset = -yIndex + y; - return getSimilarMonitorAt( pos.relative( right, xOffset ) - .relative( down, yOffset ) ); - } - - public Direction getRight() - { - return getDirection().getCounterClockWise(); - } - - public Direction getDown() - { - Direction orientation = getOrientation(); - if( orientation == Direction.NORTH ) - { - return Direction.UP; - } - return orientation == Direction.DOWN ? getDirection() : getDirection().getOpposite(); - } - - private MonitorState getSimilarMonitorAt( BlockPos pos ) + public void blockTick() { - if( pos.equals( getBlockPos() ) ) - { - return MonitorState.present( this ); - } - - Level world = getLevel(); - if( world == null || !world.hasChunkAt( pos ) ) + if( needsValidating ) { - return MonitorState.UNLOADED; + needsValidating = false; + validate(); } - BlockEntity tile = world.getBlockEntity( pos ); - if( !(tile instanceof TileMonitor) ) + if( needsUpdate ) { - return MonitorState.MISSING; + needsUpdate = false; + expand(); } - TileMonitor monitor = (TileMonitor) tile; - return !monitor.visiting && !monitor.destroyed && advanced == monitor.advanced && getDirection() == monitor.getDirection() && getOrientation() == monitor.getOrientation() ? MonitorState.present( monitor ) : MonitorState.MISSING; - } - - // region Sizing and placement stuff - public Direction getDirection() - { - // Ensure we're actually a monitor block. This _should_ always be the case, but sometimes there's - // fun problems with the block being missing on the client. - BlockState state = getBlockState(); - return state.hasProperty( BlockMonitor.FACING ) ? state.getValue( BlockMonitor.FACING ) : Direction.NORTH; - } - - public Direction getOrientation() - { - return getBlockState().getValue( BlockMonitor.ORIENTATION ); - } - - @Override - public void load( @Nonnull CompoundTag nbt ) - { - super.load( nbt ); - - xIndex = nbt.getInt( NBT_X ); - yIndex = nbt.getInt( NBT_Y ); - width = nbt.getInt( NBT_WIDTH ); - height = nbt.getInt( NBT_HEIGHT ); - } - - // Networking stuff - - @Override - public void saveAdditional( CompoundTag nbt ) - { - nbt.putInt( NBT_X, xIndex ); - nbt.putInt( NBT_Y, yIndex ); - nbt.putInt( NBT_WIDTH, width ); - nbt.putInt( NBT_HEIGHT, height ); - - super.saveAdditional( nbt ); - } - - // @Override //TODO: make BlockEntityRenderer work with this, i guess. - // public double getRenderDistance() - // { - // return ComputerCraft.monitorDistanceSq; - // } + if( xIndex != 0 || yIndex != 0 || serverMonitor == null ) return; - // Sizing and placement stuff + serverMonitor.clearChanged(); - @Override - public void clearRemoved() - { - super.clearRemoved(); - needsValidating = true; - TickScheduler.schedule( this ); + if( serverMonitor.pollResized() ) eachComputer( c -> c.queueEvent( "monitor_resize", c.getAttachmentName() ) ); + if( serverMonitor.pollTerminalChanged() ) MonitorWatcher.enqueue( this ); } @Nonnull @Override - public IPeripheral getPeripheral( Direction side ) + public IPeripheral getPeripheral( @NotNull Direction side ) { createServerMonitor(); // Ensure the monitor is created before doing anything else. - if( peripheral == null ) - { - peripheral = new MonitorPeripheral( this ); - } + if( peripheral == null ) peripheral = new MonitorPeripheral( this ); return peripheral; } + @Nullable public ServerMonitor getCachedServerMonitor() { return serverMonitor; } + @Nullable private ServerMonitor getServerMonitor() { - if( serverMonitor != null ) - { - return serverMonitor; - } + if( serverMonitor != null ) return serverMonitor; TileMonitor origin = getOrigin().getMonitor(); - if( origin == null ) - { - return null; - } + if( origin == null ) return null; return serverMonitor = origin.serverMonitor; } + @Nullable private ServerMonitor createServerMonitor() { - if( serverMonitor != null ) - { - return serverMonitor; - } + if( serverMonitor != null ) return serverMonitor; if( xIndex == 0 && yIndex == 0 ) { @@ -383,11 +208,8 @@ private ServerMonitor createServerMonitor() { for( int y = 0; y < height; y++ ) { - TileMonitor monitor = getNeighbour( x, y ).getMonitor(); - if( monitor != null ) - { - monitor.serverMonitor = serverMonitor; - } + TileMonitor monitor = getLoadedMonitor( x, y ).getMonitor(); + if( monitor != null ) monitor.serverMonitor = serverMonitor; } } @@ -397,36 +219,73 @@ private ServerMonitor createServerMonitor() { // Otherwise fetch the origin and attempt to get its monitor // Note this may load chunks, but we don't really have a choice here. - BlockPos pos = getBlockPos(); - BlockEntity te = level.getBlockEntity( pos.relative( getRight(), -xIndex ) - .relative( getDown(), -yIndex ) ); - if( !(te instanceof TileMonitor) ) - { - return null; - } + BlockEntity te = level.getBlockEntity( toWorldPos( 0, 0 ) ); + if( !(te instanceof TileMonitor) ) return null; return serverMonitor = ((TileMonitor) te).createServerMonitor(); } } + @Nullable public ClientMonitor getClientMonitor() { - if( clientMonitor != null ) - { - return clientMonitor; - } + if( clientMonitor != null ) return clientMonitor; - BlockPos pos = getBlockPos(); - BlockEntity te = level.getBlockEntity( pos.relative( getRight(), -xIndex ) - .relative( getDown(), -yIndex ) ); - if( !(te instanceof TileMonitor) ) - { - return null; - } + BlockEntity te = level.getBlockEntity( toWorldPos( 0, 0 ) ); + if( !(te instanceof TileMonitor) ) return null; return clientMonitor = ((TileMonitor) te).clientMonitor; } + // Networking stuff + + @Nonnull + @Override + public final ClientboundBlockEntityDataPacket getUpdatePacket() + { + return ClientboundBlockEntityDataPacket.create( this ); + } + + @Nonnull + @Override + public final CompoundTag getUpdateTag() + { + CompoundTag nbt = super.getUpdateTag(); + nbt.putInt( NBT_X, xIndex ); + nbt.putInt( NBT_Y, yIndex ); + nbt.putInt( NBT_WIDTH, width ); + nbt.putInt( NBT_HEIGHT, height ); + return nbt; + } + + // @Override + // public final void handleUpdateTag( @Nonnull CompoundTag nbt ) + // { + // super.handleUpdateTag( nbt ); + // + // int oldXIndex = xIndex; + // int oldYIndex = yIndex; + // + // xIndex = nbt.getInt( NBT_X ); + // yIndex = nbt.getInt( NBT_Y ); + // width = nbt.getInt( NBT_WIDTH ); + // height = nbt.getInt( NBT_HEIGHT ); + // + // if( oldXIndex != xIndex || oldYIndex != yIndex ) + // { + // // If our index has changed then it's possible the origin monitor has changed. Thus + // // we'll clear our cache. If we're the origin then we'll need to remove the glList as well. + // if( oldXIndex == 0 && oldYIndex == 0 && clientMonitor != null ) clientMonitor.destroy(); + // clientMonitor = null; + // } + // + // if( xIndex == 0 && yIndex == 0 ) + // { + // // If we're the origin terminal then create it. + // if( clientMonitor == null ) clientMonitor = new ClientMonitor( advanced, this ); + // } + // } + public final void read( TerminalState state ) { if( xIndex != 0 || yIndex != 0 ) @@ -435,20 +294,33 @@ public final void read( TerminalState state ) return; } - if( clientMonitor == null ) - { - clientMonitor = new ClientMonitor( advanced, this ); - } + if( clientMonitor == null ) clientMonitor = new ClientMonitor( advanced, this ); clientMonitor.read( state ); } + // Sizing and placement stuff + private void updateBlockState() { - getLevel().setBlock( getBlockPos(), - getBlockState().setValue( BlockMonitor.STATE, - MonitorEdgeState.fromConnections( yIndex < height - 1, - yIndex > 0, xIndex > 0, xIndex < width - 1 ) ), - 2 ); + getLevel().setBlock( getBlockPos(), getBlockState() + .setValue( BlockMonitor.STATE, MonitorEdgeState.fromConnections( + yIndex < height - 1, yIndex > 0, + xIndex > 0, xIndex < width - 1 ) ), 2 ); + } + + // region Sizing and placement stuff + public Direction getDirection() + { + // Ensure we're actually a monitor block. This _should_ always be the case, but sometimes there's + // fun problems with the block being missing on the client. + BlockState state = getBlockState(); + return state.hasProperty( BlockMonitor.FACING ) ? state.getValue( BlockMonitor.FACING ) : Direction.NORTH; + } + + public Direction getOrientation() + { + BlockState state = getBlockState(); + return state.hasProperty( BlockMonitor.ORIENTATION ) ? state.getValue( BlockMonitor.ORIENTATION ) : Direction.NORTH; } public Direction getFront() @@ -457,6 +329,18 @@ public Direction getFront() return orientation == Direction.NORTH ? getDirection() : orientation; } + public Direction getRight() + { + return getDirection().getCounterClockWise(); + } + + public Direction getDown() + { + Direction orientation = getOrientation(); + if( orientation == Direction.NORTH ) return Direction.UP; + return orientation == Direction.DOWN ? getDirection() : getDirection().getOpposite(); + } + public int getWidth() { return width; @@ -477,18 +361,55 @@ public int getYIndex() return yIndex; } + boolean isCompatible( TileMonitor other ) + { + return !other.destroyed && advanced == other.advanced && getOrientation() == other.getOrientation() && getDirection() == other.getDirection(); + } + + /** + * Get a tile within the current monitor only if it is loaded and compatible. + * + * @param x Absolute X position in monitor coordinates + * @param y Absolute Y position in monitor coordinates + * @return The located monitor + */ + @Nonnull + private MonitorState getLoadedMonitor( int x, int y ) + { + if( x == xIndex && y == yIndex ) return MonitorState.present( this ); + BlockPos pos = toWorldPos( x, y ); + + Level world = getLevel(); + if( world == null || !world.isLoaded( pos ) ) return MonitorState.UNLOADED; + + BlockEntity tile = world.getBlockEntity( pos ); + if( !(tile instanceof TileMonitor monitor) ) return MonitorState.MISSING; + + return isCompatible( monitor ) ? MonitorState.present( monitor ) : MonitorState.MISSING; + } + private MonitorState getOrigin() { - return getNeighbour( 0, 0 ); + return getLoadedMonitor( 0, 0 ); + } + + /** + * Convert monitor coordinates to world coordinates. + * + * @param x Absolute X position in monitor coordinates + * @param y Absolute Y position in monitor coordinates + * @return The monitor's position. + */ + BlockPos toWorldPos( int x, int y ) + { + if( xIndex == x && yIndex == y ) return getBlockPos(); + return getBlockPos().relative( getRight(), -xIndex + x ).relative( getDown(), -yIndex + y ); } - private void resize( int width, int height ) + void resize( int width, int height ) { // If we're not already the origin then we'll need to generate a new terminal. - if( xIndex != 0 || yIndex != 0 ) - { - serverMonitor = null; - } + if( xIndex != 0 || yIndex != 0 ) serverMonitor = null; xIndex = 0; yIndex = 0; @@ -504,7 +425,7 @@ private void resize( int width, int height ) { for( int y = 0; y < height; y++ ) { - TileMonitor monitor = getNeighbour( x, y ).getMonitor(); + TileMonitor monitor = getLoadedMonitor( x, y ).getMonitor(); if( monitor != null && monitor.peripheral != null ) { needsTerminal = true; @@ -516,10 +437,7 @@ private void resize( int width, int height ) // Either delete the current monitor or sync a new one. if( needsTerminal ) { - if( serverMonitor == null ) - { - serverMonitor = new ServerMonitor( advanced, this ); - } + if( serverMonitor == null ) serverMonitor = new ServerMonitor( advanced, this ); } else { @@ -528,275 +446,81 @@ private void resize( int width, int height ) // Update the terminal's width and height and rebuild it. This ensures the monitor // is consistent when syncing it to other monitors. - if( serverMonitor != null ) - { - serverMonitor.rebuild(); - } + if( serverMonitor != null ) serverMonitor.rebuild(); // Update the other monitors, setting coordinates, dimensions and the server terminal + BlockPos pos = getBlockPos(); + Direction down = getDown(), right = getRight(); for( int x = 0; x < width; x++ ) { for( int y = 0; y < height; y++ ) { - TileMonitor monitor = getNeighbour( x, y ).getMonitor(); - if( monitor == null ) - { - continue; - } + BlockEntity other = getLevel().getBlockEntity( pos.relative( right, x ).relative( down, y ) ); + if( !(other instanceof TileMonitor monitor) || !isCompatible( monitor ) ) continue; monitor.xIndex = x; monitor.yIndex = y; monitor.width = width; monitor.height = height; monitor.serverMonitor = serverMonitor; + monitor.needsUpdate = monitor.needsValidating = false; monitor.updateBlockState(); monitor.updateBlock(); } } } - private boolean mergeLeft() - { - TileMonitor left = getNeighbour( -1, 0 ).getMonitor(); - if( left == null || left.yIndex != 0 || left.height != height ) - { - return false; - } - - int width = left.width + this.width; - if( width > ComputerCraft.monitorWidth ) - { - return false; - } - - TileMonitor origin = left.getOrigin().getMonitor(); - if( origin != null ) - { - origin.resize( width, height ); - } - left.expand(); - return true; - } - - private boolean mergeRight() - { - TileMonitor right = getNeighbour( width, 0 ).getMonitor(); - if( right == null || right.yIndex != 0 || right.height != height ) - { - return false; - } - - int width = this.width + right.width; - if( width > ComputerCraft.monitorWidth ) - { - return false; - } - - TileMonitor origin = getOrigin().getMonitor(); - if( origin != null ) - { - origin.resize( width, height ); - } - expand(); - return true; - } - - private boolean mergeUp() - { - TileMonitor above = getNeighbour( 0, height ).getMonitor(); - if( above == null || above.xIndex != 0 || above.width != width ) - { - return false; - } - - int height = above.height + this.height; - if( height > ComputerCraft.monitorHeight ) - { - return false; - } - - TileMonitor origin = getOrigin().getMonitor(); - if( origin != null ) - { - origin.resize( width, height ); - } - expand(); - return true; - } - - private boolean mergeDown() - { - TileMonitor below = getNeighbour( 0, -1 ).getMonitor(); - if( below == null || below.xIndex != 0 || below.width != width ) - { - return false; - } - - int height = this.height + below.height; - if( height > ComputerCraft.monitorHeight ) - { - return false; - } - - TileMonitor origin = below.getOrigin().getMonitor(); - if( origin != null ) - { - origin.resize( width, height ); - } - below.expand(); - return true; - } - void updateNeighborsDeferred() { needsUpdate = true; } - void updateNeighbors() - { - contractNeighbours(); - contract(); - expand(); - } - - @SuppressWarnings( "StatementWithEmptyBody" ) void expand() { - while( mergeLeft() || mergeRight() || mergeUp() || mergeDown() ) ; + TileMonitor monitor = getOrigin().getMonitor(); + if( monitor != null && monitor.xIndex == 0 && monitor.yIndex == 0 ) new Expander( monitor ).expand(); } - void contractNeighbours() + private void contractNeighbours() { - visiting = true; - if( xIndex > 0 ) - { - TileMonitor left = getNeighbour( xIndex - 1, yIndex ).getMonitor(); - if( left != null ) - { - left.contract(); - } - } - if( xIndex + 1 < width ) - { - TileMonitor right = getNeighbour( xIndex + 1, yIndex ).getMonitor(); - if( right != null ) - { - right.contract(); - } - } - if( yIndex > 0 ) - { - TileMonitor below = getNeighbour( xIndex, yIndex - 1 ).getMonitor(); - if( below != null ) - { - below.contract(); - } - } - if( yIndex + 1 < height ) + if( width == 1 && height == 1 ) return; + + BlockPos pos = getBlockPos(); + Direction down = getDown(), right = getRight(); + BlockPos origin = toWorldPos( 0, 0 ); + + TileMonitor toLeft = null, toAbove = null, toRight = null, toBelow = null; + if( xIndex > 0 ) toLeft = tryResizeAt( pos.relative( right, -xIndex ), xIndex, 1 ); + if( yIndex > 0 ) toAbove = tryResizeAt( origin, width, yIndex ); + if( xIndex < width - 1 ) toRight = tryResizeAt( pos.relative( right, 1 ), width - xIndex - 1, 1 ); + if( yIndex < height - 1 ) { - TileMonitor above = getNeighbour( xIndex, yIndex + 1 ).getMonitor(); - if( above != null ) - { - above.contract(); - } + toBelow = tryResizeAt( origin.relative( down, yIndex + 1 ), width, height - yIndex - 1 ); } - visiting = false; + + if( toLeft != null ) toLeft.expand(); + if( toAbove != null ) toAbove.expand(); + if( toRight != null ) toRight.expand(); + if( toBelow != null ) toBelow.expand(); } - void contract() + @Nullable + private TileMonitor tryResizeAt( BlockPos pos, int width, int height ) { - int height = this.height; - int width = this.width; - - TileMonitor origin = getOrigin().getMonitor(); - if( origin == null ) + BlockEntity tile = level.getBlockEntity( pos ); + if( tile instanceof TileMonitor monitor && isCompatible( monitor ) ) { - TileMonitor right = width > 1 ? getNeighbour( 1, 0 ).getMonitor() : null; - TileMonitor below = height > 1 ? getNeighbour( 0, 1 ).getMonitor() : null; - - if( right != null ) - { - right.resize( width - 1, 1 ); - } - if( below != null ) - { - below.resize( width, height - 1 ); - } - if( right != null ) - { - right.expand(); - } - if( below != null ) - { - below.expand(); - } - - return; + monitor.resize( width, height ); + return monitor; } - for( int y = 0; y < height; y++ ) - { - for( int x = 0; x < width; x++ ) - { - TileMonitor monitor = origin.getNeighbour( x, y ).getMonitor(); - if( monitor != null ) - { - continue; - } - - // Decompose - TileMonitor above = null; - TileMonitor left = null; - TileMonitor right = null; - TileMonitor below = null; - - if( y > 0 ) - { - above = origin; - above.resize( width, y ); - } - if( x > 0 ) - { - left = origin.getNeighbour( 0, y ).getMonitor(); - left.resize( x, 1 ); - } - if( x + 1 < width ) - { - right = origin.getNeighbour( x + 1, y ).getMonitor(); - right.resize( width - (x + 1), 1 ); - } - if( y + 1 < height ) - { - below = origin.getNeighbour( 0, y + 1 ).getMonitor(); - below.resize( width, height - (y + 1) ); - } - - // Re-expand - if( above != null ) - { - above.expand(); - } - if( left != null ) - { - left.expand(); - } - if( right != null ) - { - right.expand(); - } - if( below != null ) - { - below.expand(); - } - return; - } - } + return null; } - // endregion + private boolean checkMonitorAt( int xIndex, int yIndex ) { - MonitorState state = getNeighbour( xIndex, yIndex ); + MonitorState state = getLoadedMonitor( xIndex, yIndex ); if( state.isMissing() ) return false; TileMonitor monitor = state.getMonitor(); @@ -807,9 +531,11 @@ private boolean checkMonitorAt( int xIndex, int yIndex ) private void validate() { - if( xIndex == 0 && yIndex == 0 && width == 1 || height == 1 ) return; + if( xIndex == 0 && yIndex == 0 && width == 1 && height == 1 ) return; - if( checkMonitorAt( 0, 0 ) && checkMonitorAt( 0, height - 1 ) && + if( xIndex >= 0 && xIndex <= width && width > 0 && width <= ComputerCraft.monitorWidth && + yIndex >= 0 && yIndex <= height && height > 0 && height <= ComputerCraft.monitorHeight && + checkMonitorAt( 0, 0 ) && checkMonitorAt( 0, height - 1 ) && checkMonitorAt( width - 1, 0 ) && checkMonitorAt( width - 1, height - 1 ) ) { return; @@ -821,10 +547,12 @@ private void validate() resize( 1, 1 ); needsUpdate = true; } + // endregion private void monitorTouched( float xPos, float yPos, float zPos ) { - XYPair pair = XYPair.of( xPos, yPos, zPos, getDirection(), getOrientation() ) + XYPair pair = XYPair + .of( xPos, yPos, zPos, getDirection(), getOrientation() ) .add( xIndex, height - yIndex - 1 ); if( pair.x > width - RENDER_BORDER || pair.y > height - RENDER_BORDER || pair.x < RENDER_BORDER || pair.y < RENDER_BORDER ) @@ -833,16 +561,10 @@ private void monitorTouched( float xPos, float yPos, float zPos ) } ServerTerminal serverTerminal = getServerMonitor(); - if( serverTerminal == null || !serverTerminal.isColour() ) - { - return; - } + if( serverTerminal == null || !serverTerminal.isColour() ) return; Terminal originTerminal = serverTerminal.getTerminal(); - if( originTerminal == null ) - { - return; - } + if( originTerminal == null ) return; double xCharWidth = (width - (RENDER_BORDER + RENDER_MARGIN) * 2.0) / originTerminal.getWidth(); double yCharHeight = (height - (RENDER_BORDER + RENDER_MARGIN) * 2.0) / originTerminal.getHeight(); @@ -850,20 +572,19 @@ private void monitorTouched( float xPos, float yPos, float zPos ) int xCharPos = (int) Math.min( originTerminal.getWidth(), Math.max( (pair.x - RENDER_BORDER - RENDER_MARGIN) / xCharWidth + 1.0, 1.0 ) ); int yCharPos = (int) Math.min( originTerminal.getHeight(), Math.max( (pair.y - RENDER_BORDER - RENDER_MARGIN) / yCharHeight + 1.0, 1.0 ) ); - for( int y = 0; y < height; y++ ) + eachComputer( c -> c.queueEvent( "monitor_touch", c.getAttachmentName(), xCharPos, yCharPos ) ); + } + + private void eachComputer( Consumer fun ) + { + for( int x = 0; x < width; x++ ) { - for( int x = 0; x < width; x++ ) + for( int y = 0; y < height; y++ ) { - TileMonitor monitor = getNeighbour( x, y ).getMonitor(); - if( monitor == null ) - { - continue; - } + TileMonitor monitor = getLoadedMonitor( x, y ).getMonitor(); + if( monitor == null ) continue; - for( IComputerAccess computer : monitor.computers ) - { - computer.queueEvent( "monitor_touch", computer.getAttachmentName(), xCharPos, yCharPos ); - } + for( IComputerAccess computer : monitor.computers ) fun.accept( computer ); } } } @@ -873,33 +594,24 @@ void addComputer( IComputerAccess computer ) computers.add( computer ); } - // @Nonnull - // @Override - // public Box getRenderBoundingBox() - // { - // TileMonitor start = getNeighbour( 0, 0 ); - // TileMonitor end = getNeighbour( m_width - 1, m_height - 1 ); - // if( start != null && end != null ) - // { - // BlockPos startPos = start.getPos(); - // BlockPos endPos = end.getPos(); - // int minX = Math.min( startPos.getX(), endPos.getX() ); - // int minY = Math.min( startPos.getY(), endPos.getY() ); - // int minZ = Math.min( startPos.getZ(), endPos.getZ() ); - // int maxX = Math.max( startPos.getX(), endPos.getX() ) + 1; - // int maxY = Math.max( startPos.getY(), endPos.getY() ) + 1; - // int maxZ = Math.max( startPos.getZ(), endPos.getZ() ) + 1; - // return new Box( minX, minY, minZ, maxX, maxY, maxZ ); - // } - // else - // { - // BlockPos pos = getPos(); - // return new Box( pos.getX(), pos.getY(), pos.getZ(), pos.getX() + 1, pos.getY() + 1, pos.getZ() + 1 ); - // } - // } - void removeComputer( IComputerAccess computer ) { computers.remove( computer ); } + + // @Nonnull + // @Override + // public AABB getRenderBoundingBox() + // { + // BlockPos startPos = toWorldPos( 0, 0 ); + // BlockPos endPos = toWorldPos( width, height ); + // return new AABB( + // Math.min( startPos.getX(), endPos.getX() ), + // Math.min( startPos.getY(), endPos.getY() ), + // Math.min( startPos.getZ(), endPos.getZ() ), + // Math.max( startPos.getX(), endPos.getX() ) + 1, + // Math.max( startPos.getY(), endPos.getY() ) + 1, + // Math.max( startPos.getZ(), endPos.getZ() ) + 1 + // ); + // } } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/monitor/XYPair.java b/src/main/java/dan200/computercraft/shared/peripheral/monitor/XYPair.java index ec660394e..fc2512a40 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/monitor/XYPair.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/monitor/XYPair.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.peripheral.monitor; import net.minecraft.core.Direction; @@ -19,6 +18,11 @@ public XYPair( float x, float y ) this.y = y; } + public XYPair add( float x, float y ) + { + return new XYPair( this.x + x, this.y + y ); + } + public static XYPair of( float xPos, float yPos, float zPos, Direction facing, Direction orientation ) { switch( orientation ) @@ -66,9 +70,4 @@ public static XYPair of( float xPos, float yPos, float zPos, Direction facing, D return new XYPair( xPos, zPos ); } - - public XYPair add( float x, float y ) - { - return new XYPair( this.x + x, this.y + y ); - } } 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 dffbedde4..b8a60402d 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/printer/BlockPrinter.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/printer/BlockPrinter.java @@ -3,10 +3,9 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.peripheral.printer; -import dan200.computercraft.shared.ComputerCraftRegistry; +import dan200.computercraft.shared.Registry; import dan200.computercraft.shared.common.BlockGeneric; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; @@ -30,38 +29,42 @@ public class BlockPrinter extends BlockGeneric { + private static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING; static final BooleanProperty TOP = BooleanProperty.create( "top" ); static final BooleanProperty BOTTOM = BooleanProperty.create( "bottom" ); - private static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING; public BlockPrinter( Properties settings ) { - super( settings, ComputerCraftRegistry.ModTiles.PRINTER ); + super( settings, () -> Registry.ModBlockEntities.PRINTER ); registerDefaultState( getStateDefinition().any() .setValue( FACING, Direction.NORTH ) .setValue( TOP, false ) .setValue( BOTTOM, false ) ); } + @Override + protected void createBlockStateDefinition( StateDefinition.Builder properties ) + { + properties.add( FACING, TOP, BOTTOM ); + } + @Nullable @Override public BlockState getStateForPlacement( BlockPlaceContext placement ) { - return defaultBlockState().setValue( FACING, - placement.getHorizontalDirection() - .getOpposite() ); + return defaultBlockState().setValue( FACING, placement.getHorizontalDirection().getOpposite() ); } @Override public void playerDestroy( @Nonnull Level world, @Nonnull Player player, @Nonnull BlockPos pos, @Nonnull BlockState state, @Nullable BlockEntity te, @Nonnull ItemStack stack ) { - if( te instanceof Nameable && ((Nameable) te).hasCustomName() ) + if( te instanceof Nameable nameable && nameable.hasCustomName() ) { player.awardStat( Stats.BLOCK_MINED.get( this ) ); player.causeFoodExhaustion( 0.005F ); ItemStack result = new ItemStack( this ); - result.setHoverName( ((Nameable) te).getCustomName() ); + result.setHoverName( nameable.getCustomName() ); popResource( world, pos, result ); } else @@ -73,26 +76,9 @@ public void playerDestroy( @Nonnull Level world, @Nonnull Player player, @Nonnul @Override public void setPlacedBy( @Nonnull Level world, @Nonnull BlockPos pos, @Nonnull BlockState state, LivingEntity placer, ItemStack stack ) { - if( stack.hasCustomHoverName() ) + if( stack.hasCustomHoverName() && world.getBlockEntity( pos ) instanceof TilePrinter printer ) { - BlockEntity tileentity = world.getBlockEntity( pos ); - if( tileentity instanceof TilePrinter ) - { - ((TilePrinter) tileentity).customName = stack.getHoverName(); - } + printer.customName = stack.getHoverName(); } } - - @Override - protected void createBlockStateDefinition( StateDefinition.Builder properties ) - { - properties.add( FACING, TOP, BOTTOM ); - } - - @Nullable - @Override - public BlockEntity newBlockEntity( BlockPos pos, BlockState state ) - { - return new TilePrinter( ComputerCraftRegistry.ModTiles.PRINTER, pos, state ); - } } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/printer/ContainerPrinter.java b/src/main/java/dan200/computercraft/shared/peripheral/printer/ContainerPrinter.java index 0e0e04e16..d1b485f39 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/printer/ContainerPrinter.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/printer/ContainerPrinter.java @@ -3,10 +3,9 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.peripheral.printer; -import dan200.computercraft.shared.ComputerCraftRegistry; +import dan200.computercraft.shared.Registry; import dan200.computercraft.shared.util.SingleIntArray; import net.minecraft.world.Container; import net.minecraft.world.SimpleContainer; @@ -25,14 +24,9 @@ public class ContainerPrinter extends AbstractContainerMenu private final Container inventory; private final ContainerData properties; - public ContainerPrinter( int id, Inventory player ) - { - this( id, player, new SimpleContainer( TilePrinter.SLOTS ), new SimpleContainerData( 1 ) ); - } - private ContainerPrinter( int id, Inventory player, Container inventory, ContainerData properties ) { - super( ComputerCraftRegistry.ModContainers.PRINTER, id ); + super( Registry.ModContainers.PRINTER, id ); this.properties = properties; this.inventory = inventory; @@ -42,16 +36,10 @@ private ContainerPrinter( int id, Inventory player, Container inventory, Contain addSlot( new Slot( inventory, 0, 13, 35 ) ); // In-tray - for( int x = 0; x < 6; x++ ) - { - addSlot( new Slot( inventory, x + 1, 61 + x * 18, 22 ) ); - } + for( int x = 0; x < 6; x++ ) addSlot( new Slot( inventory, x + 1, 61 + x * 18, 22 ) ); // Out-tray - for( int x = 0; x < 6; x++ ) - { - addSlot( new Slot( inventory, x + 7, 61 + x * 18, 49 ) ); - } + for( int x = 0; x < 6; x++ ) addSlot( new Slot( inventory, x + 7, 61 + x * 18, 49 ) ); // Player inv for( int y = 0; y < 3; y++ ) @@ -69,9 +57,14 @@ private ContainerPrinter( int id, Inventory player, Container inventory, Contain } } + public ContainerPrinter( int id, Inventory player ) + { + this( id, player, new SimpleContainer( TilePrinter.SLOTS ), new SimpleContainerData( 1 ) ); + } + public ContainerPrinter( int id, Inventory player, TilePrinter printer ) { - this( id, player, printer, (SingleIntArray) () -> printer.isPrinting() ? 1 : 0 ); + this( id, player, printer, (SingleIntArray) (() -> printer.isPrinting() ? 1 : 0) ); } public boolean isPrinting() @@ -79,41 +72,35 @@ public boolean isPrinting() return properties.get( 0 ) != 0; } + @Override + public boolean stillValid( @Nonnull Player player ) + { + return inventory.stillValid( player ); + } + @Nonnull @Override public ItemStack quickMoveStack( @Nonnull Player player, int index ) { Slot slot = slots.get( index ); - if( slot == null || !slot.hasItem() ) - { - return ItemStack.EMPTY; - } + if( slot == null || !slot.hasItem() ) return ItemStack.EMPTY; ItemStack stack = slot.getItem(); ItemStack result = stack.copy(); if( index < 13 ) { // Transfer from printer to inventory - if( !moveItemStackTo( stack, 13, 49, true ) ) - { - return ItemStack.EMPTY; - } + if( !moveItemStackTo( stack, 13, 49, true ) ) return ItemStack.EMPTY; } else { // Transfer from inventory to printer if( TilePrinter.isInk( stack ) ) { - if( !moveItemStackTo( stack, 0, 1, false ) ) - { - return ItemStack.EMPTY; - } + if( !moveItemStackTo( stack, 0, 1, false ) ) return ItemStack.EMPTY; } else //if is paper { - if( !moveItemStackTo( stack, 1, 13, false ) ) - { - return ItemStack.EMPTY; - } + if( !moveItemStackTo( stack, 1, 13, false ) ) return ItemStack.EMPTY; } } @@ -126,18 +113,9 @@ public ItemStack quickMoveStack( @Nonnull Player player, int index ) slot.setChanged(); } - if( stack.getCount() == result.getCount() ) - { - return ItemStack.EMPTY; - } + if( stack.getCount() == result.getCount() ) return ItemStack.EMPTY; slot.onTake( player, stack ); return result; } - - @Override - public boolean stillValid( @Nonnull Player player ) - { - return inventory.stillValid( player ); - } } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/printer/PrinterPeripheral.java b/src/main/java/dan200/computercraft/shared/peripheral/printer/PrinterPeripheral.java index 3627af739..3bd2d2e9b 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/printer/PrinterPeripheral.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/printer/PrinterPeripheral.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.peripheral.printer; import dan200.computercraft.api.lua.IArguments; @@ -43,19 +42,6 @@ public String getType() // FIXME: None of our page modification functions actually mark the tile as dirty, so the page may not be // persisted correctly. - @Nonnull - @Override - public Object getTarget() - { - return printer; - } - - @Override - public boolean equals( IPeripheral other ) - { - return other instanceof PrinterPeripheral && ((PrinterPeripheral) other).printer == printer; - } - /** * Writes text to the current page. * @@ -72,17 +58,6 @@ public final void write( IArguments arguments ) throws LuaException page.setCursorPos( page.getCursorX() + text.length(), page.getCursorY() ); } - @Nonnull - private Terminal getCurrentPage() throws LuaException - { - Terminal currentPage = printer.getCurrentPage(); - if( currentPage == null ) - { - throw new LuaException( "Page not started" ); - } - return currentPage; - } - /** * Returns the current position of the cursor on the page. * @@ -189,4 +164,25 @@ public final int getPaperLevel() { return printer.getPaperLevel(); } + + @Override + public boolean equals( IPeripheral other ) + { + return this == other || (other instanceof PrinterPeripheral otherPrinter && otherPrinter.printer == printer); + } + + @Nonnull + @Override + public Object getTarget() + { + return printer; + } + + @Nonnull + private Terminal getCurrentPage() throws LuaException + { + Terminal currentPage = printer.getCurrentPage(); + if( currentPage == null ) throw new LuaException( "Page not started" ); + return currentPage; + } } 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 3732a3ce1..a2697f134 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/printer/TilePrinter.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/printer/TilePrinter.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.peripheral.printer; import dan200.computercraft.api.peripheral.IPeripheral; @@ -11,10 +10,7 @@ import dan200.computercraft.core.terminal.Terminal; import dan200.computercraft.shared.common.TileGeneric; import dan200.computercraft.shared.media.items.ItemPrintout; -import dan200.computercraft.shared.util.ColourUtils; -import dan200.computercraft.shared.util.DefaultSidedInventory; -import dan200.computercraft.shared.util.ItemStorage; -import dan200.computercraft.shared.util.WorldUtil; +import dan200.computercraft.shared.util.*; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.NonNullList; @@ -37,33 +33,24 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; -public final class TilePrinter extends TileGeneric implements DefaultSidedInventory, IPeripheralTile, Nameable, MenuProvider +public final class TilePrinter extends TileGeneric implements IPeripheralTile, DefaultSidedInventory, Nameable, MenuProvider { - static final int SLOTS = 13; private static final String NBT_NAME = "CustomName"; private static final String NBT_PRINTING = "Printing"; private static final String NBT_PAGE_TITLE = "PageTitle"; - private static final int[] BOTTOM_SLOTS = new int[] { - 7, - 8, - 9, - 10, - 11, - 12, - }; - private static final int[] TOP_SLOTS = new int[] { - 1, - 2, - 3, - 4, - 5, - 6, - }; + + static final int SLOTS = 13; + + private static final int[] BOTTOM_SLOTS = new int[] { 7, 8, 9, 10, 11, 12 }; + private static final int[] TOP_SLOTS = new int[] { 1, 2, 3, 4, 5, 6 }; private static final int[] SIDE_SLOTS = new int[] { 0 }; + + Component customName; + private final NonNullList inventory = NonNullList.withSize( SLOTS, ItemStack.EMPTY ); - private final ItemStorage itemHandlerAll = ItemStorage.wrap( this ); + private PrinterPeripheral peripheral; + private final Terminal page = new Terminal( ItemPrintout.LINE_MAX_LENGTH, ItemPrintout.LINES_PER_PAGE ); - Component customName; private String pageTitle = ""; private boolean printing = false; @@ -82,85 +69,12 @@ public void destroy() @Override public InteractionResult onActivate( Player player, InteractionHand hand, BlockHitResult hit ) { - if( player.isCrouching() ) - { - return InteractionResult.PASS; - } + if( player.isCrouching() ) return InteractionResult.PASS; - if( !getLevel().isClientSide ) - { - player.openMenu( this ); - } + if( !getLevel().isClientSide ) player.openMenu( this ); return InteractionResult.SUCCESS; } - private void ejectContents() - { - for( int i = 0; i < 13; i++ ) - { - ItemStack stack = inventory.get( i ); - if( !stack.isEmpty() ) - { - // Remove the stack from the inventory - setItem( i, ItemStack.EMPTY ); - - // Spawn the item in the world - WorldUtil.dropItemStack( stack, getLevel(), - Vec3.atLowerCornerOf( getBlockPos() ) - .add( 0.5, 0.75, 0.5 ) ); - } - } - } - - private void updateBlockState() - { - boolean top = false, bottom = false; - for( int i = 1; i < 7; i++ ) - { - ItemStack stack = inventory.get( i ); - if( !stack.isEmpty() && isPaper( stack ) ) - { - top = true; - break; - } - } - for( int i = 7; i < 13; i++ ) - { - ItemStack stack = inventory.get( i ); - if( !stack.isEmpty() && isPaper( stack ) ) - { - bottom = true; - break; - } - } - - updateBlockState( top, bottom ); - } - - private static boolean isPaper( @Nonnull ItemStack stack ) - { - Item item = stack.getItem(); - return item == Items.PAPER || (item instanceof ItemPrintout && ((ItemPrintout) item).getType() == ItemPrintout.Type.PAGE); - } - - private void updateBlockState( boolean top, boolean bottom ) - { - if( remove ) - { - return; - } - - BlockState state = getBlockState(); - if( state.getValue( BlockPrinter.TOP ) == top & state.getValue( BlockPrinter.BOTTOM ) == bottom ) - { - return; - } - - getLevel().setBlockAndUpdate( getBlockPos(), - state.setValue( BlockPrinter.TOP, top ) - .setValue( BlockPrinter.BOTTOM, bottom ) ); - } - @Override public void load( @Nonnull CompoundTag nbt ) { @@ -183,10 +97,7 @@ public void load( @Nonnull CompoundTag nbt ) @Override public void saveAdditional( @Nonnull CompoundTag nbt ) { - if( customName != null ) - { - nbt.putString( NBT_NAME, Component.Serializer.toJson( customName ) ); - } + if( customName != null ) nbt.putString( NBT_NAME, Component.Serializer.toJson( customName ) ); // Write page synchronized( page ) @@ -219,10 +130,7 @@ public boolean isEmpty() { for( ItemStack stack : inventory ) { - if( !stack.isEmpty() ) - { - return false; - } + if( !stack.isEmpty() ) return false; } return true; } @@ -234,15 +142,23 @@ public ItemStack getItem( int slot ) return inventory.get( slot ); } + @Nonnull + @Override + public ItemStack removeItemNoUpdate( int slot ) + { + ItemStack result = inventory.get( slot ); + inventory.set( slot, ItemStack.EMPTY ); + setChanged(); + updateBlockState(); + return result; + } + @Nonnull @Override public ItemStack removeItem( int slot, int count ) { ItemStack stack = inventory.get( slot ); - if( stack.isEmpty() ) - { - return ItemStack.EMPTY; - } + if( stack.isEmpty() ) return ItemStack.EMPTY; if( stack.getCount() <= count ) { @@ -251,8 +167,7 @@ public ItemStack removeItem( int slot, int count ) } ItemStack part = stack.split( count ); - if( inventory.get( slot ) - .isEmpty() ) + if( inventory.get( slot ).isEmpty() ) { inventory.set( slot, ItemStack.EMPTY ); updateBlockState(); @@ -261,19 +176,6 @@ public ItemStack removeItem( int slot, int count ) return part; } - @Nonnull - @Override - public ItemStack removeItemNoUpdate( int slot ) - { - ItemStack result = inventory.get( slot ); - inventory.set( slot, ItemStack.EMPTY ); - setChanged(); - updateBlockState(); - return result; - } - - // ISidedInventory implementation - @Override public void setItem( int slot, @Nonnull ItemStack stack ) { @@ -282,19 +184,10 @@ public void setItem( int slot, @Nonnull ItemStack stack ) updateBlockState(); } - @Override - public boolean stillValid( @Nonnull Player playerEntity ) - { - return isUsable( playerEntity, false ); - } - @Override public void clearContent() { - for( int i = 0; i < inventory.size(); i++ ) - { - inventory.set( i, ItemStack.EMPTY ); - } + for( int i = 0; i < inventory.size(); i++ ) inventory.set( i, ItemStack.EMPTY ); setChanged(); updateBlockState(); } @@ -316,11 +209,14 @@ else if( slot >= TOP_SLOTS[0] && slot <= TOP_SLOTS[TOP_SLOTS.length - 1] ) } } - static boolean isInk( @Nonnull ItemStack stack ) + @Override + public boolean stillValid( @Nonnull Player playerEntity ) { - return ColourUtils.getStackColour( stack ) != null; + return isUsable( playerEntity, false ); } + // ISidedInventory implementation + @Nonnull @Override public int[] getSlotsForFace( @Nonnull Direction side ) @@ -336,13 +232,6 @@ public int[] getSlotsForFace( @Nonnull Direction side ) } } - @Nonnull - @Override - public IPeripheral getPeripheral( Direction side ) - { - return new PrinterPeripheral( this ); - } - @Nullable Terminal getCurrentPage() { @@ -356,14 +245,8 @@ boolean startNewPage() { synchronized( page ) { - if( !canInputPage() ) - { - return false; - } - if( printing && !outputPage() ) - { - return false; - } + if( !canInputPage() ) return false; + if( printing && !outputPage() ) return false; return inputPage(); } } @@ -376,33 +259,6 @@ boolean endCurrentPage() } } - private boolean outputPage() - { - int height = page.getHeight(); - String[] lines = new String[height]; - String[] colours = new String[height]; - for( int i = 0; i < height; i++ ) - { - lines[i] = page.getLine( i ) - .toString(); - colours[i] = page.getTextColourLine( i ) - .toString(); - } - - ItemStack stack = ItemPrintout.createSingleFromTitleAndText( pageTitle, lines, colours ); - for( int slot : BOTTOM_SLOTS ) - { - if( inventory.get( slot ) - .isEmpty() ) - { - setItem( slot, stack ); - printing = false; - return true; - } - } - return false; - } - int getInkLevel() { ItemStack inkStack = inventory.get( 0 ); @@ -415,10 +271,7 @@ int getPaperLevel() for( int i = 1; i < 7; i++ ) { ItemStack paperStack = inventory.get( i ); - if( isPaper( paperStack ) ) - { - count += paperStack.getCount(); - } + if( isPaper( paperStack ) ) count += paperStack.getCount(); } return count; } @@ -427,13 +280,22 @@ void setPageTitle( String title ) { synchronized( page ) { - if( printing ) - { - pageTitle = title; - } + if( printing ) pageTitle = title; } } + static boolean isInk( @Nonnull ItemStack stack ) + { + return ColourUtils.getStackColour( stack ) != null; + } + + private static boolean isPaper( @Nonnull ItemStack stack ) + { + Item item = stack.getItem(); + return item == Items.PAPER + || (item instanceof ItemPrintout printout && printout.getType() == ItemPrintout.Type.PAGE); + } + private boolean canInputPage() { ItemStack inkStack = inventory.get( 0 ); @@ -449,10 +311,7 @@ private boolean inputPage() for( int i = 1; i < 7; i++ ) { ItemStack paperStack = inventory.get( i ); - if( paperStack.isEmpty() || !isPaper( paperStack ) ) - { - continue; - } + if( paperStack.isEmpty() || !isPaper( paperStack ) ) continue; // Setup the new page page.setTextColour( dye.getId() ); @@ -476,10 +335,7 @@ private boolean inputPage() // Decrement ink inkStack.shrink( 1 ); - if( inkStack.isEmpty() ) - { - inventory.set( 0, ItemStack.EMPTY ); - } + if( inkStack.isEmpty() ) inventory.set( 0, ItemStack.EMPTY ); // Decrement paper paperStack.shrink( 1 ); @@ -496,12 +352,87 @@ private boolean inputPage() return false; } + private boolean outputPage() + { + int height = page.getHeight(); + String[] lines = new String[height]; + String[] colours = new String[height]; + for( int i = 0; i < height; i++ ) + { + lines[i] = page.getLine( i ).toString(); + colours[i] = page.getTextColourLine( i ).toString(); + } + + ItemStack stack = ItemPrintout.createSingleFromTitleAndText( pageTitle, lines, colours ); + for( int slot : BOTTOM_SLOTS ) + { + if( inventory.get( slot ).isEmpty() ) + { + setItem( slot, stack ); + printing = false; + return true; + } + } + return false; + } + + private void ejectContents() + { + for( int i = 0; i < 13; i++ ) + { + ItemStack stack = inventory.get( i ); + if( !stack.isEmpty() ) + { + // Remove the stack from the inventory + setItem( i, ItemStack.EMPTY ); + + // Spawn the item in the world + WorldUtil.dropItemStack( stack, getLevel(), Vec3.atLowerCornerOf( getBlockPos() ).add( 0.5, 0.75, 0.5 ) ); + } + } + } + + private void updateBlockState() + { + boolean top = false, bottom = false; + for( int i = 1; i < 7; i++ ) + { + ItemStack stack = inventory.get( i ); + if( !stack.isEmpty() && isPaper( stack ) ) + { + top = true; + break; + } + } + for( int i = 7; i < 13; i++ ) + { + ItemStack stack = inventory.get( i ); + if( !stack.isEmpty() && isPaper( stack ) ) + { + bottom = true; + break; + } + } + + updateBlockState( top, bottom ); + } + + private void updateBlockState( boolean top, boolean bottom ) + { + if( remove || level == null ) return; + + BlockState state = getBlockState(); + if( state.getValue( BlockPrinter.TOP ) == top & state.getValue( BlockPrinter.BOTTOM ) == bottom ) return; + + getLevel().setBlockAndUpdate( getBlockPos(), state.setValue( BlockPrinter.TOP, top ).setValue( BlockPrinter.BOTTOM, bottom ) ); + } + @Nonnull @Override - public Component getName() + public IPeripheral getPeripheral( Direction side ) { - return customName != null ? customName : new TranslatableComponent( getBlockState().getBlock() - .getDescriptionId() ); + if( peripheral == null ) peripheral = new PrinterPeripheral( this ); + return peripheral; } @Override @@ -510,17 +441,25 @@ public boolean hasCustomName() return customName != null; } + @Nullable @Override - public Component getDisplayName() + public Component getCustomName() { - return Nameable.super.getDisplayName(); + return customName; } - @Nullable + @Nonnull @Override - public Component getCustomName() + public Component getName() { - return customName; + return customName != null ? customName : new TranslatableComponent( getBlockState().getBlock().getDescriptionId() ); + } + + @Nonnull + @Override + public Component getDisplayName() + { + return Nameable.super.getDisplayName(); } @Nonnull 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 73ac34bfc..3d7ec3fea 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/speaker/BlockSpeaker.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/speaker/BlockSpeaker.java @@ -3,15 +3,14 @@ * 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.shared.ComputerCraftRegistry; +import dan200.computercraft.shared.Registry; import dan200.computercraft.shared.common.BlockGeneric; -import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.BaseEntityBlock; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityTicker; @@ -21,28 +20,22 @@ import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.block.state.properties.DirectionProperty; +import javax.annotation.Nonnull; import javax.annotation.Nullable; public class BlockSpeaker extends BlockGeneric { private static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING; + private static final BlockEntityTicker serverTicker = ( level, pos, state, drive ) -> drive.serverTick(); + public BlockSpeaker( Properties settings ) { - super( settings, ComputerCraftRegistry.ModTiles.SPEAKER ); + super( settings, () -> Registry.ModBlockEntities.SPEAKER ); registerDefaultState( getStateDefinition().any() .setValue( FACING, Direction.NORTH ) ); } - @Nullable - @Override - public BlockState getStateForPlacement( BlockPlaceContext placement ) - { - return defaultBlockState().setValue( FACING, - placement.getHorizontalDirection() - .getOpposite() ); - } - @Override protected void createBlockStateDefinition( StateDefinition.Builder properties ) { @@ -51,16 +44,15 @@ protected void createBlockStateDefinition( StateDefinition.Builder BlockEntityTicker getTicker( Level world, BlockState state, BlockEntityType type ) + @Nullable + public BlockEntityTicker getTicker( @Nonnull Level level, @Nonnull BlockState state, @Nonnull BlockEntityType type ) { - return world.isClientSide ? null : BlockSpeaker.createTickerHelper( type, ComputerCraftRegistry.ModTiles.SPEAKER, TileSpeaker::tick ); + return level.isClientSide ? null : BaseEntityBlock.createTickerHelper( type, Registry.ModBlockEntities.SPEAKER, serverTicker ); } - } 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 88c5655d1..bc81593e7 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPeripheral.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/speaker/SpeakerPeripheral.java @@ -3,7 +3,6 @@ * 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.ComputerCraft; @@ -36,14 +35,15 @@ * Speakers allow playing notes and other sounds. * * @cc.module speaker + * @cc.since 1.80pr1 */ 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 final AtomicInteger notesThisTick = new AtomicInteger(); private long lastPositionTime; private Vec3 lastPosition; @@ -59,18 +59,22 @@ public void update() if( lastPlayTime > 0 && (clock - lastPositionTime) >= 20 ) { Vec3 position = getPosition(); - if( lastPosition == null || lastPosition.distanceTo( position ) >= 0.1 ) + if( lastPosition == null || lastPosition.distanceToSqr( position ) >= 0.1 ) { lastPosition = position; lastPositionTime = clock; NetworkHandler.sendToAllTracking( new SpeakerMoveClientMessage( getSource(), position ), - getWorld().getChunkAt( new BlockPos( position ) ) + getLevel().getChunkAt( new BlockPos( position ) ) ); } } } + public abstract Level getLevel(); + + public abstract Vec3 getPosition(); + protected abstract UUID getSource(); public boolean madeSound( long ticks ) @@ -88,8 +92,9 @@ public String getType() /** * Plays a sound through the speaker. * - * This plays sounds similar to the {@code /playsound} command in Minecraft. It takes the namespaced path of a sound (e.g. {@code - * minecraft:block.note_block.harp}) with an optional volume and speed multiplier, and plays it through the speaker. + * This plays sounds similar to the {@code /playsound} command in Minecraft. + * It takes the namespaced path of a sound (e.g. {@code minecraft:block.note_block.harp}) + * with an optional volume and speed multiplier, and plays it through the speaker. * * @param context The Lua context * @param name The name of the sound to play. @@ -117,6 +122,48 @@ public final boolean playSound( ILuaContext context, String name, Optional volumeA, Optional pitchA ) throws LuaException + { + float volume = (float) checkFinite( 1, volumeA.orElse( 1.0 ) ); + float pitch = (float) checkFinite( 2, pitchA.orElse( 1.0 ) ); + + NoteBlockInstrument instrument = null; + for( NoteBlockInstrument testInstrument : NoteBlockInstrument.values() ) + { + if( testInstrument.getSerializedName().equalsIgnoreCase( name ) ) + { + instrument = testInstrument; + break; + } + } + + // Check if the note exists + if( instrument == null ) throw new LuaException( "Invalid instrument, \"" + name + "\"!" ); + + // If the resource location for note block notes changes, this method call will need to be updated + boolean success = playSound( context, instrument.getSoundEvent().getLocation(), volume, (float) Math.pow( 2.0, (pitch - 12.0) / 12.0 ), true ); + if( success ) notesThisTick.incrementAndGet(); + return success; + } + private synchronized boolean playSound( ILuaContext context, ResourceLocation name, float volume, float pitch, boolean isNote ) throws LuaException { if( clock - lastPlayTime < MIN_TICKS_BETWEEN_SOUNDS ) @@ -127,7 +174,7 @@ private synchronized boolean playSound( ILuaContext context, ResourceLocation na if( clock - lastPlayTime != 0 || notesThisTick.get() >= ComputerCraft.maxNotesPerTick ) return false; } - Level world = getWorld(); + Level world = getLevel(); Vec3 pos = getPosition(); float actualVolume = Mth.clamp( volume, 0.0f, 3.0f ); @@ -135,10 +182,7 @@ private synchronized boolean playSound( ILuaContext context, ResourceLocation na context.issueMainThreadTask( () -> { MinecraftServer server = world.getServer(); - if( server == null ) - { - return null; - } + if( server == null ) return null; if( isNote ) { @@ -160,59 +204,4 @@ private synchronized boolean playSound( ILuaContext context, ResourceLocation na lastPlayTime = clock; return true; } - - public abstract Level getWorld(); - - public abstract Vec3 getPosition(); - - /** - * Plays a note block note through the speaker. - * - * This takes the name of a note to play, as well as optionally the volume and pitch to play the note at. - * - * The pitch argument uses semitones as the unit. This directly maps to the number of clicks on a note block. For reference, 0, 12, and 24 map to F#, - * and 6 and 18 map to C. - * - * @param context The Lua context - * @param name The name of the note to play. - * @param volumeA The volume to play the note at, from 0.0 to 3.0. Defaults to 1.0. - * @param pitchA The pitch to play the note at in semitones, from 0 to 24. Defaults to 12. - * @return Whether the note could be played. - * @throws LuaException If the instrument doesn't exist. - */ - @LuaFunction - public final synchronized boolean playNote( ILuaContext context, String name, Optional volumeA, Optional pitchA ) throws LuaException - { - float volume = (float) checkFinite( 1, volumeA.orElse( 1.0 ) ); - float pitch = (float) checkFinite( 2, pitchA.orElse( 1.0 ) ); - - NoteBlockInstrument instrument = null; - for( NoteBlockInstrument testInstrument : NoteBlockInstrument.values() ) - { - if( testInstrument.getSerializedName() - .equalsIgnoreCase( name ) ) - { - instrument = testInstrument; - break; - } - } - - // Check if the note exists - if( instrument == null ) - { - throw new LuaException( "Invalid instrument, \"" + name + "\"!" ); - } - - // If the resource location for note block notes changes, this method call will need to be updated - boolean success = playSound( context, - instrument.getSoundEvent().getLocation(), - volume, - (float) Math.pow( 2.0, (pitch - 12.0) / 12.0 ), - true ); - if( success ) - { - notesThisTick.incrementAndGet(); - } - return success; - } } 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 336d81ca2..3d96a0ba0 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/speaker/TileSpeaker.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/speaker/TileSpeaker.java @@ -3,18 +3,20 @@ * 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.IPeripheral; import dan200.computercraft.api.peripheral.IPeripheralTile; import dan200.computercraft.shared.common.TileGeneric; +import dan200.computercraft.shared.network.NetworkHandler; +import dan200.computercraft.shared.network.client.SpeakerStopClientMessage; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.entity.BlockEntityType; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.Vec3; +import org.jetbrains.annotations.NotNull; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -22,8 +24,6 @@ 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(); @@ -33,14 +33,24 @@ public TileSpeaker( BlockEntityType type, BlockPos pos, BlockState peripheral = new Peripheral( this ); } - public static void tick( Level world, BlockPos pos, BlockState state, TileSpeaker tileSpeaker ) + protected void serverTick() + { + peripheral.update(); + } + + @Override + public void setRemoved() { - tileSpeaker.peripheral.update(); + super.setRemoved(); + if( level != null && !level.isClientSide ) + { + NetworkHandler.sendToAllPlayers( new SpeakerStopClientMessage( source ) ); + } } @Nonnull @Override - public IPeripheral getPeripheral( Direction side ) + public IPeripheral getPeripheral( @NotNull Direction side ) { return peripheral; } @@ -55,7 +65,7 @@ private Peripheral( TileSpeaker speaker ) } @Override - public Level getWorld() + public Level getLevel() { return speaker.getLevel(); } diff --git a/src/main/java/dan200/computercraft/shared/peripheral/speaker/UpgradeSpeakerPeripheral.java b/src/main/java/dan200/computercraft/shared/peripheral/speaker/UpgradeSpeakerPeripheral.java index cbe318845..d10910298 100644 --- a/src/main/java/dan200/computercraft/shared/peripheral/speaker/UpgradeSpeakerPeripheral.java +++ b/src/main/java/dan200/computercraft/shared/peripheral/speaker/UpgradeSpeakerPeripheral.java @@ -8,6 +8,8 @@ import dan200.computercraft.api.peripheral.IComputerAccess; import dan200.computercraft.shared.network.NetworkHandler; import dan200.computercraft.shared.network.client.SpeakerStopClientMessage; +import me.shedaniel.cloth.api.utils.v1.GameInstanceUtils; +import net.minecraft.server.MinecraftServer; import javax.annotation.Nonnull; import java.util.UUID; @@ -17,6 +19,8 @@ */ public abstract class UpgradeSpeakerPeripheral extends SpeakerPeripheral { + public static final String ADJECTIVE = "upgrade.computercraft.speaker.adjective"; + private final UUID source = UUID.randomUUID(); @Override @@ -28,6 +32,10 @@ protected final UUID getSource() @Override public void detach( @Nonnull IComputerAccess computer ) { + // We could be in the process of shutting down the server, so we can't send packets in this case. + MinecraftServer server = GameInstanceUtils.getServer(); + if( server == null || server.isStopped() ) return; + 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 847b37d19..6a5351656 100644 --- a/src/main/java/dan200/computercraft/shared/pocket/apis/PocketAPI.java +++ b/src/main/java/dan200/computercraft/shared/pocket/apis/PocketAPI.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.pocket.apis; import dan200.computercraft.api.lua.ILuaAPI; @@ -23,15 +22,16 @@ /** * Control the current pocket computer, adding or removing upgrades. * - * This API is only available on pocket computers. As such, you may use its presence to determine what kind of computer you are using: + * This API is only available on pocket computers. As such, you may use its presence to determine what kind of computer + * you are using: * - *
+ * 
{@code
  * if pocket then
  *   print("On a pocket computer")
  * else
  *   print("On something else")
  * end
- * 
+ * }
* * @cc.module pocket */ @@ -63,11 +63,7 @@ public String[] getNames() public final Object[] equipBack() { Entity entity = computer.getEntity(); - if( !(entity instanceof Player) ) - { - return new Object[] { false, "Cannot find player" }; - } - Player player = (Player) entity; + if( !(entity instanceof Player player) ) return new Object[] { false, "Cannot find player" }; Inventory inventory = player.getInventory(); IPocketUpgrade previousUpgrade = computer.getUpgrade(); @@ -78,10 +74,7 @@ public final Object[] equipBack() { newUpgrade = findUpgrade( inventory.offhand, 0, previousUpgrade ); } - if( newUpgrade == null ) - { - return new Object[] { false, "Cannot find a valid upgrade" }; - } + if( newUpgrade == null ) return new Object[] { false, "Cannot find a valid upgrade" }; // Remove the current upgrade if( previousUpgrade != null ) @@ -103,30 +96,6 @@ public final Object[] equipBack() return new Object[] { true }; } - private static IPocketUpgrade findUpgrade( NonNullList inv, int start, IPocketUpgrade previous ) - { - for( int i = 0; i < inv.size(); i++ ) - { - ItemStack invStack = inv.get( (i + start) % inv.size() ); - if( !invStack.isEmpty() ) - { - IPocketUpgrade newUpgrade = PocketUpgrades.get( invStack ); - - if( newUpgrade != null && newUpgrade != previous ) - { - // Consume an item from this stack and exit the loop - invStack = invStack.copy(); - invStack.shrink( 1 ); - inv.set( (i + start) % inv.size(), invStack.isEmpty() ? ItemStack.EMPTY : invStack ); - - return newUpgrade; - } - } - } - - return null; - } - /** * Remove the pocket computer's current upgrade. * @@ -138,18 +107,11 @@ private static IPocketUpgrade findUpgrade( NonNullList inv, int start public final Object[] unequipBack() { Entity entity = computer.getEntity(); - if( !(entity instanceof Player) ) - { - return new Object[] { false, "Cannot find player" }; - } - Player player = (Player) entity; + if( !(entity instanceof Player player) ) return new Object[] { false, "Cannot find player" }; Inventory inventory = player.getInventory(); IPocketUpgrade previousUpgrade = computer.getUpgrade(); - if( previousUpgrade == null ) - { - return new Object[] { false, "Nothing to unequip" }; - } + if( previousUpgrade == null ) return new Object[] { false, "Nothing to unequip" }; computer.setUpgrade( null ); @@ -165,4 +127,28 @@ public final Object[] unequipBack() return new Object[] { true }; } + + private static IPocketUpgrade findUpgrade( NonNullList inv, int start, IPocketUpgrade previous ) + { + for( int i = 0; i < inv.size(); i++ ) + { + ItemStack invStack = inv.get( (i + start) % inv.size() ); + if( !invStack.isEmpty() ) + { + IPocketUpgrade newUpgrade = PocketUpgrades.get( invStack ); + + if( newUpgrade != null && newUpgrade != previous ) + { + // Consume an item from this stack and exit the loop + invStack = invStack.copy(); + invStack.shrink( 1 ); + inv.set( (i + start) % inv.size(), invStack.isEmpty() ? ItemStack.EMPTY : invStack ); + + return newUpgrade; + } + } + } + + return 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 59abef12b..d5b7cb568 100644 --- a/src/main/java/dan200/computercraft/shared/pocket/core/PocketServerComputer.java +++ b/src/main/java/dan200/computercraft/shared/pocket/core/PocketServerComputer.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.pocket.core; import dan200.computercraft.ComputerCraft; @@ -16,8 +15,8 @@ import dan200.computercraft.shared.computer.core.ServerComputer; import dan200.computercraft.shared.network.NetworkHandler; import dan200.computercraft.shared.pocket.items.ItemPocketComputer; -import dan200.computercraft.shared.util.NBTUtil; import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.Tag; import net.minecraft.resources.ResourceLocation; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.entity.Entity; @@ -50,19 +49,15 @@ public PocketServerComputer( Level world, int computerID, String label, int inst public Entity getEntity() { Entity entity = this.entity; - if( entity == null || stack == null || !entity.isAlive() ) - { - return null; - } + if( entity == null || stack == null || !entity.isAlive() ) return null; if( entity instanceof Player ) { Inventory inventory = ((Player) entity).getInventory(); return inventory.items.contains( stack ) || inventory.offhand.contains( stack ) ? entity : null; } - else if( entity instanceof LivingEntity ) + else if( entity instanceof LivingEntity living ) { - LivingEntity living = (LivingEntity) entity; return living.getMainHandItem() == stack || living.getOffhandItem() == stack ? entity : null; } else @@ -88,7 +83,7 @@ public void setColour( int colour ) public int getLight() { CompoundTag tag = getUserData(); - return tag.contains( NBT_LIGHT, NBTUtil.TAG_ANY_NUMERIC ) ? tag.getInt( NBT_LIGHT ) : -1; + return tag.contains( NBT_LIGHT, Tag.TAG_ANY_NUMERIC ) ? tag.getInt( NBT_LIGHT ) : -1; } @Override @@ -97,13 +92,13 @@ public void setLight( int colour ) CompoundTag tag = getUserData(); if( colour >= 0 && colour <= 0xFFFFFF ) { - if( !tag.contains( NBT_LIGHT, NBTUtil.TAG_ANY_NUMERIC ) || tag.getInt( NBT_LIGHT ) != colour ) + if( !tag.contains( NBT_LIGHT, Tag.TAG_ANY_NUMERIC ) || tag.getInt( NBT_LIGHT ) != colour ) { tag.putInt( NBT_LIGHT, colour ); updateUserData(); } } - else if( tag.contains( NBT_LIGHT, NBTUtil.TAG_ANY_NUMERIC ) ) + else if( tag.contains( NBT_LIGHT, Tag.TAG_ANY_NUMERIC ) ) { tag.remove( NBT_LIGHT ); updateUserData(); @@ -120,10 +115,7 @@ public CompoundTag getUpgradeNBTData() @Override public void updateUpgradeNBTData() { - if( entity instanceof Player ) - { - ((Player) entity).getInventory().setChanged(); - } + if( entity instanceof Player player ) player.getInventory().setChanged(); } @Override @@ -154,10 +146,7 @@ public IPocketUpgrade getUpgrade() */ public void setUpgrade( IPocketUpgrade upgrade ) { - if( this.upgrade == upgrade ) - { - return; - } + if( this.upgrade == upgrade ) return; synchronized( this ) { @@ -172,15 +161,12 @@ public synchronized void updateValues( Entity entity, @Nonnull ItemStack stack, { if( entity != null ) { - setWorld( entity.getCommandSenderWorld() ); + setLevel( entity.getCommandSenderWorld() ); setPosition( entity.blockPosition() ); } // If a new entity has picked it up then rebroadcast the terminal to them - if( entity != this.entity && entity instanceof ServerPlayer ) - { - markTerminalChanged(); - } + if( entity != this.entity && entity instanceof ServerPlayer ) markTerminalChanged(); this.entity = entity; this.stack = stack; @@ -197,10 +183,9 @@ public void broadcastState( boolean force ) { super.broadcastState( force ); - if( (hasTerminalChanged() || force) && entity instanceof ServerPlayer ) + if( (hasTerminalChanged() || force) && entity instanceof ServerPlayer player ) { // Broadcast the state to the current entity if they're not already interacting with it. - ServerPlayer player = (ServerPlayer) entity; if( player.connection != null && !isInteracting( player ) ) { NetworkHandler.sendToPlayer( player, createTerminalPacket() ); diff --git a/src/main/java/dan200/computercraft/shared/pocket/inventory/PocketComputerMenuProvider.java b/src/main/java/dan200/computercraft/shared/pocket/inventory/PocketComputerMenuProvider.java index 8cef2a65c..e29c1e04c 100644 --- a/src/main/java/dan200/computercraft/shared/pocket/inventory/PocketComputerMenuProvider.java +++ b/src/main/java/dan200/computercraft/shared/pocket/inventory/PocketComputerMenuProvider.java @@ -5,7 +5,7 @@ */ package dan200.computercraft.shared.pocket.inventory; -import dan200.computercraft.shared.ComputerCraftRegistry; +import dan200.computercraft.shared.Registry; import dan200.computercraft.shared.computer.core.ServerComputer; import dan200.computercraft.shared.computer.inventory.ComputerMenuWithoutInventory; import dan200.computercraft.shared.pocket.items.ItemPocketComputer; @@ -53,7 +53,7 @@ public Component getDisplayName() public AbstractContainerMenu createMenu( int id, @Nonnull Inventory inventory, @Nonnull Player entity ) { return new ComputerMenuWithoutInventory( - isTypingOnly ? ComputerCraftRegistry.ModContainers.POCKET_COMPUTER_NO_TERM : ComputerCraftRegistry.ModContainers.POCKET_COMPUTER, id, inventory, + isTypingOnly ? Registry.ModContainers.POCKET_COMPUTER_NO_TERM : Registry.ModContainers.POCKET_COMPUTER, id, inventory, p -> { ItemStack stack = p.getItemInHand( hand ); return stack.getItem() == item && ItemPocketComputer.getServerComputer( stack ) == computer; 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 b98426359..115ce363c 100644 --- a/src/main/java/dan200/computercraft/shared/pocket/items/ItemPocketComputer.java +++ b/src/main/java/dan200/computercraft/shared/pocket/items/ItemPocketComputer.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.pocket.items; import com.google.common.base.Objects; @@ -24,8 +23,6 @@ import dan200.computercraft.shared.pocket.apis.PocketAPI; import dan200.computercraft.shared.pocket.core.PocketServerComputer; import dan200.computercraft.shared.pocket.inventory.PocketComputerMenuProvider; -import net.fabricmc.api.EnvType; -import net.fabricmc.api.Environment; import net.minecraft.ChatFormatting; import net.minecraft.core.NonNullList; import net.minecraft.nbt.CompoundTag; @@ -50,9 +47,10 @@ public class ItemPocketComputer extends Item implements IComputerItem, IMedia, IColouredItem { - public static final String NBT_LIGHT = "Light"; private static final String NBT_UPGRADE = "Upgrade"; private static final String NBT_UPGRADE_INFO = "UpgradeInfo"; + public static final String NBT_LIGHT = "Light"; + private static final String NBT_INSTANCE = "Instanceid"; private static final String NBT_SESSION = "SessionId"; @@ -64,111 +62,22 @@ public ItemPocketComputer( Properties settings, ComputerFamily family ) this.family = family; } - public static ServerComputer getServerComputer( @Nonnull ItemStack stack ) - { - int session = getSessionID( stack ); - if( session != ComputerCraft.serverComputerRegistry.getSessionID() ) return null; - - int instanceID = getInstanceID( stack ); - return instanceID >= 0 ? ComputerCraft.serverComputerRegistry.get( instanceID ) : null; - } - - @Environment( EnvType.CLIENT ) - public static ComputerState getState( @Nonnull ItemStack stack ) - { - ClientComputer computer = getClientComputer( stack ); - return computer == null ? ComputerState.OFF : computer.getState(); - } - - private static ClientComputer getClientComputer( @Nonnull ItemStack stack ) - { - int instanceID = getInstanceID( stack ); - return instanceID >= 0 ? ComputerCraft.clientComputerRegistry.get( instanceID ) : null; - } - - @Environment( EnvType.CLIENT ) - public static int getLightState( @Nonnull ItemStack stack ) - { - ClientComputer computer = getClientComputer( stack ); - if( computer != null && computer.isOn() ) - { - CompoundTag computerNBT = computer.getUserData(); - if( computerNBT != null && computerNBT.contains( NBT_LIGHT ) ) - { - return computerNBT.getInt( NBT_LIGHT ); - } - } - return -1; - } - - public static void setUpgrade( @Nonnull ItemStack stack, IPocketUpgrade upgrade ) - { - CompoundTag compound = stack.getOrCreateTag(); - - if( upgrade == null ) - { - compound.remove( NBT_UPGRADE ); - } - else - { - compound.putString( NBT_UPGRADE, - upgrade.getUpgradeID() - .toString() ); - } - - compound.remove( NBT_UPGRADE_INFO ); - } - - public static CompoundTag getUpgradeInfo( @Nonnull ItemStack stack ) + public ItemStack create( int id, String label, int colour, IPocketUpgrade upgrade ) { - return stack.getOrCreateTagElement( NBT_UPGRADE_INFO ); + ItemStack result = new ItemStack( this ); + if( id >= 0 ) result.getOrCreateTag().putInt( NBT_ID, id ); + if( label != null ) result.setHoverName( new TextComponent( label ) ); + if( upgrade != null ) result.getOrCreateTag().putString( NBT_UPGRADE, upgrade.getUpgradeID().toString() ); + if( colour != -1 ) result.getOrCreateTag().putInt( NBT_COLOUR, colour ); + return result; } - // @Nullable - // @Override - // public String getCreatorModId( ItemStack stack ) - // { - // IPocketUpgrade upgrade = getUpgrade( stack ); - // if( upgrade != null ) - // { - // // If we're a non-vanilla, non-CC upgrade then return whichever mod this upgrade - // // belongs to. - // String mod = PocketUpgrades.getOwner( upgrade ); - // if( mod != null && !mod.equals( ComputerCraft.MOD_ID ) ) return mod; - // } - // - // return super.getCreatorModId( stack ); - // } - - @Nonnull @Override - public InteractionResultHolder use( Level world, Player player, @Nonnull InteractionHand hand ) + public void fillItemCategory( @Nonnull CreativeModeTab group, @Nonnull NonNullList stacks ) { - ItemStack stack = player.getItemInHand( hand ); - if( !world.isClientSide ) - { - PocketServerComputer computer = createServerComputer( world, player.getInventory(), player, stack ); - - boolean stop = false; - if( computer != null ) - { - computer.turnOn(); - - IPocketUpgrade upgrade = getUpgrade( stack ); - if( upgrade != null ) - { - computer.updateValues( player, stack, upgrade ); - stop = upgrade.onRightClick( world, computer, computer.getPeripheral( ComputerSide.BACK ) ); - } - } - - if( !stop && computer != null ) - { - boolean isTypingOnly = hand == InteractionHand.OFF_HAND; - new ComputerContainerData( computer ).open( player, new PocketComputerMenuProvider( computer, stack, this, hand, isTypingOnly ) ); - } - } - return new InteractionResultHolder<>( InteractionResult.SUCCESS, stack ); + if( !allowdedIn( group ) ) return; + stacks.add( create( -1, null, -1, null ) ); + PocketUpgrades.getVanillaUpgrades().map( x -> create( -1, null, -1, x ) ).forEach( stacks::add ); } @Override @@ -185,7 +94,7 @@ public void inventoryTick( @Nonnull ItemStack stack, Level world, @Nonnull Entit // Ping computer computer.keepAlive(); - computer.setWorld( world ); + computer.setLevel( world ); computer.updateValues( entity, stack, upgrade ); // Sync ID @@ -193,10 +102,7 @@ public void inventoryTick( @Nonnull ItemStack stack, Level world, @Nonnull Entit if( id != getComputerID( stack ) ) { setComputerID( stack, id ); - if( inventory != null ) - { - inventory.setChanged(); - } + if( inventory != null ) inventory.setChanged(); } // Sync label @@ -204,10 +110,7 @@ public void inventoryTick( @Nonnull ItemStack stack, Level world, @Nonnull Entit if( !Objects.equal( label, getLabel( stack ) ) ) { setLabel( stack, label ); - if( inventory != null ) - { - inventory.setChanged(); - } + if( inventory != null ) inventory.setChanged(); } // Update pocket upgrade @@ -224,17 +127,35 @@ public void inventoryTick( @Nonnull ItemStack stack, Level world, @Nonnull Entit } } + @Nonnull @Override - public void appendHoverText( @Nonnull ItemStack stack, @Nullable Level world, @Nonnull List list, TooltipFlag flag ) + public InteractionResultHolder use( Level world, Player player, @Nonnull InteractionHand hand ) { - if( flag.isAdvanced() || getLabel( stack ) == null ) + ItemStack stack = player.getItemInHand( hand ); + if( !world.isClientSide ) { - int id = getComputerID( stack ); - if( id >= 0 ) + PocketServerComputer computer = createServerComputer( world, player.getInventory(), player, stack ); + + boolean stop = false; + if( computer != null ) + { + computer.turnOn(); + + IPocketUpgrade upgrade = getUpgrade( stack ); + if( upgrade != null ) + { + computer.updateValues( player, stack, upgrade ); + stop = upgrade.onRightClick( world, computer, computer.getPeripheral( ComputerSide.BACK ) ); + } + } + + if( !stop && computer != null ) { - list.add( new TranslatableComponent( "gui.computercraft.tooltip.computer_id", id ).withStyle( ChatFormatting.GRAY ) ); + boolean isTypingOnly = hand == InteractionHand.OFF_HAND; + new ComputerContainerData( computer ).open( player, new PocketComputerMenuProvider( computer, stack, this, hand, isTypingOnly ) ); } } + return new InteractionResultHolder<>( InteractionResult.SUCCESS, stack ); } @Nonnull @@ -245,7 +166,9 @@ public Component getName( @Nonnull ItemStack stack ) IPocketUpgrade upgrade = getUpgrade( stack ); if( upgrade != null ) { - return new TranslatableComponent( baseString + ".upgraded", new TranslatableComponent( upgrade.getUnlocalisedAdjective() ) ); + return new TranslatableComponent( baseString + ".upgraded", + new TranslatableComponent( upgrade.getUnlocalisedAdjective() ) + ); } else { @@ -253,62 +176,32 @@ public Component getName( @Nonnull ItemStack stack ) } } - // IComputerItem implementation @Override - public void fillItemCategory( @Nonnull CreativeModeTab group, @Nonnull NonNullList stacks ) - { - if( !allowdedIn( group ) ) - { - return; - } - stacks.add( create( -1, null, -1, null ) ); - for( IPocketUpgrade upgrade : PocketUpgrades.getVanillaUpgrades() ) - { - stacks.add( create( -1, null, -1, upgrade ) ); - } - } - - public ItemStack create( int id, String label, int colour, IPocketUpgrade upgrade ) + public void appendHoverText( @Nonnull ItemStack stack, @Nullable Level world, @Nonnull List list, TooltipFlag flag ) { - ItemStack result = new ItemStack( this ); - if( id >= 0 ) - { - result.getOrCreateTag() - .putInt( NBT_ID, id ); - } - if( label != null ) - { - result.setHoverName( new TextComponent( label ) ); - } - if( upgrade != null ) - { - result.getOrCreateTag() - .putString( NBT_UPGRADE, - upgrade.getUpgradeID() - .toString() ); - } - if( colour != -1 ) + if( flag.isAdvanced() || getLabel( stack ) == null ) { - result.getOrCreateTag() - .putInt( NBT_COLOUR, colour ); + int id = getComputerID( stack ); + if( id >= 0 ) + { + list.add( new TranslatableComponent( "gui.computercraft.tooltip.computer_id", id ) + .withStyle( ChatFormatting.GRAY ) ); + } } - return result; } public PocketServerComputer createServerComputer( final Level world, Container inventory, Entity entity, @Nonnull ItemStack stack ) { - if( world.isClientSide ) - { - return null; - } + if( world.isClientSide ) return null; PocketServerComputer computer; int instanceID = getInstanceID( stack ); int sessionID = getSessionID( stack ); int correctSessionID = ComputerCraft.serverComputerRegistry.getSessionID(); - if( instanceID >= 0 && sessionID == correctSessionID && ComputerCraft.serverComputerRegistry.contains( instanceID ) ) + if( instanceID >= 0 && sessionID == correctSessionID && + ComputerCraft.serverComputerRegistry.contains( instanceID ) ) { computer = (PocketServerComputer) ComputerCraft.serverComputerRegistry.get( instanceID ); } @@ -326,32 +219,56 @@ public PocketServerComputer createServerComputer( final Level world, Container i computerID = ComputerCraftAPI.createUniqueNumberedSaveDir( world, "computer" ); setComputerID( stack, computerID ); } - computer = new PocketServerComputer( world, computerID, getLabel( stack ), instanceID, getFamily() ); + computer = new PocketServerComputer( + world, + computerID, + getLabel( stack ), + instanceID, + getFamily() + ); computer.updateValues( entity, stack, getUpgrade( stack ) ); computer.addAPI( new PocketAPI( computer ) ); ComputerCraft.serverComputerRegistry.add( instanceID, computer ); - if( inventory != null ) - { - inventory.setChanged(); - } + if( inventory != null ) inventory.setChanged(); } - computer.setWorld( world ); + computer.setLevel( world ); return computer; } - public static IPocketUpgrade getUpgrade( @Nonnull ItemStack stack ) + public static ServerComputer getServerComputer( @Nonnull ItemStack stack ) { - CompoundTag compound = stack.getTag(); - return compound != null && compound.contains( NBT_UPGRADE ) ? PocketUpgrades.get( compound.getString( NBT_UPGRADE ) ) : null; + int session = getSessionID( stack ); + if( session != ComputerCraft.serverComputerRegistry.getSessionID() ) return null; + int instanceID = getInstanceID( stack ); + return instanceID >= 0 ? ComputerCraft.serverComputerRegistry.get( instanceID ) : null; } - // IMedia + public static ClientComputer createClientComputer( @Nonnull ItemStack stack ) + { + int instanceID = getInstanceID( stack ); + if( instanceID >= 0 ) + { + if( !ComputerCraft.clientComputerRegistry.contains( instanceID ) ) + { + ComputerCraft.clientComputerRegistry.add( instanceID, new ClientComputer( instanceID ) ); + } + return ComputerCraft.clientComputerRegistry.get( instanceID ); + } + return null; + } + + private static ClientComputer getClientComputer( @Nonnull ItemStack stack ) + { + int instanceID = getInstanceID( stack ); + return instanceID >= 0 ? ComputerCraft.clientComputerRegistry.get( instanceID ) : null; + } + + // IComputerItem implementation private static void setComputerID( @Nonnull ItemStack stack, int computerID ) { - stack.getOrCreateTag() - .putInt( NBT_ID, computerID ); + stack.getOrCreateTag().putInt( NBT_ID, computerID ); } @Override @@ -369,9 +286,14 @@ public ComputerFamily getFamily() @Override public ItemStack withFamily( @Nonnull ItemStack stack, @Nonnull ComputerFamily family ) { - return PocketComputerItemFactory.create( getComputerID( stack ), getLabel( stack ), getColour( stack ), family, getUpgrade( stack ) ); + return PocketComputerItemFactory.create( + getComputerID( stack ), getLabel( stack ), getColour( stack ), + family, getUpgrade( stack ) + ); } + // IMedia + @Override public boolean setLabel( @Nonnull ItemStack stack, String label ) { @@ -386,16 +308,13 @@ public boolean setLabel( @Nonnull ItemStack stack, String label ) return true; } - public static ClientComputer createClientComputer( @Nonnull ItemStack stack ) + @Override + public IMount createDataMount( @Nonnull ItemStack stack, @Nonnull Level world ) { - int instanceID = getInstanceID( stack ); - if( instanceID >= 0 ) + int id = getComputerID( stack ); + if( id >= 0 ) { - if( !ComputerCraft.clientComputerRegistry.contains( instanceID ) ) - { - ComputerCraft.clientComputerRegistry.add( instanceID, new ClientComputer( instanceID ) ); - } - return ComputerCraft.clientComputerRegistry.get( instanceID ); + return ComputerCraftAPI.createSaveDirMount( world, "computer/" + id, ComputerCraft.computerSpaceLimit ); } return null; } @@ -406,32 +325,67 @@ private static int getInstanceID( @Nonnull ItemStack stack ) return nbt != null && nbt.contains( NBT_INSTANCE ) ? nbt.getInt( NBT_INSTANCE ) : -1; } + private static void setInstanceID( @Nonnull ItemStack stack, int instanceID ) + { + stack.getOrCreateTag().putInt( NBT_INSTANCE, instanceID ); + } + private static int getSessionID( @Nonnull ItemStack stack ) { CompoundTag nbt = stack.getTag(); return nbt != null && nbt.contains( NBT_SESSION ) ? nbt.getInt( NBT_SESSION ) : -1; } - private static void setInstanceID( @Nonnull ItemStack stack, int instanceID ) + private static void setSessionID( @Nonnull ItemStack stack, int sessionID ) { - stack.getOrCreateTag() - .putInt( NBT_INSTANCE, instanceID ); + stack.getOrCreateTag().putInt( NBT_SESSION, sessionID ); } - private static void setSessionID( @Nonnull ItemStack stack, int sessionID ) + public static ComputerState getState( @Nonnull ItemStack stack ) { - stack.getOrCreateTag() - .putInt( NBT_SESSION, sessionID ); + ClientComputer computer = getClientComputer( stack ); + return computer == null ? ComputerState.OFF : computer.getState(); } - @Override - public IMount createDataMount( @Nonnull ItemStack stack, @Nonnull Level world ) + public static int getLightState( @Nonnull ItemStack stack ) { - int id = getComputerID( stack ); - if( id >= 0 ) + ClientComputer computer = getClientComputer( stack ); + if( computer != null && computer.isOn() ) { - return ComputerCraftAPI.createSaveDirMount( world, "computer/" + id, ComputerCraft.computerSpaceLimit ); + CompoundTag computerNBT = computer.getUserData(); + if( computerNBT != null && computerNBT.contains( NBT_LIGHT ) ) + { + return computerNBT.getInt( NBT_LIGHT ); + } } - return null; + return -1; + } + + public static IPocketUpgrade getUpgrade( @Nonnull ItemStack stack ) + { + CompoundTag compound = stack.getTag(); + return compound != null && compound.contains( NBT_UPGRADE ) + ? PocketUpgrades.get( compound.getString( NBT_UPGRADE ) ) : null; + } + + public static void setUpgrade( @Nonnull ItemStack stack, IPocketUpgrade upgrade ) + { + CompoundTag compound = stack.getOrCreateTag(); + + if( upgrade == null ) + { + compound.remove( NBT_UPGRADE ); + } + else + { + compound.putString( NBT_UPGRADE, upgrade.getUpgradeID().toString() ); + } + + compound.remove( NBT_UPGRADE_INFO ); + } + + public static CompoundTag getUpgradeInfo( @Nonnull ItemStack stack ) + { + return stack.getOrCreateTagElement( NBT_UPGRADE_INFO ); } } diff --git a/src/main/java/dan200/computercraft/shared/pocket/items/PocketComputerItemFactory.java b/src/main/java/dan200/computercraft/shared/pocket/items/PocketComputerItemFactory.java index dec881201..4c8a93c94 100644 --- a/src/main/java/dan200/computercraft/shared/pocket/items/PocketComputerItemFactory.java +++ b/src/main/java/dan200/computercraft/shared/pocket/items/PocketComputerItemFactory.java @@ -3,11 +3,10 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.pocket.items; import dan200.computercraft.api.pocket.IPocketUpgrade; -import dan200.computercraft.shared.ComputerCraftRegistry; +import dan200.computercraft.shared.Registry; import dan200.computercraft.shared.computer.core.ComputerFamily; import net.minecraft.world.item.ItemStack; @@ -23,9 +22,9 @@ public static ItemStack create( int id, String label, int colour, ComputerFamily switch( family ) { case NORMAL: - return ComputerCraftRegistry.ModItems.POCKET_COMPUTER_NORMAL.create( id, label, colour, upgrade ); + return Registry.ModItems.POCKET_COMPUTER_NORMAL.create( id, label, colour, upgrade ); case ADVANCED: - return ComputerCraftRegistry.ModItems.POCKET_COMPUTER_ADVANCED.create( id, label, colour, upgrade ); + return Registry.ModItems.POCKET_COMPUTER_ADVANCED.create( id, label, colour, upgrade ); default: return ItemStack.EMPTY; } diff --git a/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketModem.java b/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketModem.java index 2d069d423..b4da402af 100644 --- a/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketModem.java +++ b/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketModem.java @@ -3,16 +3,16 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.pocket.peripherals; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.pocket.AbstractPocketUpgrade; import dan200.computercraft.api.pocket.IPocketAccess; -import dan200.computercraft.shared.ComputerCraftRegistry; import dan200.computercraft.shared.peripheral.modem.ModemState; +import dan200.computercraft.shared.peripheral.modem.wireless.WirelessModemPeripheral; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.Entity; +import net.minecraft.world.item.ItemStack; import javax.annotation.Nonnull; import javax.annotation.Nullable; @@ -21,10 +21,9 @@ public class PocketModem extends AbstractPocketUpgrade { private final boolean advanced; - public PocketModem( boolean advanced ) + public PocketModem( ResourceLocation id, ItemStack stack, boolean advanced ) { - super( new ResourceLocation( "computercraft", advanced ? "wireless_modem_advanced" : "wireless_modem_normal" ), - advanced ? ComputerCraftRegistry.ModBlocks.WIRELESS_MODEM_ADVANCED : ComputerCraftRegistry.ModBlocks.WIRELESS_MODEM_NORMAL ); + super( id, advanced ? WirelessModemPeripheral.ADVANCED_ADJECTIVE : WirelessModemPeripheral.NORMAL_ADJECTIVE, stack ); this.advanced = advanced; } @@ -38,24 +37,13 @@ public IPeripheral createPeripheral( @Nonnull IPocketAccess access ) @Override public void update( @Nonnull IPocketAccess access, @Nullable IPeripheral peripheral ) { - if( !(peripheral instanceof PocketModemPeripheral) ) - { - return; - } + if( !(peripheral instanceof PocketModemPeripheral modem) ) return; Entity entity = access.getEntity(); - PocketModemPeripheral modem = (PocketModemPeripheral) peripheral; - - if( entity != null ) - { - modem.setLocation( entity.getCommandSenderWorld(), entity.getEyePosition( 1 ) ); - } + if( entity != null ) modem.setLocation( entity.getCommandSenderWorld(), entity.getEyePosition( 1 ) ); ModemState state = modem.getModemState(); - if( state.pollChanged() ) - { - access.setLight( state.isOpen() ? 0xBA0000 : -1 ); - } + if( state.pollChanged() ) access.setLight( state.isOpen() ? 0xBA0000 : -1 ); } } diff --git a/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketSpeaker.java b/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketSpeaker.java index 8e775eb9d..e6bc09b2b 100644 --- a/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketSpeaker.java +++ b/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketSpeaker.java @@ -3,24 +3,24 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.pocket.peripherals; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.pocket.AbstractPocketUpgrade; import dan200.computercraft.api.pocket.IPocketAccess; -import dan200.computercraft.shared.ComputerCraftRegistry; +import dan200.computercraft.shared.peripheral.speaker.UpgradeSpeakerPeripheral; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.Entity; +import net.minecraft.world.item.ItemStack; import javax.annotation.Nonnull; import javax.annotation.Nullable; public class PocketSpeaker extends AbstractPocketUpgrade { - public PocketSpeaker() + public PocketSpeaker( ResourceLocation id, ItemStack item ) { - super( new ResourceLocation( "computercraft", "speaker" ), ComputerCraftRegistry.ModBlocks.SPEAKER ); + super( id, UpgradeSpeakerPeripheral.ADJECTIVE, item ); } @Nullable @@ -33,12 +33,7 @@ public IPeripheral createPeripheral( @Nonnull IPocketAccess access ) @Override public void update( @Nonnull IPocketAccess access, @Nullable IPeripheral peripheral ) { - if( !(peripheral instanceof PocketSpeakerPeripheral) ) - { - return; - } - - PocketSpeakerPeripheral speaker = (PocketSpeakerPeripheral) peripheral; + if( !(peripheral instanceof PocketSpeakerPeripheral speaker) ) return; Entity entity = access.getEntity(); if( entity != 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 366d9ac58..1644873b7 100644 --- a/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketSpeakerPeripheral.java +++ b/src/main/java/dan200/computercraft/shared/pocket/peripherals/PocketSpeakerPeripheral.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.pocket.peripherals; import dan200.computercraft.api.peripheral.IPeripheral; @@ -23,7 +22,7 @@ void setLocation( Level world, Vec3 position ) } @Override - public Level getWorld() + public Level getLevel() { return world; } diff --git a/src/main/java/dan200/computercraft/shared/pocket/recipes/PocketComputerUpgradeRecipe.java b/src/main/java/dan200/computercraft/shared/pocket/recipes/PocketComputerUpgradeRecipe.java index a5c7e78a5..9660a4712 100644 --- a/src/main/java/dan200/computercraft/shared/pocket/recipes/PocketComputerUpgradeRecipe.java +++ b/src/main/java/dan200/computercraft/shared/pocket/recipes/PocketComputerUpgradeRecipe.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.pocket.recipes; import dan200.computercraft.api.pocket.IPocketUpgrade; @@ -23,13 +22,17 @@ public final class PocketComputerUpgradeRecipe extends CustomRecipe { - public static final RecipeSerializer SERIALIZER = new SimpleRecipeSerializer<>( PocketComputerUpgradeRecipe::new ); - private PocketComputerUpgradeRecipe( ResourceLocation identifier ) { super( identifier ); } + @Override + public boolean canCraftInDimensions( int x, int y ) + { + return x >= 2 && y >= 2; + } + @Nonnull @Override public ItemStack getResultItem() @@ -67,16 +70,10 @@ public ItemStack assemble( @Nonnull CraftingContainer inventory ) } } - if( computer.isEmpty() ) - { - return ItemStack.EMPTY; - } + if( computer.isEmpty() ) return ItemStack.EMPTY; ItemPocketComputer itemComputer = (ItemPocketComputer) computer.getItem(); - if( ItemPocketComputer.getUpgrade( computer ) != null ) - { - return ItemStack.EMPTY; - } + if( ItemPocketComputer.getUpgrade( computer ) != null ) return ItemStack.EMPTY; // Check for upgrades around the item IPocketUpgrade upgrade = null; @@ -85,18 +82,12 @@ public ItemStack assemble( @Nonnull CraftingContainer inventory ) for( int x = 0; x < inventory.getWidth(); x++ ) { ItemStack item = inventory.getItem( x + y * inventory.getWidth() ); - if( x == computerX && y == computerY ) - { - continue; - } + if( x == computerX && y == computerY ) continue; if( x == computerX && y == computerY - 1 ) { upgrade = PocketUpgrades.get( item ); - if( upgrade == null ) - { - return ItemStack.EMPTY; - } + if( upgrade == null ) return ItemStack.EMPTY; } else if( !item.isEmpty() ) { @@ -105,10 +96,7 @@ else if( !item.isEmpty() ) } } - if( upgrade == null ) - { - return ItemStack.EMPTY; - } + if( upgrade == null ) return ItemStack.EMPTY; // Construct the new stack ComputerFamily family = itemComputer.getFamily(); @@ -118,16 +106,12 @@ else if( !item.isEmpty() ) return PocketComputerItemFactory.create( computerID, label, colour, family, upgrade ); } - @Override - public boolean canCraftInDimensions( int x, int y ) - { - return x >= 2 && y >= 2; - } - @Nonnull @Override public RecipeSerializer getSerializer() { return SERIALIZER; } + + public static final SimpleRecipeSerializer SERIALIZER = new SimpleRecipeSerializer<>( PocketComputerUpgradeRecipe::new ); } diff --git a/src/main/java/dan200/computercraft/shared/turtle/FurnaceRefuelHandler.java b/src/main/java/dan200/computercraft/shared/turtle/FurnaceRefuelHandler.java index c8f3ad0a0..ce62fed45 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/FurnaceRefuelHandler.java +++ b/src/main/java/dan200/computercraft/shared/turtle/FurnaceRefuelHandler.java @@ -3,14 +3,12 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.turtle; import com.google.common.eventbus.Subscribe; import dan200.computercraft.api.turtle.ITurtleAccess; import dan200.computercraft.api.turtle.event.TurtleRefuelEvent; import dan200.computercraft.shared.util.InventoryUtil; -import dan200.computercraft.shared.util.ItemStorage; import dan200.computercraft.shared.util.WorldUtil; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; @@ -26,35 +24,24 @@ private FurnaceRefuelHandler() { } - @Subscribe - public static void onTurtleRefuel( TurtleRefuelEvent event ) - { - if( event.getHandler() == null && getFuelPerItem( event.getStack() ) > 0 ) - { - event.setHandler( INSTANCE ); - } - } - @Override public int refuel( @Nonnull ITurtleAccess turtle, @Nonnull ItemStack currentStack, int slot, int limit ) { - ItemStorage storage = ItemStorage.wrap( turtle.getInventory() ); - ItemStack stack = storage.take( slot, limit, ItemStack.EMPTY, false ); - int fuelToGive = getFuelPerItem( stack ) * stack.getCount(); + int fuelSpaceLeft = turtle.getFuelLimit() - turtle.getFuelLevel(); + int fuelPerItem = getFuelPerItem( turtle.getItemHandler().getStack( slot ) ); + int fuelItemLimit = (int) Math.ceil( fuelSpaceLeft / (double) fuelPerItem ); + if( limit > fuelItemLimit ) limit = fuelItemLimit; + ItemStack stack = turtle.getItemHandler().take( slot, limit, ItemStack.EMPTY, false ); + int fuelToGive = fuelPerItem * stack.getCount(); // Store the replacement item in the inventory - Item replacementStack = stack.getItem() - .getCraftingRemainingItem(); + Item replacementStack = stack.getItem().getCraftingRemainingItem(); if( replacementStack != null ) { - ItemStack remainder = InventoryUtil.storeItems( new ItemStack( replacementStack ), storage, turtle.getSelectedSlot() ); + ItemStack remainder = InventoryUtil.storeItems( new ItemStack( replacementStack ), turtle.getItemHandler(), turtle.getSelectedSlot() ); if( !remainder.isEmpty() ) { - WorldUtil.dropItemStack( remainder, - turtle.getLevel(), - turtle.getPosition(), - turtle.getDirection() - .getOpposite() ); + WorldUtil.dropItemStack( remainder, turtle.getLevel(), turtle.getPosition(), turtle.getDirection().getOpposite() ); } } @@ -63,8 +50,13 @@ public int refuel( @Nonnull ITurtleAccess turtle, @Nonnull ItemStack currentStac private static int getFuelPerItem( @Nonnull ItemStack stack ) { - int burnTime = FurnaceBlockEntity.getFuel() - .getOrDefault( stack.getItem(), 0 ); + int burnTime = FurnaceBlockEntity.getFuel().getOrDefault( stack.getItem(), 0 ); return (burnTime * 5) / 100; } + + @Subscribe + public static void onTurtleRefuel( TurtleRefuelEvent event ) + { + if( event.getHandler() == null && getFuelPerItem( event.getStack() ) > 0 ) event.setHandler( INSTANCE ); + } } diff --git a/src/main/java/dan200/computercraft/shared/turtle/apis/TurtleAPI.java b/src/main/java/dan200/computercraft/shared/turtle/apis/TurtleAPI.java index 3da20374e..653176016 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/apis/TurtleAPI.java +++ b/src/main/java/dan200/computercraft/shared/turtle/apis/TurtleAPI.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.turtle.apis; import dan200.computercraft.api.lua.*; @@ -12,7 +11,6 @@ import dan200.computercraft.api.turtle.TurtleCommandResult; import dan200.computercraft.api.turtle.TurtleSide; import dan200.computercraft.core.apis.IAPIEnvironment; -import dan200.computercraft.core.asm.TaskCallback; import dan200.computercraft.core.tracking.TrackingField; import dan200.computercraft.shared.peripheral.generic.data.ItemData; import dan200.computercraft.shared.peripheral.generic.methods.InventoryMethods; @@ -27,6 +25,7 @@ * The turtle API allows you to control your turtle. * * @cc.module turtle + * @cc.since 1.3 */ public class TurtleAPI implements ILuaAPI { @@ -45,6 +44,12 @@ public String[] getNames() return new String[] { "turtle" }; } + private MethodResult trackCommand( ITurtleCommand command ) + { + environment.addTrackingChange( TrackingField.TURTLE_OPS ); + return turtle.executeCommand( command ); + } + /** * Move the turtle forward one block. * @@ -58,12 +63,6 @@ public final MethodResult forward() return trackCommand( new TurtleMoveCommand( MoveDirection.FORWARD ) ); } - private MethodResult trackCommand( ITurtleCommand command ) - { - environment.addTrackingChange( TrackingField.TURTLE_OPS ); - return turtle.executeCommand( command ); - } - /** * Move the turtle backwards one block. * @@ -132,13 +131,15 @@ public final MethodResult turnRight() /** * Attempt to break the block in front of the turtle. * - * This requires a turtle tool capable of breaking the block. Diamond pickaxes (mining turtles) can break any vanilla block, but other tools (such as - * axes) are more limited. + * This requires a turtle tool capable of breaking the block. Diamond pickaxes + * (mining turtles) can break any vanilla block, but other tools (such as axes) + * are more limited. * * @param side The specific tool to use. Should be "left" or "right". * @return The turtle command result. * @cc.treturn boolean Whether a block was broken. * @cc.treturn string|nil The reason no block was broken. + * @cc.changed 1.6 Added optional side argument. */ @LuaFunction public final MethodResult dig( Optional side ) @@ -154,6 +155,7 @@ public final MethodResult dig( Optional side ) * @return The turtle command result. * @cc.treturn boolean Whether a block was broken. * @cc.treturn string|nil The reason no block was broken. + * @cc.changed 1.6 Added optional side argument. */ @LuaFunction public final MethodResult digUp( Optional side ) @@ -169,6 +171,7 @@ public final MethodResult digUp( Optional side ) * @return The turtle command result. * @cc.treturn boolean Whether a block was broken. * @cc.treturn string|nil The reason no block was broken. + * @cc.changed 1.6 Added optional side argument. */ @LuaFunction public final MethodResult digDown( Optional side ) @@ -189,6 +192,7 @@ public final MethodResult digDown( Optional side ) * @cc.tparam [opt] string text When placing a sign, set its contents to this text. * @cc.treturn boolean Whether the block could be placed. * @cc.treturn string|nil The reason the block was not placed. + * @cc.since 1.4 */ @LuaFunction public final MethodResult place( IArguments args ) @@ -204,6 +208,7 @@ public final MethodResult place( IArguments args ) * @cc.tparam [opt] string text When placing a sign, set its contents to this text. * @cc.treturn boolean Whether the block could be placed. * @cc.treturn string|nil The reason the block was not placed. + * @cc.since 1.4 * @see #place For more information about placing items. */ @LuaFunction @@ -220,6 +225,7 @@ public final MethodResult placeUp( IArguments args ) * @cc.tparam [opt] string text When placing a sign, set its contents to this text. * @cc.treturn boolean Whether the block could be placed. * @cc.treturn string|nil The reason the block was not placed. + * @cc.since 1.4 * @see #place For more information about placing items. */ @LuaFunction @@ -229,13 +235,15 @@ public final MethodResult placeDown( IArguments args ) } /** - * Drop the currently selected stack into the inventory in front of the turtle, or as an item into the world if there is no inventory. + * Drop the currently selected stack into the inventory in front of the turtle, or as an item into the world if + * there is no inventory. * * @param count The number of items to drop. If not given, the entire stack will be dropped. * @return The turtle command result. * @throws LuaException If dropping an invalid number of items. * @cc.treturn boolean Whether items were dropped. * @cc.treturn string|nil The reason the no items were dropped. + * @cc.since 1.31 * @see #select */ @LuaFunction @@ -244,24 +252,16 @@ public final MethodResult drop( Optional count ) throws LuaException return trackCommand( new TurtleDropCommand( InteractDirection.FORWARD, checkCount( count ) ) ); } - private static int checkCount( Optional countArg ) throws LuaException - { - int count = countArg.orElse( 64 ); - if( count < 0 || count > 64 ) - { - throw new LuaException( "Item count " + count + " out of range" ); - } - return count; - } - /** - * Drop the currently selected stack into the inventory above the turtle, or as an item into the world if there is no inventory. + * Drop the currently selected stack into the inventory above the turtle, or as an item into the world if there is + * no inventory. * * @param count The number of items to drop. If not given, the entire stack will be dropped. * @return The turtle command result. * @throws LuaException If dropping an invalid number of items. * @cc.treturn boolean Whether items were dropped. * @cc.treturn string|nil The reason the no items were dropped. + * @cc.since 1.4 * @see #select */ @LuaFunction @@ -271,13 +271,15 @@ public final MethodResult dropUp( Optional count ) throws LuaException } /** - * Drop the currently selected stack into the inventory in front of the turtle, or as an item into the world if there is no inventory. + * Drop the currently selected stack into the inventory in front of the turtle, or as an item into the world if + * there is no inventory. * * @param count The number of items to drop. If not given, the entire stack will be dropped. * @return The turtle command result. * @throws LuaException If dropping an invalid number of items. * @cc.treturn boolean Whether items were dropped. * @cc.treturn string|nil The reason the no items were dropped. + * @cc.since 1.4 * @see #select */ @LuaFunction @@ -308,15 +310,6 @@ public final MethodResult select( int slot ) throws LuaException } ); } - private static int checkSlot( int slot ) throws LuaException - { - if( slot < 1 || slot > 16 ) - { - throw new LuaException( "Slot number " + slot + " out of range" ); - } - return slot - 1; - } - /** * Get the number of items in the given slot. * @@ -328,14 +321,7 @@ private static int checkSlot( int slot ) throws LuaException public final int getItemCount( Optional slot ) throws LuaException { int actualSlot = checkSlot( slot ).orElse( turtle.getSelectedSlot() ); - return turtle.getInventory() - .getItem( actualSlot ) - .getCount(); - } - - private static Optional checkSlot( Optional slot ) throws LuaException - { - return slot.isPresent() ? Optional.of( checkSlot( slot.get() ) ) : Optional.empty(); + return turtle.getInventory().getItem( actualSlot ).getCount(); } /** @@ -351,13 +337,13 @@ private static Optional checkSlot( Optional slot ) throws LuaE public final int getItemSpace( Optional slot ) throws LuaException { int actualSlot = checkSlot( slot ).orElse( turtle.getSelectedSlot() ); - ItemStack stack = turtle.getInventory() - .getItem( actualSlot ); + ItemStack stack = turtle.getInventory().getItem( actualSlot ); return stack.isEmpty() ? 64 : Math.min( stack.getMaxStackSize(), 64 ) - stack.getCount(); } /** - * Check if there is a solid block in front of the turtle. In this case, solid refers to any non-air or liquid block. + * Check if there is a solid block in front of the turtle. In this case, solid refers to any non-air or liquid + * block. * * @return The turtle command result. * @cc.treturn boolean If there is a solid block in front. @@ -397,6 +383,7 @@ public final MethodResult detectDown() * * @return If the block and item are equal. * @cc.treturn boolean If the block and item are equal. + * @cc.since 1.31 */ @LuaFunction public final MethodResult compare() @@ -409,6 +396,7 @@ public final MethodResult compare() * * @return If the block and item are equal. * @cc.treturn boolean If the block and item are equal. + * @cc.since 1.31 */ @LuaFunction public final MethodResult compareUp() @@ -421,6 +409,7 @@ public final MethodResult compareUp() * * @return If the block and item are equal. * @cc.treturn boolean If the block and item are equal. + * @cc.since 1.31 */ @LuaFunction public final MethodResult compareDown() @@ -435,6 +424,8 @@ public final MethodResult compareDown() * @return The turtle command result. * @cc.treturn boolean Whether an entity was attacked. * @cc.treturn string|nil The reason nothing was attacked. + * @cc.since 1.4 + * @cc.changed 1.6 Added optional side argument. */ @LuaFunction public final MethodResult attack( Optional side ) @@ -449,6 +440,8 @@ public final MethodResult attack( Optional side ) * @return The turtle command result. * @cc.treturn boolean Whether an entity was attacked. * @cc.treturn string|nil The reason nothing was attacked. + * @cc.since 1.4 + * @cc.changed 1.6 Added optional side argument. */ @LuaFunction public final MethodResult attackUp( Optional side ) @@ -463,6 +456,8 @@ public final MethodResult attackUp( Optional side ) * @return The turtle command result. * @cc.treturn boolean Whether an entity was attacked. * @cc.treturn string|nil The reason nothing was attacked. + * @cc.since 1.4 + * @cc.changed 1.6 Added optional side argument. */ @LuaFunction public final MethodResult attackDown( Optional side ) @@ -480,6 +475,8 @@ public final MethodResult attackDown( Optional side ) * @throws LuaException If given an invalid number of items. * @cc.treturn boolean Whether items were picked up. * @cc.treturn string|nil The reason the no items were picked up. + * @cc.since 1.4 + * @cc.changed 1.6 Added an optional limit argument. */ @LuaFunction public final MethodResult suck( Optional count ) throws LuaException @@ -495,6 +492,8 @@ public final MethodResult suck( Optional count ) throws LuaException * @throws LuaException If given an invalid number of items. * @cc.treturn boolean Whether items were picked up. * @cc.treturn string|nil The reason the no items were picked up. + * @cc.since 1.4 + * @cc.changed 1.6 Added an optional limit argument. */ @LuaFunction public final MethodResult suckUp( Optional count ) throws LuaException @@ -510,6 +509,8 @@ public final MethodResult suckUp( Optional count ) throws LuaException * @throws LuaException If given an invalid number of items. * @cc.treturn boolean Whether items were picked up. * @cc.treturn string|nil The reason the no items were picked up. + * @cc.since 1.4 + * @cc.changed 1.6 Added an optional limit argument. */ @LuaFunction public final MethodResult suckDown( Optional count ) throws LuaException @@ -523,6 +524,7 @@ public final MethodResult suckDown( Optional count ) throws LuaExceptio * @return The fuel level, or "unlimited". * @cc.treturn [1] number The current amount of fuel a turtle this turtle has. * @cc.treturn [2] "unlimited" If turtles do not consume fuel when moving. + * @cc.since 1.4 * @see #getFuelLimit() * @see #refuel(Optional) */ @@ -535,8 +537,8 @@ public final Object getFuelLevel() /** * Refuel this turtle. * - * While most actions a turtle can perform (such as digging or placing blocks), moving consumes fuel from the - * turtle's internal buffer. If a turtle has no fuel, it will not move. + * While most actions a turtle can perform (such as digging or placing blocks) are free, moving consumes fuel from + * the turtle's internal buffer. If a turtle has no fuel, it will not move. * * {@link #refuel} refuels the turtle, consuming fuel items (such as coal or lava buckets) from the currently * selected slot and converting them into energy. This finishes once the turtle is fully refuelled or all items have @@ -565,6 +567,7 @@ public final Object getFuelLevel() * local is_fuel, reason = turtle.refuel(0) * if not is_fuel then printError(reason) end * } + * @cc.since 1.4 * @see #getFuelLevel() * @see #getFuelLimit() */ @@ -572,10 +575,7 @@ public final Object getFuelLevel() public final MethodResult refuel( Optional countA ) throws LuaException { int count = countA.orElse( Integer.MAX_VALUE ); - if( count < 0 ) - { - throw new LuaException( "Refuel count " + count + " out of range" ); - } + if( count < 0 ) throw new LuaException( "Refuel count " + count + " out of range" ); return trackCommand( new TurtleRefuelCommand( count ) ); } @@ -586,6 +586,7 @@ public final MethodResult refuel( Optional countA ) throws LuaException * @return If the items are the same. * @throws LuaException If the slot is out of range. * @cc.treturn boolean If the two items are equal. + * @cc.since 1.4 */ @LuaFunction public final MethodResult compareTo( int slot ) throws LuaException @@ -602,6 +603,7 @@ public final MethodResult compareTo( int slot ) throws LuaException * @throws LuaException If the slot is out of range. * @throws LuaException If the number of items is out of range. * @cc.treturn boolean If some items were successfully moved. + * @cc.since 1.45 */ @LuaFunction public final MethodResult transferTo( int slotArg, Optional countArg ) throws LuaException @@ -615,6 +617,7 @@ public final MethodResult transferTo( int slotArg, Optional countArg ) * Get the currently selected slot. * * @return The current slot. + * @cc.since 1.6 * @see #select */ @LuaFunction @@ -631,6 +634,7 @@ public final int getSelectedSlot() * @return The limit, or "unlimited". * @cc.treturn [1] number The maximum amount of fuel a turtle can hold. * @cc.treturn [2] "unlimited" If turtles do not consume fuel when moving. + * @cc.since 1.6 * @see #getFuelLevel() * @see #refuel(Optional) */ @@ -651,6 +655,7 @@ public final Object getFuelLimit() * @cc.treturn [1] true If the item was equipped. * @cc.treturn [2] false If we could not equip the item. * @cc.treturn [2] string The reason equipping this item failed. + * @cc.since 1.6 * @see #equipRight() */ @LuaFunction @@ -670,7 +675,8 @@ public final MethodResult equipLeft() * @cc.treturn [1] true If the item was equipped. * @cc.treturn [2] false If we could not equip the item. * @cc.treturn [2] string The reason equipping this item failed. - * @see #equipRight() + * @cc.since 1.6 + * @see #equipLeft() */ @LuaFunction public final MethodResult equipRight() @@ -684,10 +690,12 @@ public final MethodResult equipRight() * @return The turtle command result. * @cc.treturn boolean Whether there is a block in front of the turtle. * @cc.treturn table|string Information about the block in front, or a message explaining that there is no block. + * @cc.since 1.64 + * @cc.changed 1.76 Added block state to return value. * @cc.usage
{@code
      * local has_block, data = turtle.inspect()
      * if has_block then
-     *   print(textutils.serialize(data))
+     *   print(textutils.serialise(data))
      *   -- {
      *   --   name = "minecraft:oak_log",
      *   --   state = { axis = "x" },
@@ -709,6 +717,7 @@ public final MethodResult inspect()
      * @return The turtle command result.
      * @cc.treturn boolean Whether there is a block above the turtle.
      * @cc.treturn table|string Information about the above below, or a message explaining that there is no block.
+     * @cc.since 1.64
      */
     @LuaFunction
     public final MethodResult inspectUp()
@@ -722,6 +731,7 @@ public final MethodResult inspectUp()
      * @return The turtle command result.
      * @cc.treturn boolean Whether there is a block below the turtle.
      * @cc.treturn table|string Information about the block below, or a message explaining that there is no block.
+     * @cc.since 1.64
      */
     @LuaFunction
     public final MethodResult inspectDown()
@@ -734,38 +744,36 @@ public final MethodResult inspectDown()
      *
      * @param context  The Lua context
      * @param slot     The slot to get information about. Defaults to the {@link #select selected slot}.
-     * @param detailed Whether to include "detailed" information. When {@code true} the method will contain much more information about the item at the
-     *                 cost of taking longer to run.
+     * @param detailed Whether to include "detailed" information. When {@code true} the method will contain much
+     *                 more information about the item at the cost of taking longer to run.
      * @return The command result.
      * @throws LuaException If the slot is out of range.
      * @cc.treturn nil|table Information about the given slot, or {@code nil} if it is empty.
+     * @cc.since 1.64
      * @cc.usage Print the current slot, assuming it contains 13 dirt.
      *
      * 
{@code
-     *     print(textutils.serialize(turtle.getItemDetail()))
-     *     -- => {
-     *     --  name = "minecraft:dirt",
-     *     --  count = 13,
-     *     -- }
-     *     }
+ * print(textutils.serialise(turtle.getItemDetail())) + * -- => { + * -- name = "minecraft:dirt", + * -- count = 13, + * -- } + * }
* @see InventoryMethods#getItemDetail Describes the information returned by a detailed query. */ @LuaFunction public final MethodResult getItemDetail( ILuaContext context, Optional slot, Optional detailed ) throws LuaException { int actualSlot = checkSlot( slot ).orElse( turtle.getSelectedSlot() ); - return detailed.orElse( false ) ? TaskCallback.make( context, () -> getItemDetail( actualSlot, true ) ) : MethodResult.of( getItemDetail( actualSlot, - false ) ); + return detailed.orElse( false ) + ? context.executeMainThreadTask( () -> getItemDetail( actualSlot, true ) ) + : MethodResult.of( getItemDetail( actualSlot, false ) ); } private Object[] getItemDetail( int slot, boolean detailed ) { - ItemStack stack = turtle.getInventory() - .getItem( slot ); - if( stack.isEmpty() ) - { - return new Object[] { null }; - } + ItemStack stack = turtle.getInventory().getItem( slot ); + if( stack.isEmpty() ) return new Object[] { null }; Map table = detailed ? ItemData.fill( new HashMap<>(), stack ) @@ -773,4 +781,23 @@ private Object[] getItemDetail( int slot, boolean detailed ) return new Object[] { table }; } + + + private static int checkSlot( int slot ) throws LuaException + { + if( slot < 1 || slot > 16 ) throw new LuaException( "Slot number " + slot + " out of range" ); + return slot - 1; + } + + private static Optional checkSlot( Optional slot ) throws LuaException + { + return slot.isPresent() ? Optional.of( checkSlot( slot.get() ) ) : Optional.empty(); + } + + private static int checkCount( Optional countArg ) throws LuaException + { + int count = countArg.orElse( 64 ); + if( count < 0 || count > 64 ) throw new LuaException( "Item count " + count + " out of range" ); + return count; + } } 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 72b3f477b..90aaf4faa 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/blocks/BlockTurtle.java +++ b/src/main/java/dan200/computercraft/shared/turtle/blocks/BlockTurtle.java @@ -3,17 +3,16 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - 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; import dan200.computercraft.shared.turtle.core.TurtleBrain; import dan200.computercraft.shared.turtle.items.ITurtleItem; import dan200.computercraft.shared.turtle.items.TurtleItemFactory; +import dan200.computercraft.shared.util.WaterloggableHelpers; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.resources.ResourceLocation; @@ -24,6 +23,7 @@ import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.block.BaseEntityBlock; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.RenderShape; import net.minecraft.world.level.block.SimpleWaterloggedBlock; @@ -42,48 +42,43 @@ import javax.annotation.Nonnull; import javax.annotation.Nullable; +import java.util.function.Supplier; -import static dan200.computercraft.shared.util.WaterloggableHelpers.*; -import static net.minecraft.world.level.block.state.properties.BlockStateProperties.WATERLOGGED; +import static dan200.computercraft.shared.util.WaterloggableHelpers.WATERLOGGED; +import static dan200.computercraft.shared.util.WaterloggableHelpers.getFluidStateForPlacement; public class BlockTurtle extends BlockComputerBase implements SimpleWaterloggedBlock { public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING; - private static final VoxelShape DEFAULT_SHAPE = Shapes.box( 0.125, 0.125, 0.125, 0.875, 0.875, 0.875 ); + private static final VoxelShape DEFAULT_SHAPE = Shapes.box( + 0.125, 0.125, 0.125, + 0.875, 0.875, 0.875 + ); + + private final BlockEntityTicker clientTicker = ( level, pos, state, computer ) -> computer.clientTick(); - public BlockTurtle( Properties settings, ComputerFamily family, BlockEntityType type ) + public BlockTurtle( Properties settings, ComputerFamily family, Supplier> type ) { super( settings, family, type ); registerDefaultState( getStateDefinition().any() .setValue( FACING, Direction.NORTH ) - .setValue( WATERLOGGED, false ) ); - } - - @Nonnull - @Override - @Deprecated - public RenderShape getRenderShape( @Nonnull BlockState state ) - { - return RenderShape.ENTITYBLOCK_ANIMATED; + .setValue( WATERLOGGED, false ) + ); } - @Nonnull @Override - @Deprecated - public BlockState updateShape( @Nonnull BlockState state, @Nonnull Direction side, @Nonnull BlockState otherState, - @Nonnull LevelAccessor world, @Nonnull BlockPos pos, @Nonnull BlockPos otherPos ) + protected void createBlockStateDefinition( StateDefinition.Builder builder ) { - updateWaterloggedPostPlacement( state, world, pos ); - return state; + builder.add( FACING, WATERLOGGED ); } @Nonnull @Override @Deprecated - public FluidState getFluidState( @Nonnull BlockState state ) + public RenderShape getRenderShape( @Nonnull BlockState state ) { - return getWaterloggedFluidState( state ); + return RenderShape.ENTITYBLOCK_ANIMATED; } @Nonnull @@ -92,116 +87,97 @@ public FluidState getFluidState( @Nonnull BlockState state ) public VoxelShape getShape( @Nonnull BlockState state, BlockGetter world, @Nonnull BlockPos pos, @Nonnull CollisionContext context ) { BlockEntity tile = world.getBlockEntity( pos ); - Vec3 offset = tile instanceof TileTurtle ? ((TileTurtle) tile).getRenderOffset( 1.0f ) : Vec3.ZERO; + Vec3 offset = tile instanceof TileTurtle turtle ? turtle.getRenderOffset( 1.0f ) : Vec3.ZERO; return offset.equals( Vec3.ZERO ) ? DEFAULT_SHAPE : DEFAULT_SHAPE.move( offset.x, offset.y, offset.z ); } - @Override - public float getExplosionResistance() - { - // TODO Implement below functionality - return 2000; - } - @Nullable @Override public BlockState getStateForPlacement( BlockPlaceContext placement ) { - return defaultBlockState().setValue( FACING, placement.getHorizontalDirection() ) - .setValue( WATERLOGGED, getWaterloggedStateForPlacement( placement ) ); + return defaultBlockState() + .setValue( FACING, placement.getHorizontalDirection() ) + .setValue( WATERLOGGED, getFluidStateForPlacement( placement ) ); } + @Nonnull @Override - protected void createBlockStateDefinition( StateDefinition.Builder builder ) + @Deprecated + public FluidState getFluidState( @Nonnull BlockState state ) { - builder.add( FACING, WATERLOGGED ); + return WaterloggableHelpers.getFluidState( state ); } @Nonnull @Override - protected ItemStack getItem( TileComputerBase tile ) + @Deprecated + public BlockState updateShape( @Nonnull BlockState state, @Nonnull Direction side, @Nonnull BlockState otherState, @Nonnull LevelAccessor world, @Nonnull BlockPos pos, @Nonnull BlockPos otherPos ) { - return tile instanceof TileTurtle ? TurtleItemFactory.create( (TileTurtle) tile ) : ItemStack.EMPTY; + WaterloggableHelpers.updateShape( state, world, pos ); + return state; } - // @Override - // public float getBlastResistance( BlockState state, BlockView world, BlockPos pos, Explosion explosion ) - // { - // Entity exploder = explosion.getExploder(); - // if( getFamily() == ComputerFamily.ADVANCED || exploder instanceof LivingEntity || exploder instanceof ExplosiveProjectileEntity ) - // { - // return 2000; - // } - // - // return super.getExplosionResistance( state, world, pos, explosion ); - // } - @Override - public void setPlacedBy( @Nonnull Level world, @Nonnull BlockPos pos, @Nonnull BlockState state, @Nullable LivingEntity player, @Nonnull ItemStack stack ) + public void setPlacedBy( @Nonnull Level world, @Nonnull BlockPos pos, @Nonnull BlockState state, @Nullable LivingEntity entity, @Nonnull ItemStack stack ) { - super.setPlacedBy( world, pos, state, player, stack ); + super.setPlacedBy( world, pos, state, entity, stack ); BlockEntity tile = world.getBlockEntity( pos ); if( !world.isClientSide && tile instanceof TileTurtle turtle ) { + if( entity instanceof Player player ) turtle.setOwningPlayer( player.getGameProfile() ); - if( player instanceof Player ) - { - ((TileTurtle) tile).setOwningPlayer( ((Player) player).getGameProfile() ); - } - - if( stack.getItem() instanceof ITurtleItem ) + if( stack.getItem() instanceof ITurtleItem item ) { - ITurtleItem item = (ITurtleItem) stack.getItem(); - // Set Upgrades for( TurtleSide side : TurtleSide.values() ) { - turtle.getAccess() - .setUpgrade( side, item.getUpgrade( stack, side ) ); + turtle.getAccess().setUpgrade( side, item.getUpgrade( stack, side ) ); } - turtle.getAccess() - .setFuelLevel( item.getFuelLevel( stack ) ); + turtle.getAccess().setFuelLevel( item.getFuelLevel( stack ) ); // Set colour int colour = item.getColour( stack ); - if( colour != -1 ) - { - turtle.getAccess() - .setColour( colour ); - } + if( colour != -1 ) turtle.getAccess().setColour( colour ); // Set overlay ResourceLocation overlay = item.getOverlay( stack ); - if( overlay != null ) - { - ((TurtleBrain) turtle.getAccess()).setOverlay( overlay ); - } + if( overlay != null ) ((TurtleBrain) turtle.getAccess()).setOverlay( overlay ); } } } - public BlockEntityType getTypeByFamily( ComputerFamily family ) + @Override + public float getExplosionResistance() { - if( family == ComputerFamily.ADVANCED ) - { - return ComputerCraftRegistry.ModTiles.TURTLE_ADVANCED; - } - return ComputerCraftRegistry.ModTiles.TURTLE_NORMAL; + // TODO Implement below functionality + return 2000; } - @Nullable + // @Override + // public float getExplosionResistance( BlockState state, BlockGetter world, BlockPos pos, Explosion explosion ) + // { + // Entity exploder = explosion.getExploder(); + // if( getFamily() == ComputerFamily.ADVANCED || exploder instanceof LivingEntity || exploder instanceof AbstractHurtingProjectile) + // { + // return 2000; + // } + // + // return super.getExplosionResistance( state, world, pos, explosion ); + // } + + @Nonnull @Override - public BlockEntity newBlockEntity( BlockPos pos, BlockState state ) + protected ItemStack getItem( TileComputerBase tile ) { - return new TileTurtle( getTypeByFamily( getFamily() ), pos, state, getFamily() ); + return tile instanceof TileTurtle turtle ? TurtleItemFactory.create( turtle ) : ItemStack.EMPTY; } - @Nullable @Override - public BlockEntityTicker getTicker( Level world, BlockState state, BlockEntityType type ) + @Nullable + public BlockEntityTicker getTicker( @Nonnull Level level, @Nonnull BlockState state, @Nonnull BlockEntityType type ) { - return world.isClientSide ? BlockTurtle.createTickerHelper( type, getTypeByFamily( getFamily() ), ( world1, pos, state1, computer ) -> computer.clientTick() ) : super.getTicker( world, state, type ); + return level.isClientSide ? BaseEntityBlock.createTickerHelper( type, this.type.get(), clientTicker ) : super.getTicker( level, 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 d5826752b..0f66c5f3e 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/blocks/TileTurtle.java +++ b/src/main/java/dan200/computercraft/shared/turtle/blocks/TileTurtle.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.turtle.blocks; import com.mojang.authlib.GameProfile; @@ -27,6 +26,7 @@ import net.minecraft.core.NonNullList; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; +import net.minecraft.nbt.Tag; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; @@ -46,32 +46,51 @@ 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 NonNullList inventory = NonNullList - .withSize( INVENTORY_SIZE, ItemStack.EMPTY ); - private final NonNullList previousInventory = NonNullList - .withSize( INVENTORY_SIZE, ItemStack.EMPTY ); + + enum MoveState + { + NOT_MOVED, + IN_PROGRESS, + MOVED + } + + private final NonNullList inventory = NonNullList.withSize( INVENTORY_SIZE, ItemStack.EMPTY ); + private final NonNullList previousInventory = NonNullList.withSize( INVENTORY_SIZE, ItemStack.EMPTY ); private boolean inventoryChanged = false; private TurtleBrain brain = new TurtleBrain( this ); private MoveState moveState = MoveState.NOT_MOVED; public TileTurtle( BlockEntityType type, BlockPos pos, BlockState state, ComputerFamily family ) { - super( type, family, pos, state ); + super( type, pos, state, family ); + } + + private boolean hasMoved() + { + return moveState == MoveState.MOVED; } @Override - protected void unload() + protected ServerComputer createComputer( int instanceID, int id ) { - if( !hasMoved() ) - { - super.unload(); - } + ServerComputer computer = new ServerComputer( + getLevel(), id, label, instanceID, getFamily(), + ComputerCraft.turtleTermWidth, ComputerCraft.turtleTermHeight + ); + computer.setPosition( getBlockPos() ); + computer.addAPI( new TurtleAPI( computer.getAPIEnvironment(), getAccess() ) ); + brain.setupComputer( computer ); + return computer; + } + + public ComputerProxy createProxy() + { + return brain.getProxy(); } @Override @@ -106,102 +125,15 @@ public void destroy() } } - private boolean hasMoved() - { - return moveState == MoveState.MOVED; - } - @Override - public int getContainerSize() - { - return INVENTORY_SIZE; - } - - @Override - public boolean isEmpty() - { - for( ItemStack stack : inventory ) - { - if( !stack.isEmpty() ) - { - return false; - } - } - return true; - } - - @Nonnull - @Override - public ItemStack getItem( int slot ) - { - return slot >= 0 && slot < INVENTORY_SIZE ? inventory.get( slot ) - : ItemStack.EMPTY; - } - - @Nonnull - @Override - public ItemStack removeItem( int slot, int count ) - { - if( count == 0 ) - { - return ItemStack.EMPTY; - } - - ItemStack stack = getItem( slot ); - if( stack.isEmpty() ) - { - return ItemStack.EMPTY; - } - - if( stack.getCount() <= count ) - { - setItem( slot, ItemStack.EMPTY ); - return stack; - } - - ItemStack part = stack.split( count ); - onInventoryDefinitelyChanged(); - return part; - } - - @Nonnull - @Override - public ItemStack removeItemNoUpdate( int slot ) - { - ItemStack result = getItem( slot ); - setItem( slot, ItemStack.EMPTY ); - return result; - } - - @Override - public void setItem( int i, @Nonnull ItemStack stack ) + protected void unload() { - if( i >= 0 && i < INVENTORY_SIZE - && !InventoryUtil.areItemsEqual( stack, inventory.get( i ) ) ) + if( !hasMoved() ) { - inventory.set( i, stack ); - onInventoryDefinitelyChanged(); + super.unload(); } } - @Override - public boolean stillValid( @Nonnull Player player ) - { - return isUsable( player, false ); - } - - private void onInventoryDefinitelyChanged() - { - super.setChanged(); - inventoryChanged = true; - } - - @Override - protected boolean canNameWithTag( Player player ) - { - return true; - } - @Nonnull @Override public InteractionResult onActivate( Player player, InteractionHand hand, BlockHitResult hit ) @@ -210,12 +142,12 @@ public InteractionResult onActivate( Player player, InteractionHand hand, BlockH ItemStack currentItem = player.getItemInHand( hand ); if( !currentItem.isEmpty() ) { - if( currentItem.getItem() instanceof DyeItem ) + if( currentItem.getItem() instanceof DyeItem dyeItem ) { // Dye to change turtle colour if( !getLevel().isClientSide ) { - DyeColor dye = ((DyeItem) currentItem.getItem()).getDyeColor(); + DyeColor dye = dyeItem.getDyeColor(); if( brain.getDyeColour() != dye ) { brain.setDyeColour( dye ); @@ -227,8 +159,7 @@ public InteractionResult onActivate( Player player, InteractionHand hand, BlockH } return InteractionResult.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( !getLevel().isClientSide ) @@ -238,8 +169,7 @@ else if( currentItem.getItem() == Items.WATER_BUCKET brain.setColour( -1 ); if( !player.isCreative() ) { - player.setItemInHand( hand, - new ItemStack( Items.BUCKET ) ); + player.setItemInHand( hand, new ItemStack( Items.BUCKET ) ); player.getInventory().setChanged(); } } @@ -253,35 +183,26 @@ else if( currentItem.getItem() == Items.WATER_BUCKET } @Override - public void onNeighbourChange( @Nonnull BlockPos neighbour ) + protected boolean canNameWithTag( Player player ) { - if( moveState == MoveState.NOT_MOVED ) - { - super.onNeighbourChange( neighbour ); - } + return true; } @Override - public void onNeighbourTileEntityChange( @Nonnull BlockPos neighbour ) + protected double getInteractRange( Player player ) { - if( moveState == MoveState.NOT_MOVED ) - { - super.onNeighbourTileEntityChange( neighbour ); - } + return 12.0; } @Override - public void serverTick() + protected void serverTick() { super.serverTick(); brain.update(); if( inventoryChanged ) { ServerComputer computer = getServerComputer(); - if( computer != null ) - { - computer.queueEvent( "turtle_inventory" ); - } + if( computer != null ) computer.queueEvent( "turtle_inventory" ); inventoryChanged = false; for( int n = 0; n < getContainerSize(); n++ ) @@ -298,32 +219,31 @@ protected void clientTick() @Override protected void updateBlockState( ComputerState newState ) - {} + { + } @Override - public void saveAdditional( @Nonnull CompoundTag nbt ) + public void onNeighbourChange( @Nonnull BlockPos neighbour ) { - // Write inventory - ListTag nbttaglist = new ListTag(); - for( int i = 0; i < INVENTORY_SIZE; i++ ) - { - if( !inventory.get( i ).isEmpty() ) - { - CompoundTag tag = new CompoundTag(); - tag.putByte( "Slot", (byte) i ); - inventory.get( i ).save( tag ); - nbttaglist.add( tag ); - } - } - nbt.put( "Items", nbttaglist ); + if( moveState == MoveState.NOT_MOVED ) super.onNeighbourChange( neighbour ); + } - // Write brain - nbt = brain.writeToNBT( nbt ); + @Override + public void onNeighbourTileEntityChange( @Nonnull BlockPos neighbour ) + { + if( moveState == MoveState.NOT_MOVED ) super.onNeighbourTileEntityChange( neighbour ); + } - super.saveAdditional( nbt ); + public void notifyMoveStart() + { + if( moveState == MoveState.NOT_MOVED ) moveState = MoveState.IN_PROGRESS; } - // IDirectionalTile + public void notifyMoveEnd() + { + // MoveState.MOVED is final + if( moveState == MoveState.IN_PROGRESS ) moveState = MoveState.NOT_MOVED; + } @Override public void load( @Nonnull CompoundTag nbt ) @@ -331,7 +251,7 @@ public void load( @Nonnull CompoundTag nbt ) super.load( nbt ); // Read inventory - ListTag nbttaglist = nbt.getList( "Items", NBTUtil.TAG_COMPOUND ); + ListTag nbttaglist = nbt.getList( "Items", Tag.TAG_COMPOUND ); inventory.clear(); previousInventory.clear(); for( int i = 0; i < nbttaglist.size(); i++ ) @@ -349,13 +269,36 @@ public void load( @Nonnull CompoundTag nbt ) brain.readFromNBT( nbt ); } + @Override + public void saveAdditional( @Nonnull CompoundTag nbt ) + { + // Write inventory + ListTag nbttaglist = new ListTag(); + for( int i = 0; i < INVENTORY_SIZE; i++ ) + { + if( !inventory.get( i ).isEmpty() ) + { + CompoundTag tag = new CompoundTag(); + tag.putByte( "Slot", (byte) i ); + inventory.get( i ).save( tag ); + nbttaglist.add( tag ); + } + } + nbt.put( "Items", nbttaglist ); + + // Write brain + nbt = brain.writeToNBT( nbt ); + + super.saveAdditional( nbt ); + } + @Override protected boolean isPeripheralBlockedOnSide( ComputerSide localSide ) { return hasPeripheralUpgradeOnSide( localSide ); } - // ITurtleTile + // IDirectionalTile @Override public Direction getDirection() @@ -363,147 +306,147 @@ public Direction getDirection() return getBlockState().getValue( BlockTurtle.FACING ); } - @Override - protected ServerComputer createComputer( int instanceID, int id ) + public void setDirection( Direction dir ) { - ServerComputer computer = new ServerComputer( getLevel(), id, label, instanceID, - getFamily(), ComputerCraft.turtleTermWidth, - ComputerCraft.turtleTermHeight ); - computer.setPosition( getBlockPos() ); - computer.addAPI( new TurtleAPI( computer.getAPIEnvironment(), getAccess() ) ); - brain.setupComputer( computer ); - return computer; + if( dir.getAxis() == Direction.Axis.Y ) dir = Direction.NORTH; + level.setBlockAndUpdate( worldPosition, getBlockState().setValue( BlockTurtle.FACING, dir ) ); + + updateOutput(); + updateInputsImmediately(); + + onTileEntityChange(); } + // ITurtleTile + @Override - protected void writeDescription( @Nonnull CompoundTag nbt ) + public ITurtleUpgrade getUpgrade( TurtleSide side ) { - super.writeDescription( nbt ); - brain.writeDescription( nbt ); + return brain.getUpgrade( side ); } @Override - protected void readDescription( @Nonnull CompoundTag nbt ) + public int getColour() { - super.readDescription( nbt ); - brain.readDescription( nbt ); + return brain.getColour(); } @Override - public ComputerProxy createProxy() + public ResourceLocation getOverlay() { - return brain.getProxy(); + return brain.getOverlay(); } - public void setDirection( Direction dir ) + @Override + public ITurtleAccess getAccess() { - if( dir.getAxis() == Direction.Axis.Y ) - { - dir = Direction.NORTH; - } - level.setBlockAndUpdate( worldPosition, getBlockState().setValue( BlockTurtle.FACING, dir ) ); - updateOutput(); - updateInput(); - onTileEntityChange(); + return brain; } - public void onTileEntityChange() + @Override + public Vec3 getRenderOffset( float f ) { - super.setChanged(); + return brain.getRenderOffset( f ); } - private boolean hasPeripheralUpgradeOnSide( ComputerSide side ) + @Override + public float getRenderYaw( float f ) { - ITurtleUpgrade upgrade; - switch( side ) - { - case RIGHT: - upgrade = getUpgrade( TurtleSide.RIGHT ); - break; - case LEFT: - upgrade = getUpgrade( TurtleSide.LEFT ); - break; - default: - return false; - } - return upgrade != null && upgrade.getType().isPeripheral(); + return brain.getVisualYaw( f ); } - // IInventory - @Override - protected double getInteractRange( Player player ) + public float getToolRenderAngle( TurtleSide side, float f ) { - return 12.0; + return brain.getToolRenderAngle( side, f ); } - public void notifyMoveStart() + void setOwningPlayer( GameProfile player ) { - if( moveState == MoveState.NOT_MOVED ) - { - moveState = MoveState.IN_PROGRESS; - } + brain.setOwningPlayer( player ); + setChanged(); } - public void notifyMoveEnd() - { - // MoveState.MOVED is final - if( moveState == MoveState.IN_PROGRESS ) - { - moveState = MoveState.NOT_MOVED; - } - } + // IInventory @Override - public int getColour() + public int getContainerSize() { - return brain.getColour(); + return INVENTORY_SIZE; } @Override - public ResourceLocation getOverlay() + public boolean isEmpty() { - return brain.getOverlay(); + for( ItemStack stack : inventory ) + { + if( !stack.isEmpty() ) return false; + } + return true; } + @Nonnull @Override - public ITurtleUpgrade getUpgrade( TurtleSide side ) + public ItemStack getItem( int slot ) { - return brain.getUpgrade( side ); + return slot >= 0 && slot < INVENTORY_SIZE ? inventory.get( slot ) : ItemStack.EMPTY; } + @Nonnull @Override - public ITurtleAccess getAccess() + public ItemStack removeItemNoUpdate( int slot ) { - return brain; + ItemStack result = getItem( slot ); + setItem( slot, ItemStack.EMPTY ); + return result; } + @Nonnull @Override - public Vec3 getRenderOffset( float f ) + public ItemStack removeItem( int slot, int count ) { - return brain.getRenderOffset( f ); + if( count == 0 ) return ItemStack.EMPTY; + + ItemStack stack = getItem( slot ); + if( stack.isEmpty() ) return ItemStack.EMPTY; + + if( stack.getCount() <= count ) + { + setItem( slot, ItemStack.EMPTY ); + return stack; + } + + ItemStack part = stack.split( count ); + onInventoryDefinitelyChanged(); + return part; } @Override - public float getRenderYaw( float f ) + public void setItem( int i, @Nonnull ItemStack stack ) { - return brain.getVisualYaw( f ); + if( i >= 0 && i < INVENTORY_SIZE && !InventoryUtil.areItemsEqual( stack, inventory.get( i ) ) ) + { + inventory.set( i, stack ); + onInventoryDefinitelyChanged(); + } } @Override - public float getToolRenderAngle( TurtleSide side, float f ) + public void clearContent() { - return brain.getToolRenderAngle( side, f ); - } + boolean changed = false; + for( int i = 0; i < INVENTORY_SIZE; i++ ) + { + if( !inventory.get( i ).isEmpty() ) + { + inventory.set( i, ItemStack.EMPTY ); + changed = true; + } + } - void setOwningPlayer( GameProfile player ) - { - brain.setOwningPlayer( player ); - setChanged(); + if( changed ) onInventoryDefinitelyChanged(); } - // Networking stuff - @Override public void setChanged() { @@ -522,26 +465,59 @@ public void setChanged() } @Override - public void clearContent() + public boolean stillValid( @Nonnull Player player ) { - boolean changed = false; - for( int i = 0; i < INVENTORY_SIZE; i++ ) - { - if( !inventory.get( i ).isEmpty() ) - { - inventory.set( i, ItemStack.EMPTY ); - changed = true; - } - } + return isUsable( player, false ); + } - if( changed ) - { - onInventoryDefinitelyChanged(); - } + private void onInventoryDefinitelyChanged() + { + super.setChanged(); + inventoryChanged = true; + } + + public void onTileEntityChange() + { + super.setChanged(); + } + + // Networking stuff + + @Nonnull + @Override + public CompoundTag getUpdateTag() + { + CompoundTag nbt = super.getUpdateTag(); + brain.writeDescription( nbt ); + return nbt; } + // @Override + // public void handleUpdateTag( @Nonnull CompoundTag nbt ) + // { + // super.handleUpdateTag( nbt ); + // brain.readDescription( nbt ); + // } + // Privates + private boolean hasPeripheralUpgradeOnSide( ComputerSide side ) + { + ITurtleUpgrade upgrade; + switch( side ) + { + case RIGHT: + upgrade = getUpgrade( TurtleSide.RIGHT ); + break; + case LEFT: + upgrade = getUpgrade( TurtleSide.LEFT ); + break; + default: + return false; + } + return upgrade != null && upgrade.getType().isPeripheral(); + } + public void transferStateFrom( TileTurtle copy ) { super.transferStateFrom( copy ); @@ -557,14 +533,8 @@ public void transferStateFrom( TileTurtle copy ) @Nullable @Override - public AbstractContainerMenu createMenu( int id, @Nonnull Inventory inventory, - @Nonnull Player player ) + public AbstractContainerMenu createMenu( int id, @Nonnull Inventory inventory, @Nonnull Player player ) { return new ContainerTurtle( id, inventory, brain ); } - - enum MoveState - { - NOT_MOVED, IN_PROGRESS, MOVED - } } diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/InteractDirection.java b/src/main/java/dan200/computercraft/shared/turtle/core/InteractDirection.java index 02f489575..6ae52d2cd 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/InteractDirection.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/InteractDirection.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.turtle.core; import dan200.computercraft.api.turtle.ITurtleAccess; @@ -11,7 +10,9 @@ public enum InteractDirection { - FORWARD, UP, DOWN; + FORWARD, + UP, + DOWN; public Direction toWorldDir( ITurtleAccess turtle ) { diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/MoveDirection.java b/src/main/java/dan200/computercraft/shared/turtle/core/MoveDirection.java index b4bc5872b..b86edc54c 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/MoveDirection.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/MoveDirection.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.turtle.core; import dan200.computercraft.api.turtle.ITurtleAccess; @@ -11,7 +10,10 @@ public enum MoveDirection { - FORWARD, BACK, UP, DOWN; + FORWARD, + BACK, + UP, + DOWN; public Direction toWorldDir( ITurtleAccess turtle ) { @@ -21,8 +23,7 @@ public Direction toWorldDir( ITurtleAccess turtle ) default: return turtle.getDirection(); case BACK: - return turtle.getDirection() - .getOpposite(); + return turtle.getDirection().getOpposite(); case UP: return Direction.UP; case DOWN: diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurnDirection.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurnDirection.java index becb48380..dafd1dd93 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurnDirection.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurnDirection.java @@ -3,10 +3,10 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.turtle.core; public enum TurnDirection { - LEFT, RIGHT, + LEFT, + RIGHT, } 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 57f7c76e5..c8ddce4e0 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleBrain.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.turtle.core; import com.google.common.base.Objects; @@ -19,11 +18,15 @@ import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.ServerComputer; import dan200.computercraft.shared.turtle.blocks.TileTurtle; -import dan200.computercraft.shared.util.*; +import dan200.computercraft.shared.util.Colour; +import dan200.computercraft.shared.util.Holiday; +import dan200.computercraft.shared.util.HolidayUtil; +import dan200.computercraft.shared.util.InventoryDelegate; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.nbt.CompoundTag; +import net.minecraft.nbt.Tag; import net.minecraft.resources.ResourceLocation; import net.minecraft.tags.FluidTags; import net.minecraft.world.Container; @@ -59,45 +62,49 @@ public class TurtleBrain implements ITurtleAccess private static final String NBT_SLOT = "Slot"; private static final int ANIM_DURATION = 8; - private final Queue commandQueue = new ArrayDeque<>(); - private final Map upgrades = new EnumMap<>( TurtleSide.class ); - private final Map peripherals = new EnumMap<>( TurtleSide.class ); - private final Map upgradeNBTData = new EnumMap<>( TurtleSide.class ); - TurtlePlayer cachedPlayer; + private TileTurtle owner; - private final Container inventory = (InventoryDelegate) () -> owner; private ComputerProxy proxy; private GameProfile owningPlayer; + + private final Container inventory = (InventoryDelegate) () -> owner; + + private final Queue commandQueue = new ArrayDeque<>(); private int commandsIssued = 0; + + private final Map upgrades = new EnumMap<>( TurtleSide.class ); + private final Map peripherals = new EnumMap<>( TurtleSide.class ); + private final Map upgradeNBTData = new EnumMap<>( TurtleSide.class ); + private int selectedSlot = 0; private int fuelLevel = 0; private int colourHex = -1; private ResourceLocation overlay = null; + private TurtleAnimation animation = TurtleAnimation.NONE; private int animationProgress = 0; private int lastAnimationProgress = 0; + TurtlePlayer cachedPlayer; + public TurtleBrain( TileTurtle turtle ) { owner = turtle; } - public TileTurtle getOwner() + public void setOwner( TileTurtle owner ) { - return owner; + this.owner = owner; } - public void setOwner( TileTurtle owner ) + public TileTurtle getOwner() { - this.owner = owner; + return owner; } public ComputerProxy getProxy() { - if( proxy == null ) - { - proxy = new ComputerProxy( () -> owner ); - } + if( proxy == null ) proxy = new ComputerProxy( () -> owner ); return proxy; } @@ -111,80 +118,154 @@ public void setupComputer( ServerComputer computer ) updatePeripherals( computer ); } - private void updatePeripherals( ServerComputer serverComputer ) + public void update() { - if( serverComputer == null ) + Level world = getLevel(); + if( !world.isClientSide ) { - return; + // Advance movement + updateCommands(); + + // The block may have been broken while the command was executing (for instance, if a block explodes + // when being mined). If so, abort. + if( owner.isRemoved() ) return; } - // Update peripherals - for( TurtleSide side : TurtleSide.values() ) + // Advance animation + updateAnimation(); + + // Advance upgrades + if( !upgrades.isEmpty() ) { - ITurtleUpgrade upgrade = getUpgrade( side ); - IPeripheral peripheral = null; - if( upgrade != null && upgrade.getType() - .isPeripheral() ) + for( Map.Entry entry : upgrades.entrySet() ) { - peripheral = upgrade.createPeripheral( this, side ); + entry.getValue().update( this, entry.getKey() ); } + } + } - IPeripheral existing = peripherals.get( side ); - if( existing == peripheral || (existing != null && peripheral != null && existing.equals( peripheral )) ) - { - // If the peripheral is the same, just use that. - peripheral = existing; - } - else - { - // Otherwise update our map - peripherals.put( side, peripheral ); - } + /** + * Read common data for saving and client synchronisation. + * + * @param nbt The tag to read from + */ + private void readCommon( CompoundTag nbt ) + { + // Read fields + colourHex = nbt.contains( NBT_COLOUR ) ? nbt.getInt( NBT_COLOUR ) : -1; + fuelLevel = nbt.contains( NBT_FUEL ) ? nbt.getInt( NBT_FUEL ) : 0; + overlay = nbt.contains( NBT_OVERLAY ) ? new ResourceLocation( nbt.getString( NBT_OVERLAY ) ) : null; - // Always update the computer: it may not be the same computer as before! - serverComputer.setPeripheral( toDirection( side ), peripheral ); + // Read upgrades + setUpgradeDirect( TurtleSide.LEFT, nbt.contains( NBT_LEFT_UPGRADE ) ? TurtleUpgrades.get( nbt.getString( NBT_LEFT_UPGRADE ) ) : null ); + setUpgradeDirect( TurtleSide.RIGHT, nbt.contains( NBT_RIGHT_UPGRADE ) ? TurtleUpgrades.get( nbt.getString( NBT_RIGHT_UPGRADE ) ) : null ); + + // NBT + upgradeNBTData.clear(); + if( nbt.contains( NBT_LEFT_UPGRADE_DATA ) ) + { + upgradeNBTData.put( TurtleSide.LEFT, nbt.getCompound( NBT_LEFT_UPGRADE_DATA ).copy() ); + } + if( nbt.contains( NBT_RIGHT_UPGRADE_DATA ) ) + { + upgradeNBTData.put( TurtleSide.RIGHT, nbt.getCompound( NBT_RIGHT_UPGRADE_DATA ).copy() ); } } - private static ComputerSide toDirection( TurtleSide side ) + private void writeCommon( CompoundTag nbt ) { - switch( side ) + nbt.putInt( NBT_FUEL, fuelLevel ); + if( colourHex != -1 ) nbt.putInt( NBT_COLOUR, colourHex ); + if( overlay != null ) nbt.putString( NBT_OVERLAY, overlay.toString() ); + + // Write upgrades + String leftUpgradeId = getUpgradeId( getUpgrade( TurtleSide.LEFT ) ); + if( leftUpgradeId != null ) nbt.putString( NBT_LEFT_UPGRADE, leftUpgradeId ); + String rightUpgradeId = getUpgradeId( getUpgrade( TurtleSide.RIGHT ) ); + if( rightUpgradeId != null ) nbt.putString( NBT_RIGHT_UPGRADE, rightUpgradeId ); + + // Write upgrade NBT + if( upgradeNBTData.containsKey( TurtleSide.LEFT ) ) { - case LEFT: - return ComputerSide.LEFT; - case RIGHT: - default: - return ComputerSide.RIGHT; + nbt.put( NBT_LEFT_UPGRADE_DATA, getUpgradeNBTData( TurtleSide.LEFT ).copy() ); + } + if( upgradeNBTData.containsKey( TurtleSide.RIGHT ) ) + { + nbt.put( NBT_RIGHT_UPGRADE_DATA, getUpgradeNBTData( TurtleSide.RIGHT ).copy() ); } } - public void update() + public void readFromNBT( CompoundTag nbt ) { - Level world = getLevel(); - if( !world.isClientSide ) + readCommon( nbt ); + + // Read state + selectedSlot = nbt.getInt( NBT_SLOT ); + + // Read owner + if( nbt.contains( "Owner", Tag.TAG_COMPOUND ) ) { - // Advance movement - updateCommands(); + CompoundTag owner = nbt.getCompound( "Owner" ); + owningPlayer = new GameProfile( + new UUID( owner.getLong( "UpperId" ), owner.getLong( "LowerId" ) ), + owner.getString( "Name" ) + ); + } + else + { + owningPlayer = null; + } + } - // The block may have been broken while the command was executing (for instance, if a block explodes - // when being mined). If so, abort. - if( owner.isRemoved() ) return; + public CompoundTag writeToNBT( CompoundTag nbt ) + { + writeCommon( nbt ); + + // Write state + nbt.putInt( NBT_SLOT, selectedSlot ); + + // Write owner + if( owningPlayer != null ) + { + CompoundTag owner = new CompoundTag(); + nbt.put( "Owner", owner ); + + owner.putLong( "UpperId", owningPlayer.getId().getMostSignificantBits() ); + owner.putLong( "LowerId", owningPlayer.getId().getLeastSignificantBits() ); + owner.putString( "Name", owningPlayer.getName() ); } - // Advance animation - updateAnimation(); + return nbt; + } - // Advance upgrades - if( !upgrades.isEmpty() ) + private static String getUpgradeId( ITurtleUpgrade upgrade ) + { + return upgrade != null ? upgrade.getUpgradeID().toString() : null; + } + + public void readDescription( CompoundTag nbt ) + { + readCommon( nbt ); + + // Animation + TurtleAnimation anim = TurtleAnimation.values()[nbt.getInt( "Animation" )]; + if( anim != animation && + anim != TurtleAnimation.WAIT && + anim != TurtleAnimation.SHORT_WAIT && + anim != TurtleAnimation.NONE ) { - for( Map.Entry entry : upgrades.entrySet() ) - { - entry.getValue() - .update( this, entry.getKey() ); - } + animation = anim; + animationProgress = 0; + lastAnimationProgress = 0; } } + public void writeDescription( CompoundTag nbt ) + { + writeCommon( nbt ); + nbt.putInt( "Animation", animation.ordinal() ); + } + @Nonnull @Override public Level getLevel() @@ -220,20 +301,12 @@ public boolean teleportTo( @Nonnull Level world, @Nonnull BlockPos pos ) } // Ensure the chunk is loaded - if( !world.hasChunkAt( pos ) ) - { - return false; - } + if( !world.isLoaded( pos ) ) return false; // Ensure we're inside the world border - if( !world.getWorldBorder() - .isWithinBounds( pos ) ) - { - return false; - } + if( !world.getWorldBorder().isWithinBounds( pos ) ) return false; - FluidState existingFluid = world.getBlockState( pos ) - .getFluidState(); + FluidState existingFluid = world.getBlockState( pos ).getFluidState(); BlockState newState = oldBlock // We only mark this as waterlogged when travelling into a source block. This prevents us from spreading // fluid by creating a new source when moving into a block, causing the next block to be almost full and @@ -247,8 +320,7 @@ public boolean teleportTo( @Nonnull Level world, @Nonnull BlockPos pos ) // Create a new turtle if( world.setBlock( pos, newState, 0 ) ) { - Block block = world.getBlockState( pos ) - .getBlock(); + Block block = world.getBlockState( pos ).getBlock(); if( block == oldBlock.getBlock() ) { BlockEntity newTile = world.getBlockEntity( pos ); @@ -257,18 +329,17 @@ public boolean teleportTo( @Nonnull Level world, @Nonnull BlockPos pos ) // Copy the old turtle state into the new turtle newTurtle.setLevel( world ); newTurtle.transferStateFrom( oldOwner ); - newTurtle.createServerComputer() - .setWorld( world ); - newTurtle.createServerComputer() - .setPosition( pos ); + + ServerComputer computer = newTurtle.createServerComputer(); + computer.setLevel( world ); + computer.setPosition( pos ); // Remove the old turtle oldWorld.removeBlock( oldPos, false ); // Make sure everybody knows about it - newTurtle.updateBlock(); - newTurtle.updateInput(); newTurtle.updateOutput(); + newTurtle.updateInputsImmediately(); return true; } } @@ -292,7 +363,11 @@ public Vec3 getVisualPosition( float f ) { Vec3 offset = getRenderOffset( f ); BlockPos pos = owner.getBlockPos(); - return new Vec3( pos.getX() + 0.5 + offset.x, pos.getY() + 0.5 + offset.y, pos.getZ() + 0.5 + offset.z ); + return new Vec3( + pos.getX() + 0.5 + offset.x, + pos.getY() + 0.5 + offset.y, + pos.getZ() + 0.5 + offset.z + ); } @Override @@ -302,19 +377,23 @@ public float getVisualYaw( float f ) switch( animation ) { case TURN_LEFT: + { yaw += 90.0f * (1.0f - getAnimationFraction( f )); if( yaw >= 360.0f ) { yaw -= 360.0f; } break; + } case TURN_RIGHT: + { yaw += -90.0f * (1.0f - getAnimationFraction( f )); if( yaw < 0.0f ) { yaw += 360.0f; } break; + } } return yaw; } @@ -341,10 +420,7 @@ public int getSelectedSlot() @Override public void setSelectedSlot( int slot ) { - if( getLevel().isClientSide ) - { - throw new UnsupportedOperationException( "Cannot set the slot on the client" ); - } + if( getLevel().isClientSide ) throw new UnsupportedOperationException( "Cannot set the slot on the client" ); if( slot >= 0 && slot < owner.getContainerSize() ) { @@ -353,35 +429,11 @@ public void setSelectedSlot( int slot ) } } + @Nonnull @Override - public int getColour() - { - return colourHex; - } - - @Override - public void setColour( int colour ) - { - if( colour >= 0 && colour <= 0xFFFFFF ) - { - if( colourHex != colour ) - { - colourHex = colour; - owner.updateBlock(); - } - } - else if( colourHex != -1 ) - { - colourHex = -1; - owner.updateBlock(); - } - } - - @Nullable - @Override - public GameProfile getOwningPlayer() + public Container getInventory() { - return owningPlayer; + return inventory; } @Override @@ -419,15 +471,9 @@ public int getFuelLimit() @Override public boolean consumeFuel( int fuel ) { - if( getLevel().isClientSide ) - { - throw new UnsupportedOperationException( "Cannot consume fuel on the client" ); - } + if( getLevel().isClientSide ) throw new UnsupportedOperationException( "Cannot consume fuel on the client" ); - if( !isFuelNeeded() ) - { - return true; - } + if( !isFuelNeeded() ) return true; int consumption = Math.max( fuel, 0 ); if( getFuelLevel() >= consumption ) @@ -441,10 +487,7 @@ public boolean consumeFuel( int fuel ) @Override public void addFuel( int fuel ) { - if( getLevel().isClientSide ) - { - throw new UnsupportedOperationException( "Cannot add fuel on the client" ); - } + if( getLevel().isClientSide ) throw new UnsupportedOperationException( "Cannot add fuel on the client" ); int addition = Math.max( fuel, 0 ); setFuelLevel( getFuelLevel() + addition ); @@ -454,30 +497,18 @@ public void addFuel( int fuel ) @Override public MethodResult executeCommand( @Nonnull ITurtleCommand command ) { - if( getLevel().isClientSide ) - { - throw new UnsupportedOperationException( "Cannot run commands on the client" ); - } + if( getLevel().isClientSide ) 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 ); - return new CommandCallback( commandID ).pull; - } - - private int issueCommand( ITurtleCommand command ) - { commandQueue.offer( new TurtleCommandQueueEntry( ++commandsIssued, command ) ); - return commandsIssued; + int commandID = commandsIssued; + return new CommandCallback( commandID ).pull; } @Override public void playAnimation( @Nonnull TurtleAnimation animation ) { - if( getLevel().isClientSide ) - { - throw new UnsupportedOperationException( "Cannot play animations on the client" ); - } + if( getLevel().isClientSide ) throw new UnsupportedOperationException( "Cannot play animations on the client" ); this.animation = animation; if( this.animation == TurtleAnimation.SHORT_WAIT ) @@ -493,46 +524,123 @@ public void playAnimation( @Nonnull TurtleAnimation animation ) owner.updateBlock(); } - @Override - public ITurtleUpgrade getUpgrade( @Nonnull TurtleSide side ) + public ResourceLocation getOverlay() { - return upgrades.get( side ); + return overlay; } - @Override - public void setUpgrade( @Nonnull TurtleSide side, ITurtleUpgrade upgrade ) + public void setOverlay( ResourceLocation overlay ) { - // Remove old upgrade - if( upgrades.containsKey( side ) ) - { - if( upgrades.get( side ) == upgrade ) - { - return; - } - upgrades.remove( side ); - } - else + if( !Objects.equal( this.overlay, overlay ) ) { - if( upgrade == null ) - { - return; - } + this.overlay = overlay; + owner.updateBlock(); } + } - upgradeNBTData.remove( side ); + public DyeColor getDyeColour() + { + if( colourHex == -1 ) return null; + Colour colour = Colour.fromHex( colourHex ); + return colour == null ? null : DyeColor.byId( 15 - colour.ordinal() ); + } - // Set new upgrade - if( upgrade != null ) - { - upgrades.put( side, upgrade ); + public void setDyeColour( DyeColor dyeColour ) + { + int newColour = -1; + if( dyeColour != null ) + { + newColour = Colour.values()[15 - dyeColour.getId()].getHex(); + } + if( colourHex != newColour ) + { + colourHex = newColour; + owner.updateBlock(); + } + } + + @Override + public void setColour( int colour ) + { + if( colour >= 0 && colour <= 0xFFFFFF ) + { + if( colourHex != colour ) + { + colourHex = colour; + owner.updateBlock(); + } + } + else if( colourHex != -1 ) + { + colourHex = -1; + owner.updateBlock(); + } + } + + @Override + public int getColour() + { + return colourHex; + } + + public void setOwningPlayer( GameProfile profile ) + { + owningPlayer = profile; + } + + @Nullable + @Override + public GameProfile getOwningPlayer() + { + return owningPlayer; + } + + @Override + public ITurtleUpgrade getUpgrade( @Nonnull TurtleSide side ) + { + return upgrades.get( side ); + } + + @Override + public void setUpgrade( @Nonnull TurtleSide side, ITurtleUpgrade upgrade ) + { + if( !setUpgradeDirect( side, upgrade ) || owner.getLevel() == null ) return; + + // This is a separate function to avoid updating the block when reading the NBT. We don't need to do this as + // either the block is newly placed (and so won't have changed) or is being updated with /data, which calls + // updateBlock for us. + owner.updateBlock(); + + // Recompute peripherals in case an upgrade being removed has exposed a new peripheral. + // TODO: Only update peripherals, or even only two sides? + owner.updateInputsImmediately(); + } + + private boolean setUpgradeDirect( @Nonnull TurtleSide side, ITurtleUpgrade upgrade ) + { + // Remove old upgrade + if( upgrades.containsKey( side ) ) + { + if( upgrades.get( side ) == upgrade ) return false; + upgrades.remove( side ); + } + else + { + if( upgrade == null ) return false; } + upgradeNBTData.remove( side ); + + // Set new upgrade + if( upgrade != null ) upgrades.put( side, upgrade ); + // Notify clients and create peripherals - if( owner.getLevel() != null ) + if( owner.getLevel() != null && !owner.getLevel().isClientSide ) { updatePeripherals( owner.createServerComputer() ); - owner.updateBlock(); } + + return true; } @Override @@ -546,10 +654,7 @@ public IPeripheral getPeripheral( @Nonnull TurtleSide side ) public CompoundTag getUpgradeNBTData( TurtleSide side ) { CompoundTag nbt = upgradeNBTData.get( side ); - if( nbt == null ) - { - upgradeNBTData.put( side, nbt = new CompoundTag() ); - } + if( nbt == null ) upgradeNBTData.put( side, nbt = new CompoundTag() ); return nbt; } @@ -559,40 +664,110 @@ public void updateUpgradeNBTData( @Nonnull TurtleSide side ) owner.updateBlock(); } - @Nonnull - @Override - public Container getInventory() + public Vec3 getRenderOffset( float f ) { - return inventory; + switch( animation ) + { + case MOVE_FORWARD: + case MOVE_BACK: + case MOVE_UP: + case MOVE_DOWN: + { + // Get direction + Direction dir; + switch( animation ) + { + case MOVE_FORWARD: + default: + dir = getDirection(); + break; + case MOVE_BACK: + dir = getDirection().getOpposite(); + break; + case MOVE_UP: + dir = Direction.UP; + break; + case MOVE_DOWN: + dir = Direction.DOWN; + break; + } + + double distance = -1.0 + getAnimationFraction( f ); + return new Vec3( + distance * dir.getStepX(), + distance * dir.getStepY(), + distance * dir.getStepZ() + ); + } + default: + { + return Vec3.ZERO; + } + } } - public void setOwningPlayer( GameProfile profile ) + public float getToolRenderAngle( TurtleSide side, float f ) { - owningPlayer = profile; + return (side == TurtleSide.LEFT && animation == TurtleAnimation.SWING_LEFT_TOOL) || + (side == TurtleSide.RIGHT && animation == TurtleAnimation.SWING_RIGHT_TOOL) + ? 45.0f * (float) Math.sin( getAnimationFraction( f ) * Math.PI ) + : 0.0f; } - private void updateCommands() + private static ComputerSide toDirection( TurtleSide side ) { - if( animation != TurtleAnimation.NONE || commandQueue.isEmpty() ) + switch( side ) { - return; + case LEFT: + return ComputerSide.LEFT; + case RIGHT: + default: + return ComputerSide.RIGHT; } + } - // If we've got a computer, ensure that we're allowed to perform work. - ServerComputer computer = owner.getServerComputer(); - if( computer != null && !computer.getComputer() - .getMainThreadMonitor() - .canWork() ) + private void updatePeripherals( ServerComputer serverComputer ) + { + if( serverComputer == null ) return; + + // Update peripherals + for( TurtleSide side : TurtleSide.values() ) { - return; + ITurtleUpgrade upgrade = getUpgrade( side ); + IPeripheral peripheral = null; + if( upgrade != null && upgrade.getType().isPeripheral() ) + { + peripheral = upgrade.createPeripheral( this, side ); + } + + IPeripheral existing = peripherals.get( side ); + if( existing == peripheral || (existing != null && peripheral != null && existing.equals( peripheral )) ) + { + // If the peripheral is the same, just use that. + peripheral = existing; + } + else + { + // Otherwise update our map + peripherals.put( side, peripheral ); + } + + // Always update the computer: it may not be the same computer as before! + serverComputer.setPeripheral( toDirection( side ), peripheral ); } + } + + private void updateCommands() + { + if( animation != TurtleAnimation.NONE || commandQueue.isEmpty() ) return; + + // If we've got a computer, ensure that we're allowed to perform work. + ServerComputer computer = owner.getServerComputer(); + if( computer != null && !computer.getComputer().getMainThreadMonitor().canWork() ) return; // Pull a new command TurtleCommandQueueEntry nextCommand = commandQueue.poll(); - if( nextCommand == null ) - { - return; - } + if( nextCommand == null ) return; // Execute the command long start = System.nanoTime(); @@ -600,18 +775,10 @@ private void updateCommands() long end = System.nanoTime(); // Dispatch the callback - if( computer == null ) - { - return; - } - computer.getComputer() - .getMainThreadMonitor() - .trackWork( end - start, TimeUnit.NANOSECONDS ); + if( computer == null ) return; + computer.getComputer().getMainThreadMonitor().trackWork( end - start, TimeUnit.NANOSECONDS ); int callbackID = nextCommand.callbackID; - if( callbackID < 0 ) - { - return; - } + if( callbackID < 0 ) return; if( result != null && result.isSuccess() ) { @@ -627,17 +794,14 @@ private void updateCommands() else { computer.queueEvent( "turtle_response", new Object[] { - callbackID, - true, + callbackID, true, } ); } } else { computer.queueEvent( "turtle_response", new Object[] { - callbackID, - false, - result != null ? result.getErrorMessage() : null, + callbackID, false, result != null ? result.getErrorMessage() : null, } ); } } @@ -651,7 +815,10 @@ private void updateAnimation() if( ComputerCraft.turtlesCanPush ) { // Advance entity pushing - if( animation == TurtleAnimation.MOVE_FORWARD || animation == TurtleAnimation.MOVE_BACK || animation == TurtleAnimation.MOVE_UP || animation == TurtleAnimation.MOVE_DOWN ) + if( animation == TurtleAnimation.MOVE_FORWARD || + animation == TurtleAnimation.MOVE_BACK || + animation == TurtleAnimation.MOVE_UP || + animation == TurtleAnimation.MOVE_DOWN ) { BlockPos pos = getPosition(); Direction moveDir; @@ -737,13 +904,12 @@ private void updateAnimation() double x = position.x + world.random.nextGaussian() * 0.1; double y = position.y + 0.5 + world.random.nextGaussian() * 0.1; double z = position.z + world.random.nextGaussian() * 0.1; - world.addParticle( ParticleTypes.HEART, - x, - y, - z, + world.addParticle( + ParticleTypes.HEART, x, y, z, world.random.nextGaussian() * 0.02, world.random.nextGaussian() * 0.02, - world.random.nextGaussian() * 0.02 ); + world.random.nextGaussian() * 0.02 + ); } } } @@ -759,40 +925,6 @@ private void updateAnimation() } } - public Vec3 getRenderOffset( float f ) - { - switch( animation ) - { - case MOVE_FORWARD: - case MOVE_BACK: - case MOVE_UP: - case MOVE_DOWN: - // Get direction - Direction dir; - switch( animation ) - { - case MOVE_FORWARD: - default: - dir = getDirection(); - break; - case MOVE_BACK: - dir = getDirection().getOpposite(); - break; - case MOVE_UP: - dir = Direction.UP; - break; - case MOVE_DOWN: - dir = Direction.DOWN; - break; - } - - double distance = -1.0 + getAnimationFraction( f ); - return new Vec3( distance * dir.getStepX(), distance * dir.getStepY(), distance * dir.getStepZ() ); - default: - return Vec3.ZERO; - } - } - private float getAnimationFraction( float f ) { float next = (float) animationProgress / ANIM_DURATION; @@ -800,187 +932,6 @@ private float getAnimationFraction( float f ) return previous + (next - previous) * f; } - public void readFromNBT( CompoundTag nbt ) - { - readCommon( nbt ); - - // Read state - selectedSlot = nbt.getInt( NBT_SLOT ); - - // Read owner - if( nbt.contains( "Owner", NBTUtil.TAG_COMPOUND ) ) - { - CompoundTag owner = nbt.getCompound( "Owner" ); - owningPlayer = new GameProfile( new UUID( owner.getLong( "UpperId" ), owner.getLong( "LowerId" ) ), owner.getString( "Name" ) ); - } - else - { - owningPlayer = null; - } - } - - /** - * Read common data for saving and client synchronisation. - * - * @param nbt The tag to read from - */ - private void readCommon( CompoundTag nbt ) - { - // Read fields - colourHex = nbt.contains( NBT_COLOUR ) ? nbt.getInt( NBT_COLOUR ) : -1; - fuelLevel = nbt.contains( NBT_FUEL ) ? nbt.getInt( NBT_FUEL ) : 0; - overlay = nbt.contains( NBT_OVERLAY ) ? new ResourceLocation( nbt.getString( NBT_OVERLAY ) ) : null; - - // Read upgrades - setUpgrade( TurtleSide.LEFT, nbt.contains( NBT_LEFT_UPGRADE ) ? TurtleUpgrades.get( nbt.getString( NBT_LEFT_UPGRADE ) ) : null ); - setUpgrade( TurtleSide.RIGHT, nbt.contains( NBT_RIGHT_UPGRADE ) ? TurtleUpgrades.get( nbt.getString( NBT_RIGHT_UPGRADE ) ) : null ); - - // NBT - upgradeNBTData.clear(); - if( nbt.contains( NBT_LEFT_UPGRADE_DATA ) ) - { - upgradeNBTData.put( TurtleSide.LEFT, - nbt.getCompound( NBT_LEFT_UPGRADE_DATA ) - .copy() ); - } - if( nbt.contains( NBT_RIGHT_UPGRADE_DATA ) ) - { - upgradeNBTData.put( TurtleSide.RIGHT, - nbt.getCompound( NBT_RIGHT_UPGRADE_DATA ) - .copy() ); - } - } - - public CompoundTag writeToNBT( CompoundTag nbt ) - { - writeCommon( nbt ); - - // Write state - nbt.putInt( NBT_SLOT, selectedSlot ); - - // Write owner - if( owningPlayer != null ) - { - CompoundTag owner = new CompoundTag(); - nbt.put( "Owner", owner ); - - owner.putLong( "UpperId", owningPlayer.getId() - .getMostSignificantBits() ); - owner.putLong( "LowerId", owningPlayer.getId() - .getLeastSignificantBits() ); - owner.putString( "Name", owningPlayer.getName() ); - } - - return nbt; - } - - private void writeCommon( CompoundTag nbt ) - { - nbt.putInt( NBT_FUEL, fuelLevel ); - if( colourHex != -1 ) - { - nbt.putInt( NBT_COLOUR, colourHex ); - } - if( overlay != null ) - { - nbt.putString( NBT_OVERLAY, overlay.toString() ); - } - - // Write upgrades - String leftUpgradeId = getUpgradeId( getUpgrade( TurtleSide.LEFT ) ); - if( leftUpgradeId != null ) - { - nbt.putString( NBT_LEFT_UPGRADE, leftUpgradeId ); - } - String rightUpgradeId = getUpgradeId( getUpgrade( TurtleSide.RIGHT ) ); - if( rightUpgradeId != null ) - { - nbt.putString( NBT_RIGHT_UPGRADE, rightUpgradeId ); - } - - // Write upgrade NBT - if( upgradeNBTData.containsKey( TurtleSide.LEFT ) ) - { - nbt.put( NBT_LEFT_UPGRADE_DATA, - getUpgradeNBTData( TurtleSide.LEFT ).copy() ); - } - if( upgradeNBTData.containsKey( TurtleSide.RIGHT ) ) - { - nbt.put( NBT_RIGHT_UPGRADE_DATA, - getUpgradeNBTData( TurtleSide.RIGHT ).copy() ); - } - } - - private static String getUpgradeId( ITurtleUpgrade upgrade ) - { - return upgrade != null ? upgrade.getUpgradeID() - .toString() : null; - } - - public void readDescription( CompoundTag nbt ) - { - readCommon( nbt ); - - // Animation - TurtleAnimation anim = TurtleAnimation.values()[nbt.getInt( "Animation" )]; - if( anim != animation && anim != TurtleAnimation.WAIT && anim != TurtleAnimation.SHORT_WAIT && anim != TurtleAnimation.NONE ) - { - animation = anim; - animationProgress = 0; - lastAnimationProgress = 0; - } - } - - public void writeDescription( CompoundTag nbt ) - { - writeCommon( nbt ); - nbt.putInt( "Animation", animation.ordinal() ); - } - - public ResourceLocation getOverlay() - { - return overlay; - } - - public void setOverlay( ResourceLocation overlay ) - { - if( !Objects.equal( this.overlay, overlay ) ) - { - this.overlay = overlay; - owner.updateBlock(); - } - } - - public DyeColor getDyeColour() - { - if( colourHex == -1 ) - { - return null; - } - Colour colour = Colour.fromHex( colourHex ); - return colour == null ? null : DyeColor.byId( 15 - colour.ordinal() ); - } - - public void setDyeColour( DyeColor dyeColour ) - { - int newColour = -1; - if( dyeColour != null ) - { - newColour = Colour.values()[15 - dyeColour.getId()].getHex(); - } - if( colourHex != newColour ) - { - colourHex = newColour; - owner.updateBlock(); - } - } - - public float getToolRenderAngle( TurtleSide side, float f ) - { - return (side == TurtleSide.LEFT && animation == TurtleAnimation.SWING_LEFT_TOOL) || (side == TurtleSide.RIGHT && animation == TurtleAnimation.SWING_RIGHT_TOOL) ? 45.0f * (float) Math.sin( - getAnimationFraction( f ) * Math.PI ) : 0.0f; - } - private static final class CommandCallback implements ILuaCallback { final MethodResult pull = MethodResult.pullEvent( "turtle_response", this ); diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleCompareCommand.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleCompareCommand.java index d5acdc3d2..767f7e071 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleCompareCommand.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleCompareCommand.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.turtle.core; import dan200.computercraft.api.turtle.ITurtleAccess; @@ -37,8 +36,7 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle ) Direction direction = this.direction.toWorldDir( turtle ); // Get currently selected stack - ItemStack selectedStack = turtle.getInventory() - .getItem( turtle.getSelectedSlot() ); + ItemStack selectedStack = turtle.getInventory().getItem( turtle.getSelectedSlot() ); // Get stack representing thing in front Level world = turtle.getLevel(); @@ -79,6 +77,8 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle ) } // Compare them - return selectedStack.getItem() == lookAtStack.getItem() ? TurtleCommandResult.success() : TurtleCommandResult.failure(); + return selectedStack.getItem() == lookAtStack.getItem() + ? TurtleCommandResult.success() + : TurtleCommandResult.failure(); } } diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleCompareToCommand.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleCompareToCommand.java index c2f0261c3..24eeba440 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleCompareToCommand.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleCompareToCommand.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.turtle.core; import dan200.computercraft.api.turtle.ITurtleAccess; @@ -27,17 +26,10 @@ public TurtleCompareToCommand( int slot ) @Override public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle ) { - ItemStack selectedStack = turtle.getInventory() - .getItem( turtle.getSelectedSlot() ); - ItemStack stack = turtle.getInventory() - .getItem( slot ); - if( InventoryUtil.areItemsStackable( selectedStack, stack ) ) - { - return TurtleCommandResult.success(); - } - else - { - return TurtleCommandResult.failure(); - } + ItemStack selectedStack = turtle.getInventory().getItem( turtle.getSelectedSlot() ); + ItemStack stack = turtle.getInventory().getItem( slot ); + return InventoryUtil.areItemsStackable( selectedStack, stack ) + ? TurtleCommandResult.success() + : TurtleCommandResult.failure(); } } diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleCraftCommand.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleCraftCommand.java index ac7e7652d..c9635a1fb 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleCraftCommand.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleCraftCommand.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.turtle.core; import dan200.computercraft.api.turtle.ITurtleAccess; @@ -34,10 +33,7 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle ) // Craft the item TurtleInventoryCrafting crafting = new TurtleInventoryCrafting( turtle ); List results = crafting.doCrafting( turtle.getLevel(), limit ); - if( results == null ) - { - return TurtleCommandResult.failure( "No matching recipes" ); - } + if( results == null ) return TurtleCommandResult.failure( "No matching recipes" ); // Store or drop any remainders for( ItemStack stack : results ) @@ -49,10 +45,7 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle ) } } - if( !results.isEmpty() ) - { - turtle.playAnimation( TurtleAnimation.WAIT ); - } + if( !results.isEmpty() ) turtle.playAnimation( TurtleAnimation.WAIT ); return TurtleCommandResult.success(); } } diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleDetectCommand.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleDetectCommand.java index e422cbdb2..51e613b77 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleDetectCommand.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleDetectCommand.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.turtle.core; import dan200.computercraft.api.turtle.ITurtleAccess; @@ -37,6 +36,8 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle ) BlockPos oldPosition = turtle.getPosition(); BlockPos newPosition = oldPosition.relative( direction ); - return !WorldUtil.isLiquidBlock( world, newPosition ) && !world.isEmptyBlock( newPosition ) ? TurtleCommandResult.success() : TurtleCommandResult.failure(); + return !WorldUtil.isLiquidBlock( world, newPosition ) && !world.isEmptyBlock( newPosition ) + ? TurtleCommandResult.success() + : TurtleCommandResult.failure(); } } diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleDropCommand.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleDropCommand.java index a035ba148..3b0408b33 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleDropCommand.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleDropCommand.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.turtle.core; import dan200.computercraft.api.turtle.ITurtleAccess; diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleEquipCommand.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleEquipCommand.java index b87bc201f..b03851d4e 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleEquipCommand.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleEquipCommand.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.turtle.core; import dan200.computercraft.api.turtle.*; @@ -39,10 +38,7 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle ) { newUpgradeStack = selectedStack.copy(); newUpgrade = TurtleUpgrades.get( newUpgradeStack ); - if( newUpgrade == null || !TurtleUpgrades.suitableForFamily( ((TurtleBrain) turtle).getFamily(), newUpgrade ) ) - { - return TurtleCommandResult.failure( "Not a valid upgrade" ); - } + if( newUpgrade == null ) return TurtleCommandResult.failure( "Not a valid upgrade" ); } else { diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleInspectCommand.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleInspectCommand.java index 1dee94820..42cab180d 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleInspectCommand.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleInspectCommand.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.turtle.core; import dan200.computercraft.api.turtle.ITurtleAccess; @@ -14,7 +13,6 @@ import net.minecraft.core.Direction; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.Property; import javax.annotation.Nonnull; import java.util.HashMap; @@ -42,26 +40,11 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle ) BlockPos newPosition = oldPosition.relative( direction ); BlockState state = world.getBlockState( newPosition ); - if( state.isAir() ) - { - return TurtleCommandResult.failure( "No block to inspect" ); - } + if( state.isAir() ) return TurtleCommandResult.failure( "No block to inspect" ); Map table = BlockData.fill( new HashMap<>(), state ); return TurtleCommandResult.success( new Object[] { table } ); - } - @SuppressWarnings( { - "unchecked", - "rawtypes" - } ) - private static Object getPropertyValue( Property property, Comparable value ) - { - if( value instanceof String || value instanceof Number || value instanceof Boolean ) - { - return value; - } - return property.getName( value ); } } 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 0d6956fae..3b81ebda6 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleMoveCommand.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleMoveCommand.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.turtle.core; import dan200.computercraft.ComputerCraft; @@ -27,7 +26,6 @@ public class TurtleMoveCommand implements ITurtleCommand { - private static final AABB EMPTY_BOX = new AABB( 0, 0, 0, 0, 0, 0 ); private final MoveDirection direction; public TurtleMoveCommand( MoveDirection direction ) @@ -47,7 +45,7 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle ) BlockPos oldPosition = turtle.getPosition(); BlockPos newPosition = oldPosition.relative( direction ); - TurtlePlayer turtlePlayer = TurtlePlaceCommand.createPlayer( turtle, oldPosition, direction ); + TurtlePlayer turtlePlayer = TurtlePlayer.getWithPosition( turtle, oldPosition, direction ); TurtleCommandResult canEnterResult = canEnter( turtlePlayer, oldWorld, newPosition ); if( !canEnterResult.isSuccess() ) { @@ -56,15 +54,19 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle ) // Check existing block is air or replaceable BlockState state = oldWorld.getBlockState( newPosition ); - if( !oldWorld.isEmptyBlock( newPosition ) && !WorldUtil.isLiquidBlock( oldWorld, newPosition ) && !state.getMaterial() - .isReplaceable() ) + if( !oldWorld.isEmptyBlock( newPosition ) && + !WorldUtil.isLiquidBlock( oldWorld, newPosition ) && + !state.getMaterial().isReplaceable() ) { return TurtleCommandResult.failure( "Movement obstructed" ); } // Check there isn't anything in the way - VoxelShape collision = state.getCollisionShape( oldWorld, oldPosition ) - .move( newPosition.getX(), newPosition.getY(), newPosition.getZ() ); + VoxelShape collision = state.getCollisionShape( oldWorld, oldPosition ).move( + newPosition.getX(), + newPosition.getY(), + newPosition.getZ() + ); if( !oldWorld.isUnobstructed( null, collision ) ) { @@ -77,8 +79,11 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle ) List list = oldWorld.getEntitiesOfClass( Entity.class, getBox( collision ), x -> x != null && x.isAlive() && x.blocksBuilding ); for( Entity entity : list ) { - AABB pushedBB = entity.getBoundingBox() - .move( direction.getStepX(), direction.getStepY(), direction.getStepZ() ); + AABB pushedBB = entity.getBoundingBox().move( + direction.getStepX(), + direction.getStepY(), + direction.getStepZ() + ); if( !oldWorld.isUnobstructed( null, Shapes.create( pushedBB ) ) ) { return TurtleCommandResult.failure( "Movement obstructed" ); @@ -93,10 +98,7 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle ) } // Move - if( !turtle.teleportTo( oldWorld, newPosition ) ) - { - return TurtleCommandResult.failure( "Movement failed" ); - } + if( !turtle.teleportTo( oldWorld, newPosition ) ) return TurtleCommandResult.failure( "Movement failed" ); // Consume fuel turtle.consumeFuel( 1 ); @@ -127,10 +129,7 @@ private static TurtleCommandResult canEnter( TurtlePlayer turtlePlayer, Level wo { return TurtleCommandResult.failure( position.getY() < 0 ? "Too low to move" : "Too high to move" ); } - if( !world.isInWorldBounds( position ) ) - { - return TurtleCommandResult.failure( "Cannot leave the world" ); - } + if( !world.isInWorldBounds( position ) ) return TurtleCommandResult.failure( "Cannot leave the world" ); // Check spawn protection if( ComputerCraft.turtlesObeyBlockProtection && !TurtlePermissions.isBlockEnterable( world, position, turtlePlayer ) ) @@ -138,12 +137,8 @@ private static TurtleCommandResult canEnter( TurtlePlayer turtlePlayer, Level wo return TurtleCommandResult.failure( "Cannot enter protected area" ); } - if( !world.hasChunkAt( position ) ) - { - return TurtleCommandResult.failure( "Cannot leave loaded world" ); - } - if( !world.getWorldBorder() - .isWithinBounds( position ) ) + if( !world.isLoaded( position ) ) return TurtleCommandResult.failure( "Cannot leave loaded world" ); + if( !world.getWorldBorder().isWithinBounds( position ) ) { return TurtleCommandResult.failure( "Cannot pass the world border" ); } @@ -155,4 +150,6 @@ private static AABB getBox( VoxelShape shape ) { return shape.isEmpty() ? EMPTY_BOX : shape.bounds(); } + + private static final AABB EMPTY_BOX = new AABB( 0, 0, 0, 0, 0, 0 ); } 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 edf5d8181..1907359ff 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlaceCommand.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlaceCommand.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.turtle.core; import dan200.computercraft.ComputerCraft; @@ -12,10 +11,7 @@ import dan200.computercraft.api.turtle.TurtleAnimation; import dan200.computercraft.api.turtle.TurtleCommandResult; import dan200.computercraft.shared.TurtlePermissions; -import dan200.computercraft.shared.util.DirectionUtil; -import dan200.computercraft.shared.util.DropConsumer; -import dan200.computercraft.shared.util.InventoryUtil; -import dan200.computercraft.shared.util.WorldUtil; +import dan200.computercraft.shared.util.*; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.network.chat.TextComponent; @@ -28,6 +24,7 @@ import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.item.context.UseOnContext; import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.SignBlockEntity; import net.minecraft.world.level.block.state.BlockState; @@ -36,7 +33,6 @@ import org.apache.commons.lang3.tuple.Pair; import javax.annotation.Nonnull; -import java.util.List; public class TurtlePlaceCommand implements ITurtleCommand { @@ -49,168 +45,74 @@ public TurtlePlaceCommand( InteractDirection direction, Object[] arguments ) extraArguments = arguments; } - public static ItemStack deploy( @Nonnull ItemStack stack, ITurtleAccess turtle, Direction direction, Object[] extraArguments, String[] outErrorMessage ) - { - // Create a fake player, and orient it appropriately - BlockPos playerPosition = turtle.getPosition() - .relative( direction ); - TurtlePlayer turtlePlayer = createPlayer( turtle, playerPosition, direction ); - - return deploy( stack, turtle, turtlePlayer, direction, extraArguments, outErrorMessage ); - } - @Nonnull @Override public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle ) { // Get thing to place - ItemStack stack = turtle.getInventory() - .getItem( turtle.getSelectedSlot() ); - if( stack.isEmpty() ) - { - return TurtleCommandResult.failure( "No items to place" ); - } + ItemStack stack = turtle.getInventory().getItem( turtle.getSelectedSlot() ); + if( stack.isEmpty() ) return TurtleCommandResult.failure( "No items to place" ); // Remember old block Direction direction = this.direction.toWorldDir( turtle ); - BlockPos coordinates = turtle.getPosition() - .relative( direction ); + BlockPos coordinates = turtle.getPosition().relative( direction ); // Create a fake player, and orient it appropriately - BlockPos playerPosition = turtle.getPosition() - .relative( direction ); - TurtlePlayer turtlePlayer = createPlayer( turtle, playerPosition, direction ); + BlockPos playerPosition = turtle.getPosition().relative( direction ); + TurtlePlayer turtlePlayer = TurtlePlayer.getWithPosition( turtle, playerPosition, direction ); // Do the deploying - String[] errorMessage = new String[1]; - ItemStack remainder = deploy( stack, turtle, turtlePlayer, direction, extraArguments, errorMessage ); - if( remainder != stack ) + turtlePlayer.loadInventory( turtle ); + ErrorMessage message = new ErrorMessage(); + boolean result = deploy( stack, turtle, turtlePlayer, direction, extraArguments, message ); + turtlePlayer.unloadInventory( turtle ); + if( result ) { - // Put the remaining items back - turtle.getInventory() - .setItem( turtle.getSelectedSlot(), remainder ); - turtle.getInventory() - .setChanged(); - // Animate and return success turtle.playAnimation( TurtleAnimation.WAIT ); return TurtleCommandResult.success(); } + else if( message.message != null ) + { + return TurtleCommandResult.failure( message.message ); + } else { - if( errorMessage[0] != null ) - { - return TurtleCommandResult.failure( errorMessage[0] ); - } - else if( stack.getItem() instanceof BlockItem ) - { - return TurtleCommandResult.failure( "Cannot place block here" ); - } - else - { - return TurtleCommandResult.failure( "Cannot place item here" ); - } + return TurtleCommandResult.failure( stack.getItem() instanceof BlockItem ? "Cannot place block here" : "Cannot place item here" ); } } - public static TurtlePlayer createPlayer( ITurtleAccess turtle, BlockPos position, Direction direction ) + public static boolean deployCopiedItem( @Nonnull ItemStack stack, ITurtleAccess turtle, Direction direction, Object[] extraArguments, ErrorMessage outErrorMessage ) { - TurtlePlayer turtlePlayer = TurtlePlayer.get( turtle ); - orientPlayer( turtle, turtlePlayer, position, direction ); - return turtlePlayer; + // Create a fake player, and orient it appropriately + BlockPos playerPosition = turtle.getPosition().relative( direction ); + TurtlePlayer turtlePlayer = TurtlePlayer.getWithPosition( turtle, playerPosition, direction ); + turtlePlayer.loadInventory( stack ); + boolean result = deploy( stack, turtle, turtlePlayer, direction, extraArguments, outErrorMessage ); + turtlePlayer.getInventory().clearContent(); + return result; } - public static ItemStack deploy( @Nonnull ItemStack stack, ITurtleAccess turtle, TurtlePlayer turtlePlayer, Direction direction, - Object[] extraArguments, String[] outErrorMessage ) + private static boolean deploy( @Nonnull ItemStack stack, ITurtleAccess turtle, TurtlePlayer turtlePlayer, Direction direction, Object[] extraArguments, ErrorMessage outErrorMessage ) { // Deploy on an entity - ItemStack remainder = deployOnEntity( stack, turtle, turtlePlayer, direction, extraArguments, outErrorMessage ); - if( remainder != stack ) - { - return remainder; - } + if( deployOnEntity( stack, turtle, turtlePlayer ) ) return true; - // Deploy on the block immediately in front BlockPos position = turtle.getPosition(); BlockPos newPosition = position.relative( direction ); - remainder = deployOnBlock( stack, turtle, turtlePlayer, newPosition, direction.getOpposite(), extraArguments, true, outErrorMessage ); - if( remainder != stack ) - { - return remainder; - } - - // Deploy on the block one block away - remainder = deployOnBlock( stack, - turtle, - turtlePlayer, - newPosition.relative( direction ), - direction.getOpposite(), - extraArguments, - false, - outErrorMessage ); - if( remainder != stack ) - { - return remainder; - } - if( direction.getAxis() != Direction.Axis.Y ) - { + // Try to deploy against a block. Tries the following options: + // Deploy on the block immediately in front + return deployOnBlock( stack, turtle, turtlePlayer, newPosition, direction.getOpposite(), extraArguments, true, outErrorMessage ) + // Deploy on the block one block away + || deployOnBlock( stack, turtle, turtlePlayer, newPosition.relative( direction ), direction.getOpposite(), extraArguments, false, outErrorMessage ) // Deploy down on the block in front - remainder = deployOnBlock( stack, turtle, turtlePlayer, newPosition.below(), Direction.UP, extraArguments, false, outErrorMessage ); - if( remainder != stack ) - { - return remainder; - } - } - - // Deploy back onto the turtle - remainder = deployOnBlock( stack, turtle, turtlePlayer, position, direction, extraArguments, false, outErrorMessage ); - return remainder; - - // If nothing worked, return the original stack unchanged + || (direction.getAxis() != Direction.Axis.Y && deployOnBlock( stack, turtle, turtlePlayer, newPosition.below(), Direction.UP, extraArguments, false, outErrorMessage )) + // Deploy back onto the turtle + || deployOnBlock( stack, turtle, turtlePlayer, position, direction, extraArguments, false, outErrorMessage ); } - private static void orientPlayer( ITurtleAccess turtle, TurtlePlayer turtlePlayer, BlockPos position, Direction direction ) - { - double posX = position.getX() + 0.5; - double posY = position.getY() + 0.5; - double posZ = position.getZ() + 0.5; - - // Stop intersection with the turtle itself - if( turtle.getPosition() - .equals( position ) ) - { - posX += 0.48 * direction.getStepX(); - posY += 0.48 * direction.getStepY(); - posZ += 0.48 * direction.getStepZ(); - } - - if( direction.getAxis() != Direction.Axis.Y ) - { - turtlePlayer.setYRot( direction.toYRot() ); - turtlePlayer.setXRot( 0.0f ); - } - else - { - turtlePlayer.setYRot( turtle.getDirection() - .toYRot() ); - turtlePlayer.setXRot( DirectionUtil.toPitchAngle( direction ) ); - } - - turtlePlayer.setPosRaw( posX, posY, posZ ); - turtlePlayer.xo = posX; - turtlePlayer.yo = posY; - turtlePlayer.zo = posZ; - turtlePlayer.xRotO = turtlePlayer.getXRot(); - turtlePlayer.yRotO = turtlePlayer.getYRot(); - - turtlePlayer.yHeadRot = turtlePlayer.getYRot(); - turtlePlayer.yHeadRotO = turtlePlayer.yHeadRot; - } - - @Nonnull - private static ItemStack deployOnEntity( @Nonnull ItemStack stack, final ITurtleAccess turtle, TurtlePlayer turtlePlayer, Direction direction, - Object[] extraArguments, String[] outErrorMessage ) + private static boolean deployOnEntity( @Nonnull ItemStack stack, final ITurtleAccess turtle, TurtlePlayer turtlePlayer ) { // See if there is an entity present final Level world = turtle.getLevel(); @@ -218,217 +120,185 @@ private static ItemStack deployOnEntity( @Nonnull ItemStack stack, final ITurtle Vec3 turtlePos = turtlePlayer.position(); Vec3 rayDir = turtlePlayer.getViewVector( 1.0f ); Pair hit = WorldUtil.rayTraceEntities( world, turtlePos, rayDir, 1.5 ); - if( hit == null ) - { - return stack; - } - - // Load up the turtle's inventory - ItemStack stackCopy = stack.copy(); - turtlePlayer.loadInventory( stackCopy ); + if( hit == null ) return false; // Start claiming entity drops Entity hitEntity = hit.getKey(); Vec3 hitPos = hit.getValue(); - DropConsumer.set( hitEntity, drop -> InventoryUtil.storeItems( drop, turtle.getItemHandler(), turtle.getSelectedSlot() ) ); - // Place on the entity - boolean placed = false; - InteractionResult cancelResult = hitEntity.interactAt( turtlePlayer, hitPos, InteractionHand.MAIN_HAND ); + ItemStorage itemHandler = ItemStorage.wrap( turtlePlayer.getInventory() ); + DropConsumer.set( hitEntity, drop -> InventoryUtil.storeItems( drop, itemHandler, 1 ) ); - if( cancelResult != null && cancelResult.consumesAction() ) - { - placed = true; - } - else - { - cancelResult = hitEntity.interact( turtlePlayer, InteractionHand.MAIN_HAND ); - if( cancelResult != null && cancelResult.consumesAction() ) - { - placed = true; - } - else if( hitEntity instanceof LivingEntity ) - { - placed = stackCopy.interactLivingEntity( turtlePlayer, (LivingEntity) hitEntity, InteractionHand.MAIN_HAND ).consumesAction(); - if( placed ) turtlePlayer.loadInventory( stackCopy ); - } - } + boolean placed = doDeployOnEntity( stack, turtlePlayer, hitEntity, hitPos ); - // Stop claiming drops - List remainingDrops = DropConsumer.clear(); - for( ItemStack remaining : remainingDrops ) - { - WorldUtil.dropItemStack( remaining, - world, - position, - turtle.getDirection() - .getOpposite() ); - } + DropConsumer.clearAndDrop( world, position, turtle.getDirection().getOpposite() ); + return placed; + } + + /** + * Place a block onto an entity. For instance, feeding cows. + * + * @param stack The stack we're placing. + * @param turtlePlayer The player of the turtle we're placing. + * @param hitEntity The entity we're interacting with. + * @param hitPos The position our ray trace hit the entity. + * @return If this item was deployed. + * @see net.minecraft.server.network.ServerGamePacketListenerImpl#handleInteract(ServerboundInteractPacket) + * @see net.minecraft.world.entity.player.Player#interactOn(Entity, InteractionHand) + */ + private static boolean doDeployOnEntity( @Nonnull ItemStack stack, TurtlePlayer turtlePlayer, @Nonnull Entity hitEntity, @Nonnull Vec3 hitPos ) + { + // Placing "onto" a block follows two flows. First we try to interactAt. If that doesn't succeed, then we try to + // call the normal interact path. Cancelling an interactAt *does not* cancel a normal interact path. + + InteractionResult interactAt = hitEntity.interactAt( turtlePlayer, hitPos, InteractionHand.MAIN_HAND ); + if( interactAt.consumesAction() ) return true; - // Put everything we collected into the turtles inventory, then return - ItemStack remainder = turtlePlayer.unloadInventory( turtle ); - if( !placed && ItemStack.matches( stack, remainder ) ) + if( hitEntity.interact( turtlePlayer, InteractionHand.MAIN_HAND ).consumesAction() ) return true; + if( hitEntity instanceof LivingEntity hitLiving ) { - return stack; + return stack.interactLivingEntity( turtlePlayer, hitLiving, InteractionHand.MAIN_HAND ).consumesAction(); } - else if( !remainder.isEmpty() ) + + return false; + } + + private static boolean canDeployOnBlock( + @Nonnull BlockPlaceContext context, ITurtleAccess turtle, TurtlePlayer player, BlockPos position, + Direction side, boolean allowReplaceable, ErrorMessage outErrorMessage + ) + { + Level world = turtle.getLevel(); + if( !world.isInWorldBounds( position ) || world.isEmptyBlock( position ) || + (context.getItemInHand().getItem() instanceof BlockItem && WorldUtil.isLiquidBlock( world, position )) ) { - return remainder; + return false; } - else + + BlockState state = world.getBlockState( position ); + + boolean replaceable = state.canBeReplaced( context ); + if( !allowReplaceable && replaceable ) return false; + + if( ComputerCraft.turtlesObeyBlockProtection ) { - return ItemStack.EMPTY; + // Check spawn protection + boolean editable = replaceable + ? TurtlePermissions.isBlockEditable( world, position, player ) + : TurtlePermissions.isBlockEditable( world, position.relative( side ), player ); + if( !editable ) + { + if( outErrorMessage != null ) outErrorMessage.message = "Cannot place in protected area"; + return false; + } } + + return true; } - @Nonnull - private static ItemStack deployOnBlock( @Nonnull ItemStack stack, ITurtleAccess turtle, TurtlePlayer turtlePlayer, BlockPos position, Direction side, - Object[] extraArguments, boolean allowReplace, String[] outErrorMessage ) + private static boolean deployOnBlock( + @Nonnull ItemStack stack, ITurtleAccess turtle, TurtlePlayer turtlePlayer, BlockPos position, Direction side, + Object[] extraArguments, boolean allowReplace, ErrorMessage outErrorMessage + ) { // Re-orient the fake player Direction playerDir = side.getOpposite(); BlockPos playerPosition = position.relative( side ); - orientPlayer( turtle, turtlePlayer, playerPosition, playerDir ); - - ItemStack stackCopy = stack.copy(); - turtlePlayer.loadInventory( stackCopy ); + turtlePlayer.setPosition( turtle, playerPosition, playerDir ); // Calculate where the turtle would hit the block float hitX = 0.5f + side.getStepX() * 0.5f; float hitY = 0.5f + side.getStepY() * 0.5f; float hitZ = 0.5f + side.getStepZ() * 0.5f; - if( Math.abs( hitY - 0.5f ) < 0.01f ) - { - hitY = 0.45f; - } + if( Math.abs( hitY - 0.5f ) < 0.01f ) hitY = 0.45f; // Check if there's something suitable to place onto BlockHitResult hit = new BlockHitResult( new Vec3( hitX, hitY, hitZ ), side, position, false ); UseOnContext context = new UseOnContext( turtlePlayer, InteractionHand.MAIN_HAND, hit ); - BlockPlaceContext placementContext = new BlockPlaceContext( context ); if( !canDeployOnBlock( new BlockPlaceContext( context ), turtle, turtlePlayer, position, side, allowReplace, outErrorMessage ) ) { - return stack; + return false; } - // Load up the turtle's inventory Item item = stack.getItem(); + BlockEntity existingTile = turtle.getLevel().getBlockEntity( position ); - // Do the deploying (put everything in the players inventory) - boolean placed = false; - BlockEntity existingTile = turtle.getLevel() - .getBlockEntity( position ); - - if( stackCopy.useOn( context ).consumesAction() ) - { - placed = true; - turtlePlayer.loadInventory( stackCopy ); - } - - if( !placed && (item instanceof BucketItem || item instanceof BoatItem || item instanceof WaterLilyBlockItem || item instanceof BottleItem) ) - { - InteractionResultHolder result = stackCopy.use( turtle.getLevel(), turtlePlayer, InteractionHand.MAIN_HAND ); - if( result.getResult() - .consumesAction() && !ItemStack.matches( stack, result.getObject() ) ) - { - placed = true; - turtlePlayer.loadInventory( result.getObject() ); - } - } + boolean placed = doDeployOnBlock( stack, turtlePlayer, position, context, hit ).consumesAction(); // Set text on signs - if( placed && item instanceof SignItem ) + if( placed && item instanceof SignItem && extraArguments != null && extraArguments.length >= 1 && extraArguments[0] instanceof String message ) { - if( extraArguments != null && extraArguments.length >= 1 && extraArguments[0] instanceof String ) + Level world = turtle.getLevel(); + BlockEntity tile = world.getBlockEntity( position ); + if( tile == null || tile == existingTile ) { - Level world = turtle.getLevel(); - BlockEntity tile = world.getBlockEntity( position ); - if( tile == null || tile == existingTile ) - { - tile = world.getBlockEntity( position.relative( side ) ); - } - if( tile instanceof SignBlockEntity ) - { - SignBlockEntity signTile = (SignBlockEntity) tile; - String s = (String) extraArguments[0]; - String[] split = s.split( "\n" ); - int firstLine = split.length <= 2 ? 1 : 0; - for( int i = 0; i < 4; i++ ) - { - if( i >= firstLine && i < firstLine + split.length ) - { - if( split[i - firstLine].length() > 15 ) - { - signTile.setMessage( i, new TextComponent( split[i - firstLine].substring( 0, 15 ) ) ); - } - else - { - signTile.setMessage( i, new TextComponent( split[i - firstLine] ) ); - } - } - else - { - signTile.setMessage( i, new TextComponent( "" ) ); - } - } - signTile.setChanged(); - world.sendBlockUpdated( tile.getBlockPos(), tile.getBlockState(), tile.getBlockState(), 3 ); - } + tile = world.getBlockEntity( position.relative( side ) ); } - } - // Put everything we collected into the turtles inventory, then return - ItemStack remainder = turtlePlayer.unloadInventory( turtle ); - if( !placed && ItemStack.matches( stack, remainder ) ) - { - return stack; - } - else if( !remainder.isEmpty() ) - { - return remainder; - } - else - { - return ItemStack.EMPTY; + if( tile instanceof SignBlockEntity ) setSignText( world, tile, message ); } + + return placed; } - private static boolean canDeployOnBlock( @Nonnull BlockPlaceContext context, ITurtleAccess turtle, TurtlePlayer player, BlockPos position, - Direction side, boolean allowReplaceable, String[] outErrorMessage ) + /** + * Attempt to place an item into the world. Returns true/false if an item was placed. + * + * @param stack The stack the player is using. + * @param turtlePlayer The player which represents the turtle + * @param position The block we're deploying against's position. + * @param context The context of this place action. + * @param hit Where the block we're placing against was clicked. + * @return If this item was deployed. + * @see net.minecraft.server.level.ServerPlayerGameMode#useItemOn For the original implementation. + */ + private static InteractionResult doDeployOnBlock( + @Nonnull ItemStack stack, TurtlePlayer turtlePlayer, BlockPos position, UseOnContext context, BlockHitResult hit + ) { - Level world = turtle.getLevel(); - if( !world.isInWorldBounds( position ) || world.isEmptyBlock( position ) || (context.getItemInHand() - .getItem() instanceof BlockItem && WorldUtil.isLiquidBlock( world, - position )) ) - { - return false; - } + InteractionResult useResult = stack.useOn( context ); + if( useResult != InteractionResult.PASS ) return useResult; - BlockState state = world.getBlockState( position ); - - boolean replaceable = state.canBeReplaced( context ); - if( !allowReplaceable && replaceable ) + Item item = stack.getItem(); + if( item instanceof BucketItem || item instanceof BoatItem || item instanceof WaterLilyBlockItem || item instanceof BottleItem ) { - return false; + InteractionResultHolder result = stack.use( context.getLevel(), turtlePlayer, InteractionHand.MAIN_HAND ); + if( result.getResult().consumesAction() && !ItemStack.matches( stack, result.getObject() ) ) + { + turtlePlayer.setItemInHand( InteractionHand.MAIN_HAND, result.getObject() ); + return result.getResult(); + } } - if( ComputerCraft.turtlesObeyBlockProtection ) + return InteractionResult.PASS; + } + + private static void setSignText( Level world, BlockEntity tile, String message ) + { + SignBlockEntity signTile = (SignBlockEntity) tile; + String[] split = message.split( "\n" ); + int firstLine = split.length <= 2 ? 1 : 0; + for( int i = 0; i < 4; i++ ) { - // Check spawn protection - boolean editable = replaceable ? TurtlePermissions.isBlockEditable( world, position, player ) : TurtlePermissions.isBlockEditable( world, - position.relative( - side ), - player ); - if( !editable ) + if( i >= firstLine && i < firstLine + split.length ) { - if( outErrorMessage != null ) - { - outErrorMessage[0] = "Cannot place in protected area"; - } - return false; + String line = split[i - firstLine]; + signTile.setMessage( i, line.length() > 15 + ? new TextComponent( line.substring( 0, 15 ) ) + : new TextComponent( line ) + ); + } + else + { + signTile.setMessage( i, new TextComponent( "" ) ); } } + signTile.setChanged(); + world.sendBlockUpdated( tile.getBlockPos(), tile.getBlockState(), tile.getBlockState(), Block.UPDATE_ALL ); + } - return true; + private static class ErrorMessage + { + String message; } } 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 fc7830834..7ab8e8931 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtlePlayer.java @@ -3,14 +3,14 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.turtle.core; import com.mojang.authlib.GameProfile; import dan200.computercraft.ComputerCraft; import dan200.computercraft.api.turtle.FakePlayer; import dan200.computercraft.api.turtle.ITurtleAccess; -import dan200.computercraft.shared.ComputerCraftRegistry; +import dan200.computercraft.shared.Registry; +import dan200.computercraft.shared.util.DirectionUtil; import dan200.computercraft.shared.util.FakeNetHandler; import dan200.computercraft.shared.util.InventoryUtil; import dan200.computercraft.shared.util.WorldUtil; @@ -19,6 +19,7 @@ import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.world.Container; +import net.minecraft.world.InteractionHand; import net.minecraft.world.MenuProvider; import net.minecraft.world.effect.MobEffectInstance; import net.minecraft.world.entity.Entity; @@ -35,12 +36,13 @@ import java.util.OptionalInt; import java.util.UUID; -@SuppressWarnings( "EntityConstructor" ) public final class TurtlePlayer extends FakePlayer { - private static final GameProfile DEFAULT_PROFILE = new GameProfile( UUID.fromString( "0d0c4ca0-4ff1-11e4-916c-0800200c9a66" ), "[ComputerCraft]" ); + private static final GameProfile DEFAULT_PROFILE = new GameProfile( + UUID.fromString( "0d0c4ca0-4ff1-11e4-916c-0800200c9a66" ), + "[ComputerCraft]" + ); - // TODO [M3R1-01] Fix Turtle not giving player achievement for actions private TurtlePlayer( ServerLevel world, GameProfile name ) { super( world, name ); @@ -60,7 +62,7 @@ private static TurtlePlayer create( ITurtleAccess turtle ) // Constructing a player overrides the "active player" variable in advancements. As fake players cannot // get advancements, this prevents a normal player who has placed a turtle from getting advancements. // We try to locate the "actual" player and restore them. - ServerPlayer actualPlayer = world.getServer().getPlayerList().getPlayer( player.getUUID() ); + ServerPlayer actualPlayer = world.getServer().getPlayerList().getPlayer( profile.getId() ); if( actualPlayer != null ) player.getAdvancements().setPlayer( actualPlayer ); } @@ -72,93 +74,148 @@ private static GameProfile getProfile( @Nullable GameProfile profile ) return profile != null && profile.isComplete() ? profile : DEFAULT_PROFILE; } + public static TurtlePlayer get( ITurtleAccess access ) + { + if( !(access instanceof TurtleBrain brain) ) return create( access ); + + TurtlePlayer player = brain.cachedPlayer; + if( player == null || player.getGameProfile() != getProfile( access.getOwningPlayer() ) + || player.getCommandSenderWorld() != access.getLevel() ) + { + player = brain.cachedPlayer = create( brain ); + } + else + { + player.setState( access ); + } + + return player; + } + + public static TurtlePlayer getWithPosition( ITurtleAccess turtle, BlockPos position, Direction direction ) + { + TurtlePlayer turtlePlayer = get( turtle ); + turtlePlayer.setPosition( turtle, position, direction ); + return turtlePlayer; + } + private void setState( ITurtleAccess turtle ) { if( containerMenu != inventoryMenu ) { ComputerCraft.log.warn( "Turtle has open container ({})", containerMenu ); - closeContainer(); + doCloseContainer(); } BlockPos position = turtle.getPosition(); setPosRaw( position.getX() + 0.5, position.getY() + 0.5, position.getZ() + 0.5 ); - setYRot( turtle.getDirection() - .toYRot() ); - setXRot( 0.0f ); + setRot( turtle.getDirection().toYRot(), 0 ); getInventory().clearContent(); } - public static TurtlePlayer get( ITurtleAccess access ) + public void setPosition( ITurtleAccess turtle, BlockPos position, Direction direction ) { - if( !(access instanceof TurtleBrain) ) return create( access ); + double posX = position.getX() + 0.5; + double posY = position.getY() + 0.5; + double posZ = position.getZ() + 0.5; - TurtleBrain brain = (TurtleBrain) access; - TurtlePlayer player = brain.cachedPlayer; - if( player == null || player.getGameProfile() != getProfile( access.getOwningPlayer() ) || player.getCommandSenderWorld() != access.getLevel() ) + // Stop intersection with the turtle itself + if( turtle.getPosition().equals( position ) ) { - player = brain.cachedPlayer = create( brain ); + posX += 0.48 * direction.getStepX(); + posY += 0.48 * direction.getStepY(); + posZ += 0.48 * direction.getStepZ(); + } + + if( direction.getAxis() != Direction.Axis.Y ) + { + setRot( direction.toYRot(), 0 ); } else { - player.setState( access ); + setRot( turtle.getDirection().toYRot(), DirectionUtil.toPitchAngle( direction ) ); } - return player; + setPosRaw( posX, posY, posZ ); + xo = posX; + yo = posY; + zo = posZ; + xRotO = getXRot(); + yRotO = getYRot(); + + yHeadRot = getYRot(); + yHeadRotO = yHeadRot; + } + + public void loadInventory( @Nonnull ItemStack stack ) + { + getInventory().clearContent(); + getInventory().selected = 0; + getInventory().setItem( 0, stack ); } - public void loadInventory( @Nonnull ItemStack currentStack ) + public void loadInventory( @Nonnull ITurtleAccess turtle ) { + getInventory().clearContent(); + + int currentSlot = turtle.getSelectedSlot(); + int slots = turtle.getItemHandler().size(); + // Load up the fake inventory getInventory().selected = 0; - getInventory().setItem( 0, currentStack ); + for( int i = 0; i < slots; i++ ) + { + getInventory().setItem( i, turtle.getItemHandler().getStack( (currentSlot + i) % slots ) ); + } } - public ItemStack unloadInventory( ITurtleAccess turtle ) + public void unloadInventory( ITurtleAccess turtle ) { - // Get the item we placed with - ItemStack results = getInventory().getItem( 0 ); - getInventory().setItem( 0, ItemStack.EMPTY ); + int currentSlot = turtle.getSelectedSlot(); + int slots = turtle.getItemHandler().size(); + + // Load up the fake inventory + getInventory().selected = 0; + for( int i = 0; i < slots; i++ ) + { + turtle.getItemHandler().setStack( (currentSlot + i) % slots, getInventory().getItem( i ) ); + } // Store (or drop) anything else we found BlockPos dropPosition = turtle.getPosition(); - Direction dropDirection = turtle.getDirection() - .getOpposite(); - for( int i = 0; i < getInventory().getContainerSize(); i++ ) + Direction dropDirection = turtle.getDirection().getOpposite(); + int totalSize = getInventory().getContainerSize(); + for( int i = slots; i < totalSize; i++ ) { - ItemStack stack = getInventory().getItem( i ); - if( !stack.isEmpty() ) + ItemStack remainder = InventoryUtil.storeItems( getInventory().getItem( i ), turtle.getItemHandler(), turtle.getSelectedSlot() ); + if( !remainder.isEmpty() ) { - ItemStack remainder = InventoryUtil.storeItems( stack, turtle.getItemHandler(), turtle.getSelectedSlot() ); - if( !remainder.isEmpty() ) - { - WorldUtil.dropItemStack( remainder, turtle.getLevel(), dropPosition, dropDirection ); - } - getInventory().setItem( i, ItemStack.EMPTY ); + WorldUtil.dropItemStack( remainder, turtle.getLevel(), dropPosition, dropDirection ); } } + getInventory().setChanged(); - return results; } @Nonnull @Override public EntityType getType() { - return ComputerCraftRegistry.ModEntities.TURTLE_PLAYER; + return Registry.ModEntities.TURTLE_PLAYER; } @Override - public float getEyeHeight( @Nonnull Pose pose ) + public Vec3 position() { - return 0; + return new Vec3( getX(), getY(), getZ() ); } @Override - public Vec3 position() + public float getEyeHeight( @Nonnull Pose pose ) { - return new Vec3( getX(), getY(), getZ() ); + return 0; } @Override @@ -167,6 +224,14 @@ public float getStandingEyeHeight( @Nonnull Pose pose, @Nonnull EntityDimensions return 0; } + //region Code which depends on the connection + @Nonnull + @Override + public OptionalInt openMenu( @Nullable MenuProvider prover ) + { + return OptionalInt.empty(); + } + @Override public void onEnterCombat() { @@ -193,16 +258,13 @@ public void openTextEdit( @Nonnull SignBlockEntity signTile ) { } - //region Code which depends on the connection - @Nonnull @Override - public OptionalInt openMenu( @Nullable MenuProvider prover ) + public void openHorseInventory( @Nonnull AbstractHorse horse, @Nonnull Container inventory ) { - return OptionalInt.empty(); } @Override - public void openHorseInventory( @Nonnull AbstractHorse horse, @Nonnull Container inventory ) + public void openItemGui( @Nonnull ItemStack stack, @Nonnull InteractionHand hand ) { } 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 54138dc99..79917803e 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleSuckCommand.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleSuckCommand.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.turtle.core; import dan200.computercraft.api.turtle.ITurtleAccess; @@ -14,7 +13,6 @@ import dan200.computercraft.shared.util.ItemStorage; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; -import net.minecraft.world.Container; import net.minecraft.world.entity.EntitySelector; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.item.ItemStack; @@ -55,23 +53,20 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle ) BlockPos blockPosition = turtlePosition.relative( direction ); Direction side = direction.getOpposite(); - Container inventory = InventoryUtil.getInventory( world, blockPosition, side ); + ItemStorage inventory = ItemStorage.wrap( InventoryUtil.getInventory( world, blockPosition, side ) ); if( inventory != null ) { // Take from inventory of thing in front - ItemStack stack = InventoryUtil.takeItems( quantity, ItemStorage.wrap( inventory ) ); - if( stack.isEmpty() ) - { - return TurtleCommandResult.failure( "No items to take" ); - } + ItemStack stack = InventoryUtil.takeItems( quantity, inventory ); + if( stack.isEmpty() ) return TurtleCommandResult.failure( "No items to take" ); // Try to place into the turtle ItemStack remainder = InventoryUtil.storeItems( stack, turtle.getItemHandler(), turtle.getSelectedSlot() ); if( !remainder.isEmpty() ) { // Put the remainder back in the inventory - InventoryUtil.storeItems( remainder, ItemStorage.wrap( inventory ) ); + InventoryUtil.storeItems( remainder, inventory ); } // Return true if we consumed anything @@ -88,23 +83,17 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle ) else { // Suck up loose items off the ground - AABB aabb = new AABB( blockPosition.getX(), - blockPosition.getY(), - blockPosition.getZ(), - blockPosition.getX() + 1.0, - blockPosition.getY() + 1.0, - blockPosition.getZ() + 1.0 ); + AABB aabb = new AABB( + blockPosition.getX(), blockPosition.getY(), blockPosition.getZ(), + blockPosition.getX() + 1.0, blockPosition.getY() + 1.0, blockPosition.getZ() + 1.0 + ); List list = world.getEntitiesOfClass( ItemEntity.class, aabb, EntitySelector.ENTITY_STILL_ALIVE ); - if( list.isEmpty() ) - { - return TurtleCommandResult.failure( "No items to take" ); - } + if( list.isEmpty() ) return TurtleCommandResult.failure( "No items to take" ); for( ItemEntity entity : list ) { // Suck up the item - ItemStack stack = entity.getItem() - .copy(); + ItemStack stack = entity.getItem().copy(); ItemStack storeStack; ItemStack leaveStack; diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleToolCommand.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleToolCommand.java index 77c679098..e0f67bae9 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleToolCommand.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleToolCommand.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.turtle.core; import dan200.computercraft.api.turtle.*; @@ -25,16 +24,6 @@ public TurtleToolCommand( TurtleVerb verb, InteractDirection direction, TurtleSi this.side = side; } - public static TurtleToolCommand attack( InteractDirection direction, @Nullable TurtleSide side ) - { - return new TurtleToolCommand( TurtleVerb.ATTACK, direction, side ); - } - - public static TurtleToolCommand dig( InteractDirection direction, @Nullable TurtleSide side ) - { - return new TurtleToolCommand( TurtleVerb.DIG, direction, side ); - } - @Nonnull @Override public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle ) @@ -42,17 +31,10 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle ) TurtleCommandResult firstFailure = null; for( TurtleSide side : TurtleSide.values() ) { - if( this.side != null && this.side != side ) - { - continue; - } + if( this.side != null && this.side != side ) continue; ITurtleUpgrade upgrade = turtle.getUpgrade( side ); - if( upgrade == null || !upgrade.getType() - .isTool() ) - { - continue; - } + if( upgrade == null || !upgrade.getType().isTool() ) continue; TurtleCommandResult result = upgrade.useTool( turtle, side, verb, direction.toWorldDir( turtle ) ); if( result.isSuccess() ) @@ -76,7 +58,17 @@ else if( firstFailure == null ) firstFailure = result; } } - return firstFailure != null ? firstFailure : TurtleCommandResult.failure( "No tool to " + verb.name() - .toLowerCase( Locale.ROOT ) + " with" ); + return firstFailure != null ? firstFailure + : TurtleCommandResult.failure( "No tool to " + verb.name().toLowerCase( Locale.ROOT ) + " with" ); + } + + public static TurtleToolCommand attack( InteractDirection direction, @Nullable TurtleSide side ) + { + return new TurtleToolCommand( TurtleVerb.ATTACK, direction, side ); + } + + public static TurtleToolCommand dig( InteractDirection direction, @Nullable TurtleSide side ) + { + return new TurtleToolCommand( TurtleVerb.DIG, direction, side ); } } diff --git a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleTurnCommand.java b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleTurnCommand.java index 78c27ecf3..4db159fd2 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/core/TurtleTurnCommand.java +++ b/src/main/java/dan200/computercraft/shared/turtle/core/TurtleTurnCommand.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.turtle.core; import dan200.computercraft.api.turtle.ITurtleAccess; @@ -29,17 +28,21 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle ) switch( direction ) { case LEFT: - turtle.setDirection( turtle.getDirection() - .getCounterClockWise() ); + { + turtle.setDirection( turtle.getDirection().getCounterClockWise() ); turtle.playAnimation( TurtleAnimation.TURN_LEFT ); return TurtleCommandResult.success(); + } case RIGHT: - turtle.setDirection( turtle.getDirection() - .getClockWise() ); + { + turtle.setDirection( turtle.getDirection().getClockWise() ); turtle.playAnimation( TurtleAnimation.TURN_RIGHT ); return TurtleCommandResult.success(); + } default: + { return TurtleCommandResult.failure( "Unknown direction" ); + } } } } 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 144ceb4c8..f175cb997 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/inventory/ContainerTurtle.java +++ b/src/main/java/dan200/computercraft/shared/turtle/inventory/ContainerTurtle.java @@ -3,11 +3,10 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.turtle.inventory; import dan200.computercraft.client.gui.widgets.ComputerSidebar; -import dan200.computercraft.shared.ComputerCraftRegistry; +import dan200.computercraft.shared.Registry; import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.computer.core.IComputer; import dan200.computercraft.shared.computer.inventory.ContainerComputerBase; @@ -15,7 +14,6 @@ import dan200.computercraft.shared.turtle.blocks.TileTurtle; import dan200.computercraft.shared.turtle.core.TurtleBrain; import dan200.computercraft.shared.util.SingleIntArray; -import net.minecraft.network.FriendlyByteBuf; import net.minecraft.world.Container; import net.minecraft.world.SimpleContainer; import net.minecraft.world.entity.player.Inventory; @@ -37,23 +35,12 @@ public class ContainerTurtle extends ContainerComputerBase private final ContainerData properties; - public ContainerTurtle( int id, Inventory player, TurtleBrain turtle ) - { - this( id, - p -> turtle.getOwner() - .stillValid( p ), - turtle.getOwner() - .createServerComputer(), - turtle.getFamily(), - player, - turtle.getInventory(), - (SingleIntArray) turtle::getSelectedSlot ); - } - - private ContainerTurtle( int id, Predicate canUse, IComputer computer, ComputerFamily family, Inventory playerInventory, - Container inventory, ContainerData properties ) + private ContainerTurtle( + int id, Predicate canUse, IComputer computer, ComputerFamily family, + Inventory playerInventory, Container inventory, ContainerData properties + ) { - super( ComputerCraftRegistry.ModContainers.TURTLE, id, canUse, computer, family ); + super( Registry.ModContainers.TURTLE, id, canUse, computer, family ); this.properties = properties; addDataSlots( properties ); @@ -83,20 +70,20 @@ private ContainerTurtle( int id, Predicate canUse, IComputer computer, C } } - public ContainerTurtle( int id, Inventory player, FriendlyByteBuf packetByteBuf ) + public ContainerTurtle( int id, Inventory player, TurtleBrain turtle ) { - this( id, player, new ComputerContainerData( packetByteBuf ) ); + this( + id, p -> turtle.getOwner().stillValid( p ), turtle.getOwner().createServerComputer(), turtle.getFamily(), + player, turtle.getInventory(), (SingleIntArray) turtle::getSelectedSlot + ); } public ContainerTurtle( int id, Inventory player, ComputerContainerData data ) { - this( id, - x -> true, - getComputer( player, data ), - data.getFamily(), - player, - new SimpleContainer( TileTurtle.INVENTORY_SIZE ), - new SimpleContainerData( 1 ) ); + this( + id, x -> true, getComputer( player, data ), data.getFamily(), + player, new SimpleContainer( TileTurtle.INVENTORY_SIZE ), new SimpleContainerData( 1 ) + ); } public int getSelectedSlot() @@ -104,21 +91,6 @@ public int getSelectedSlot() return properties.get( 0 ); } - @Nonnull - @Override - public ItemStack quickMoveStack( @Nonnull Player player, int slotNum ) - { - if( slotNum >= 0 && slotNum < 16 ) - { - return tryItemMerge( player, slotNum, 16, 52, true ); - } - else if( slotNum >= 16 ) - { - return tryItemMerge( player, slotNum, 0, 16, false ); - } - return ItemStack.EMPTY; - } - @Nonnull private ItemStack tryItemMerge( Player player, int slotNum, int firstSlot, int lastSlot, boolean reverse ) { @@ -153,4 +125,19 @@ private ItemStack tryItemMerge( Player player, int slotNum, int firstSlot, int l } return originalStack; } + + @Nonnull + @Override + public ItemStack quickMoveStack( @Nonnull Player player, int slotNum ) + { + if( slotNum >= 0 && slotNum < 16 ) + { + return tryItemMerge( player, slotNum, 16, 52, true ); + } + else if( slotNum >= 16 ) + { + return tryItemMerge( player, slotNum, 0, 16, false ); + } + return ItemStack.EMPTY; + } } diff --git a/src/main/java/dan200/computercraft/shared/turtle/items/ItemTurtle.java b/src/main/java/dan200/computercraft/shared/turtle/items/ItemTurtle.java index fe08e07ed..faee52872 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/items/ItemTurtle.java +++ b/src/main/java/dan200/computercraft/shared/turtle/items/ItemTurtle.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.turtle.items; import dan200.computercraft.api.turtle.ITurtleUpgrade; @@ -14,13 +13,16 @@ import dan200.computercraft.shared.computer.items.ItemComputerBase; import dan200.computercraft.shared.turtle.blocks.BlockTurtle; import net.minecraft.core.NonNullList; +import net.minecraft.core.cauldron.CauldronInteraction; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.chat.Component; import net.minecraft.network.chat.TextComponent; import net.minecraft.network.chat.TranslatableComponent; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.InteractionResult; import net.minecraft.world.item.CreativeModeTab; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.LayeredCauldronBlock; import javax.annotation.Nonnull; @@ -33,67 +35,42 @@ public ItemTurtle( BlockTurtle block, Properties settings ) super( block, settings ); } - @Override - public void fillItemCategory( @Nonnull CreativeModeTab group, @Nonnull NonNullList list ) - { - if( !allowdedIn( group ) ) - { - return; - } - - ComputerFamily family = getFamily(); - - list.add( create( -1, null, -1, null, null, 0, null ) ); - TurtleUpgrades.getVanillaUpgrades() - .filter( x -> TurtleUpgrades.suitableForFamily( family, x ) ) - .map( x -> create( -1, null, -1, null, x, 0, null ) ) - .forEach( list::add ); - } - public ItemStack create( int id, String label, int colour, ITurtleUpgrade leftUpgrade, ITurtleUpgrade rightUpgrade, int fuelLevel, ResourceLocation overlay ) { // Build the stack ItemStack stack = new ItemStack( this ); - if( label != null ) - { - stack.setHoverName( new TextComponent( label ) ); - } - if( id >= 0 ) - { - stack.getOrCreateTag() - .putInt( NBT_ID, id ); - } + if( label != null ) stack.setHoverName( new TextComponent( label ) ); + if( id >= 0 ) stack.getOrCreateTag().putInt( NBT_ID, id ); IColouredItem.setColourBasic( stack, colour ); - if( fuelLevel > 0 ) - { - stack.getOrCreateTag() - .putInt( NBT_FUEL, fuelLevel ); - } - if( overlay != null ) - { - stack.getOrCreateTag() - .putString( NBT_OVERLAY, overlay.toString() ); - } + if( fuelLevel > 0 ) stack.getOrCreateTag().putInt( NBT_FUEL, fuelLevel ); + if( overlay != null ) stack.getOrCreateTag().putString( NBT_OVERLAY, overlay.toString() ); if( leftUpgrade != null ) { - stack.getOrCreateTag() - .putString( NBT_LEFT_UPGRADE, - leftUpgrade.getUpgradeID() - .toString() ); + stack.getOrCreateTag().putString( NBT_LEFT_UPGRADE, leftUpgrade.getUpgradeID().toString() ); } if( rightUpgrade != null ) { - stack.getOrCreateTag() - .putString( NBT_RIGHT_UPGRADE, - rightUpgrade.getUpgradeID() - .toString() ); + stack.getOrCreateTag().putString( NBT_RIGHT_UPGRADE, rightUpgrade.getUpgradeID().toString() ); } return stack; } + @Override + public void fillItemCategory( @Nonnull CreativeModeTab group, @Nonnull NonNullList list ) + { + if( !allowdedIn( group ) ) return; + + ComputerFamily family = getFamily(); + + list.add( create( -1, null, -1, null, null, 0, null ) ); + TurtleUpgrades.getVanillaUpgrades() + .map( x -> create( -1, null, -1, null, x, 0, null ) ) + .forEach( list::add ); + } + @Nonnull @Override public Component getName( @Nonnull ItemStack stack ) @@ -105,15 +82,20 @@ public Component getName( @Nonnull ItemStack stack ) { return new TranslatableComponent( baseString + ".upgraded_twice", new TranslatableComponent( right.getUnlocalisedAdjective() ), - new TranslatableComponent( left.getUnlocalisedAdjective() ) ); + new TranslatableComponent( left.getUnlocalisedAdjective() ) + ); } else if( left != null ) { - return new TranslatableComponent( baseString + ".upgraded", new TranslatableComponent( left.getUnlocalisedAdjective() ) ); + return new TranslatableComponent( baseString + ".upgraded", + new TranslatableComponent( left.getUnlocalisedAdjective() ) + ); } else if( right != null ) { - return new TranslatableComponent( baseString + ".upgraded", new TranslatableComponent( right.getUnlocalisedAdjective() ) ); + return new TranslatableComponent( baseString + ".upgraded", + new TranslatableComponent( right.getUnlocalisedAdjective() ) + ); } else { @@ -145,38 +127,50 @@ else if( right != null ) // return super.getCreatorModId( stack ); // } + @Override + public ItemStack withFamily( @Nonnull ItemStack stack, @Nonnull ComputerFamily family ) + { + return TurtleItemFactory.create( + getComputerID( stack ), getLabel( stack ), + getColour( stack ), family, + getUpgrade( stack, TurtleSide.LEFT ), getUpgrade( stack, TurtleSide.RIGHT ), + getFuelLevel( stack ), getOverlay( stack ) + ); + } + @Override public ITurtleUpgrade getUpgrade( @Nonnull ItemStack stack, @Nonnull TurtleSide side ) { CompoundTag tag = stack.getTag(); - if( tag == null ) - { - return null; - } + if( tag == null ) return null; String key = side == TurtleSide.LEFT ? NBT_LEFT_UPGRADE : NBT_RIGHT_UPGRADE; return tag.contains( key ) ? TurtleUpgrades.get( tag.getString( key ) ) : null; } @Override - public int getFuelLevel( @Nonnull ItemStack stack ) + public ResourceLocation getOverlay( @Nonnull ItemStack stack ) { CompoundTag tag = stack.getTag(); - return tag != null && tag.contains( NBT_FUEL ) ? tag.getInt( NBT_FUEL ) : 0; + return tag != null && tag.contains( NBT_OVERLAY ) ? new ResourceLocation( tag.getString( NBT_OVERLAY ) ) : null; } @Override - public ResourceLocation getOverlay( @Nonnull ItemStack stack ) + public int getFuelLevel( @Nonnull ItemStack stack ) { CompoundTag tag = stack.getTag(); - return tag != null && tag.contains( NBT_OVERLAY ) ? new ResourceLocation( tag.getString( NBT_OVERLAY ) ) : null; + return tag != null && tag.contains( NBT_FUEL ) ? tag.getInt( NBT_FUEL ) : 0; } - @Override - public ItemStack withFamily( @Nonnull ItemStack stack, @Nonnull ComputerFamily family ) + public static final CauldronInteraction CAULDRON_INTERACTION = ( blockState, level, pos, player, hand, stack ) -> { - return TurtleItemFactory.create( getComputerID( stack ), getLabel( stack ), getColour( stack ), - family, getUpgrade( stack, TurtleSide.LEFT ), - getUpgrade( stack, TurtleSide.RIGHT ), getFuelLevel( stack ), getOverlay( stack ) ); - } + if( IColouredItem.getColourBasic( stack ) == -1 ) return InteractionResult.PASS; + if( !level.isClientSide ) + { + IColouredItem.setColourBasic( stack, -1 ); + LayeredCauldronBlock.lowerFillLevel( blockState, level, pos ); + } + + return InteractionResult.sidedSuccess( level.isClientSide ); + }; } diff --git a/src/main/java/dan200/computercraft/shared/turtle/items/TurtleItemFactory.java b/src/main/java/dan200/computercraft/shared/turtle/items/TurtleItemFactory.java index f829b2dde..7e08bb963 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/items/TurtleItemFactory.java +++ b/src/main/java/dan200/computercraft/shared/turtle/items/TurtleItemFactory.java @@ -3,13 +3,12 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.turtle.items; import dan200.computercraft.api.turtle.ITurtleAccess; import dan200.computercraft.api.turtle.ITurtleUpgrade; import dan200.computercraft.api.turtle.TurtleSide; -import dan200.computercraft.shared.ComputerCraftRegistry; +import dan200.computercraft.shared.Registry; import dan200.computercraft.shared.computer.core.ComputerFamily; import dan200.computercraft.shared.turtle.blocks.ITurtleTile; import net.minecraft.resources.ResourceLocation; @@ -26,26 +25,22 @@ public static ItemStack create( ITurtleTile turtle ) { ITurtleAccess access = turtle.getAccess(); - return create( turtle.getComputerID(), - turtle.getLabel(), - turtle.getColour(), - turtle.getFamily(), - access.getUpgrade( TurtleSide.LEFT ), - access.getUpgrade( TurtleSide.RIGHT ), - access.getFuelLevel(), - turtle.getOverlay() ); + return create( + turtle.getComputerID(), turtle.getLabel(), turtle.getColour(), turtle.getFamily(), + access.getUpgrade( TurtleSide.LEFT ), access.getUpgrade( TurtleSide.RIGHT ), + access.getFuelLevel(), turtle.getOverlay() + ); } @Nonnull - public static ItemStack create( int id, String label, int colour, ComputerFamily family, ITurtleUpgrade leftUpgrade, ITurtleUpgrade rightUpgrade, - int fuelLevel, ResourceLocation overlay ) + public static ItemStack create( int id, String label, int colour, ComputerFamily family, ITurtleUpgrade leftUpgrade, ITurtleUpgrade rightUpgrade, int fuelLevel, ResourceLocation overlay ) { switch( family ) { case NORMAL: - return ComputerCraftRegistry.ModItems.TURTLE_NORMAL.create( id, label, colour, leftUpgrade, rightUpgrade, fuelLevel, overlay ); + return Registry.ModItems.TURTLE_NORMAL.create( id, label, colour, leftUpgrade, rightUpgrade, fuelLevel, overlay ); case ADVANCED: - return ComputerCraftRegistry.ModItems.TURTLE_ADVANCED.create( id, label, colour, leftUpgrade, rightUpgrade, fuelLevel, overlay ); + return Registry.ModItems.TURTLE_ADVANCED.create( id, label, colour, leftUpgrade, rightUpgrade, fuelLevel, overlay ); default: return ItemStack.EMPTY; } diff --git a/src/main/java/dan200/computercraft/shared/turtle/recipes/TurtleRecipe.java b/src/main/java/dan200/computercraft/shared/turtle/recipes/TurtleRecipe.java index 7a3c94d61..7e6f8db1d 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/recipes/TurtleRecipe.java +++ b/src/main/java/dan200/computercraft/shared/turtle/recipes/TurtleRecipe.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.turtle.recipes; import dan200.computercraft.shared.computer.core.ComputerFamily; @@ -20,18 +19,7 @@ public final class TurtleRecipe extends ComputerFamilyRecipe { - public static final RecipeSerializer SERIALIZER = - new ComputerFamilyRecipe.Serializer() - { - @Override - protected TurtleRecipe create( ResourceLocation identifier, String group, int width, int height, NonNullList ingredients, ItemStack result, ComputerFamily family ) - { - return new TurtleRecipe( identifier, group, width, height, ingredients, result, family ); - } - }; - - private TurtleRecipe( ResourceLocation identifier, String group, int width, int height, NonNullList ingredients, ItemStack result, - ComputerFamily family ) + private TurtleRecipe( ResourceLocation identifier, String group, int width, int height, NonNullList ingredients, ItemStack result, ComputerFamily family ) { super( identifier, group, width, height, ingredients, result, family ); } @@ -52,4 +40,13 @@ protected ItemStack convert( @Nonnull IComputerItem item, @Nonnull ItemStack sta return TurtleItemFactory.create( computerID, label, -1, getFamily(), null, null, 0, null ); } + + public static final RecipeSerializer SERIALIZER = new Serializer<>() + { + @Override + protected TurtleRecipe create( ResourceLocation identifier, String group, int width, int height, NonNullList ingredients, ItemStack result, ComputerFamily family ) + { + return new TurtleRecipe( identifier, group, width, height, ingredients, result, family ); + } + }; } diff --git a/src/main/java/dan200/computercraft/shared/turtle/recipes/TurtleUpgradeRecipe.java b/src/main/java/dan200/computercraft/shared/turtle/recipes/TurtleUpgradeRecipe.java index 53d73c2f6..855731bbe 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/recipes/TurtleUpgradeRecipe.java +++ b/src/main/java/dan200/computercraft/shared/turtle/recipes/TurtleUpgradeRecipe.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.turtle.recipes; import dan200.computercraft.api.turtle.ITurtleUpgrade; @@ -24,13 +23,17 @@ public final class TurtleUpgradeRecipe extends CustomRecipe { - public static final RecipeSerializer SERIALIZER = new SimpleRecipeSerializer<>( TurtleUpgradeRecipe::new ); - private TurtleUpgradeRecipe( ResourceLocation id ) { super( id ); } + @Override + public boolean canCraftInDimensions( int x, int y ) + { + return x >= 3 && y >= 1; + } + @Nonnull @Override public ItemStack getResultItem() @@ -150,18 +153,7 @@ else if( !turtle.isEmpty() && rightItem.isEmpty() ) if( !items[i].isEmpty() ) { ITurtleUpgrade itemUpgrade = TurtleUpgrades.get( items[i] ); - if( itemUpgrade == null ) - { - return ItemStack.EMPTY; - } - if( upgrades[i] != null ) - { - return ItemStack.EMPTY; - } - if( !TurtleUpgrades.suitableForFamily( family, itemUpgrade ) ) - { - return ItemStack.EMPTY; - } + if( itemUpgrade == null || upgrades[i] != null ) return ItemStack.EMPTY; upgrades[i] = itemUpgrade; } } @@ -175,16 +167,12 @@ else if( !turtle.isEmpty() && rightItem.isEmpty() ) return TurtleItemFactory.create( computerID, label, colour, family, upgrades[0], upgrades[1], fuelLevel, overlay ); } - @Override - public boolean canCraftInDimensions( int x, int y ) - { - return x >= 3 && y >= 1; - } - @Nonnull @Override public RecipeSerializer getSerializer() { return SERIALIZER; } + + public static final SimpleRecipeSerializer SERIALIZER = new SimpleRecipeSerializer<>( TurtleUpgradeRecipe::new ); } diff --git a/src/main/java/dan200/computercraft/shared/turtle/upgrades/CraftingTablePeripheral.java b/src/main/java/dan200/computercraft/shared/turtle/upgrades/CraftingTablePeripheral.java index a01195ab2..f972e0588 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/upgrades/CraftingTablePeripheral.java +++ b/src/main/java/dan200/computercraft/shared/turtle/upgrades/CraftingTablePeripheral.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.turtle.upgrades; import dan200.computercraft.api.lua.LuaException; @@ -39,11 +38,12 @@ public String getType() return "workbench"; } - @Nonnull - @Override - public Object getTarget() + @LuaFunction + public final MethodResult craft( Optional count ) throws LuaException { - return turtle; + int limit = count.orElse( 64 ); + if( limit < 0 || limit > 64 ) throw new LuaException( "Crafting count " + limit + " out of range" ); + return turtle.executeCommand( new TurtleCraftCommand( limit ) ); } @Override @@ -52,14 +52,10 @@ public boolean equals( IPeripheral other ) return other instanceof CraftingTablePeripheral; } - @LuaFunction - public final MethodResult craft( Optional count ) throws LuaException + @Nonnull + @Override + public Object getTarget() { - int limit = count.orElse( 64 ); - if( limit < 0 || limit > 64 ) - { - throw new LuaException( "Crafting count " + limit + " out of range" ); - } - return turtle.executeCommand( new TurtleCraftCommand( limit ) ); + return turtle; } } diff --git a/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleAxe.java b/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleAxe.java index 744c04a8c..979c4cdd2 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleAxe.java +++ b/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleAxe.java @@ -7,28 +7,11 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; public class TurtleAxe extends TurtleTool { - public TurtleAxe( ResourceLocation id, String adjective, Item item ) + public TurtleAxe( ResourceLocation id, Item item, float damageMulitiplier ) { - super( id, adjective, item ); - } - - public TurtleAxe( ResourceLocation id, Item item ) - { - super( id, item ); - } - - public TurtleAxe( ResourceLocation id, ItemStack craftItem, ItemStack toolItem ) - { - super( id, craftItem, toolItem ); - } - - @Override - protected float getDamageMultiplier() - { - return 6.0f; + super( id, item, damageMulitiplier ); } } diff --git a/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleCraftingTable.java b/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleCraftingTable.java index f80b95859..44d8d2cd2 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleCraftingTable.java +++ b/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleCraftingTable.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.turtle.upgrades; import dan200.computercraft.api.client.TransformedModel; @@ -16,21 +15,18 @@ import net.fabricmc.api.Environment; import net.minecraft.client.resources.model.ModelResourceLocation; import net.minecraft.resources.ResourceLocation; -import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.item.ItemStack; import javax.annotation.Nonnull; public class TurtleCraftingTable extends AbstractTurtleUpgrade { - @Environment( EnvType.CLIENT ) - private ModelResourceLocation leftModel; - - @Environment( EnvType.CLIENT ) - private ModelResourceLocation rightModel; + private static final ModelResourceLocation leftModel = new ModelResourceLocation( "computercraft:turtle_crafting_table_left", "inventory" ); + private static final ModelResourceLocation rightModel = new ModelResourceLocation( "computercraft:turtle_crafting_table_right", "inventory" ); - public TurtleCraftingTable( ResourceLocation id ) + public TurtleCraftingTable( ResourceLocation id, ItemStack stack ) { - super( id, TurtleUpgradeType.PERIPHERAL, Blocks.CRAFTING_TABLE ); + super( id, TurtleUpgradeType.PERIPHERAL, "upgrade.minecraft.crafting_table.adjective", stack ); } @Override @@ -44,17 +40,6 @@ public IPeripheral createPeripheral( @Nonnull ITurtleAccess turtle, @Nonnull Tur @Environment( EnvType.CLIENT ) public TransformedModel getModel( ITurtleAccess turtle, @Nonnull TurtleSide side ) { - loadModelLocations(); return TransformedModel.of( side == TurtleSide.LEFT ? leftModel : rightModel ); } - - @Environment( EnvType.CLIENT ) - private void loadModelLocations() - { - if( leftModel == null ) - { - leftModel = new ModelResourceLocation( "computercraft:turtle_crafting_table_left", "inventory" ); - rightModel = new ModelResourceLocation( "computercraft:turtle_crafting_table_right", "inventory" ); - } - } } diff --git a/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleHoe.java b/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleHoe.java index c3098a2af..ccc5a7470 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleHoe.java +++ b/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleHoe.java @@ -6,65 +6,34 @@ package dan200.computercraft.shared.turtle.upgrades; -import dan200.computercraft.api.turtle.ITurtleAccess; import dan200.computercraft.api.turtle.TurtleCommandResult; -import dan200.computercraft.api.turtle.TurtleSide; -import dan200.computercraft.api.turtle.TurtleVerb; -import dan200.computercraft.shared.turtle.core.TurtlePlaceCommand; import dan200.computercraft.shared.turtle.core.TurtlePlayer; import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.Material; -import javax.annotation.Nonnull; - public class TurtleHoe extends TurtleTool { - public TurtleHoe( ResourceLocation id, String adjective, Item item ) - { - super( id, adjective, item ); - } - - public TurtleHoe( ResourceLocation id, Item item ) - { - super( id, item ); - } - public TurtleHoe( ResourceLocation id, ItemStack craftItem, ItemStack toolItem ) + public TurtleHoe( ResourceLocation id, Item item, float damageMulitiplier ) { - super( id, craftItem, toolItem ); - } - - @Nonnull - @Override - public TurtleCommandResult useTool( @Nonnull ITurtleAccess turtle, @Nonnull TurtleSide side, @Nonnull TurtleVerb verb, @Nonnull Direction direction ) - { - if( verb == TurtleVerb.DIG ) - { - ItemStack hoe = item.copy(); - ItemStack remainder = TurtlePlaceCommand.deploy( hoe, turtle, direction, null, null ); - if( remainder != hoe ) - { - return TurtleCommandResult.success(); - } - } - return super.useTool( turtle, side, verb, direction ); + super( id, item, damageMulitiplier ); } @Override - protected boolean canBreakBlock( BlockState state, Level world, BlockPos pos, TurtlePlayer player ) + protected TurtleCommandResult checkBlockBreakable( BlockState state, Level world, BlockPos pos, TurtlePlayer player ) { - if( !super.canBreakBlock( state, world, pos, player ) ) + if( super.checkBlockBreakable( state, world, pos, player ) == TurtleCommandResult.failure() ) { - return false; + return TurtleCommandResult.failure(); } Material material = state.getMaterial(); - return material == Material.PLANT || material == Material.CACTUS || material == Material.VEGETABLE || material == Material.LEAVES || material == Material.WATER_PLANT || material == Material.REPLACEABLE_PLANT; + if( material == Material.PLANT || material == Material.CACTUS || material == Material.VEGETABLE + || material == Material.LEAVES || material == Material.WATER_PLANT || material == Material.REPLACEABLE_PLANT ) return TurtleCommandResult.success(); + return TurtleCommandResult.failure(); } } diff --git a/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleModem.java b/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleModem.java index 74edc1935..ad12ef00e 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleModem.java +++ b/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleModem.java @@ -3,13 +3,11 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.turtle.upgrades; import dan200.computercraft.api.client.TransformedModel; import dan200.computercraft.api.peripheral.IPeripheral; import dan200.computercraft.api.turtle.*; -import dan200.computercraft.shared.ComputerCraftRegistry; import dan200.computercraft.shared.peripheral.modem.ModemState; import dan200.computercraft.shared.peripheral.modem.wireless.WirelessModemPeripheral; import net.fabricmc.api.EnvType; @@ -19,6 +17,7 @@ import net.minecraft.core.Direction; import net.minecraft.nbt.CompoundTag; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; @@ -26,22 +25,68 @@ public class TurtleModem extends AbstractTurtleUpgrade { + private static class Peripheral extends WirelessModemPeripheral + { + private final ITurtleAccess turtle; + + Peripheral( ITurtleAccess turtle, boolean advanced ) + { + super( new ModemState(), advanced ); + this.turtle = turtle; + } + + @Nonnull + @Override + public Level getLevel() + { + return turtle.getLevel(); + } + + @Nonnull + @Override + public Vec3 getPosition() + { + BlockPos turtlePos = turtle.getPosition(); + return new Vec3( + turtlePos.getX(), + turtlePos.getY(), + turtlePos.getZ() + ); + } + + @Override + public boolean equals( IPeripheral other ) + { + return this == other || (other instanceof Peripheral modem && modem.turtle == turtle); + } + } + private final boolean advanced; - @Environment( EnvType.CLIENT ) - private ModelResourceLocation leftOffModel; - @Environment( EnvType.CLIENT ) - private ModelResourceLocation rightOffModel; - @Environment( EnvType.CLIENT ) - private ModelResourceLocation leftOnModel; - @Environment( EnvType.CLIENT ) - private ModelResourceLocation rightOnModel; - public TurtleModem( boolean advanced, ResourceLocation id ) + private final ModelResourceLocation leftOffModel; + private final ModelResourceLocation rightOffModel; + private final ModelResourceLocation leftOnModel; + private final ModelResourceLocation rightOnModel; + + public TurtleModem( ResourceLocation id, ItemStack stack, boolean advanced ) { - super( id, - TurtleUpgradeType.PERIPHERAL, - advanced ? ComputerCraftRegistry.ModBlocks.WIRELESS_MODEM_ADVANCED : ComputerCraftRegistry.ModBlocks.WIRELESS_MODEM_NORMAL ); + super( id, TurtleUpgradeType.PERIPHERAL, advanced ? WirelessModemPeripheral.ADVANCED_ADJECTIVE : WirelessModemPeripheral.NORMAL_ADJECTIVE, stack ); this.advanced = advanced; + + if( advanced ) + { + leftOffModel = new ModelResourceLocation( "computercraft:turtle_modem_advanced_off_left", "inventory" ); + rightOffModel = new ModelResourceLocation( "computercraft:turtle_modem_advanced_off_right", "inventory" ); + leftOnModel = new ModelResourceLocation( "computercraft:turtle_modem_advanced_on_left", "inventory" ); + rightOnModel = new ModelResourceLocation( "computercraft:turtle_modem_advanced_on_right", "inventory" ); + } + else + { + leftOffModel = new ModelResourceLocation( "computercraft:turtle_modem_normal_off_left", "inventory" ); + rightOffModel = new ModelResourceLocation( "computercraft:turtle_modem_normal_off_right", "inventory" ); + leftOnModel = new ModelResourceLocation( "computercraft:turtle_modem_normal_on_left", "inventory" ); + rightOnModel = new ModelResourceLocation( "computercraft:turtle_modem_normal_on_right", "inventory" ); + } } @Override @@ -62,8 +107,6 @@ public TurtleCommandResult useTool( @Nonnull ITurtleAccess turtle, @Nonnull Turt @Environment( EnvType.CLIENT ) public TransformedModel getModel( ITurtleAccess turtle, @Nonnull TurtleSide side ) { - loadModelLocations(); - boolean active = false; if( turtle != null ) { @@ -71,29 +114,9 @@ public TransformedModel getModel( ITurtleAccess turtle, @Nonnull TurtleSide side active = turtleNBT.contains( "active" ) && turtleNBT.getBoolean( "active" ); } - return side == TurtleSide.LEFT ? TransformedModel.of( active ? leftOnModel : leftOffModel ) : TransformedModel.of( active ? rightOnModel : rightOffModel ); - } - - @Environment( EnvType.CLIENT ) - private void loadModelLocations() - { - if( leftOffModel == null ) - { - if( advanced ) - { - leftOffModel = new ModelResourceLocation( "computercraft:turtle_modem_advanced_off_left", "inventory" ); - rightOffModel = new ModelResourceLocation( "computercraft:turtle_modem_advanced_off_right", "inventory" ); - leftOnModel = new ModelResourceLocation( "computercraft:turtle_modem_advanced_on_left", "inventory" ); - rightOnModel = new ModelResourceLocation( "computercraft:turtle_modem_advanced_on_right", "inventory" ); - } - else - { - leftOffModel = new ModelResourceLocation( "computercraft:turtle_modem_normal_off_left", "inventory" ); - rightOffModel = new ModelResourceLocation( "computercraft:turtle_modem_normal_off_right", "inventory" ); - leftOnModel = new ModelResourceLocation( "computercraft:turtle_modem_normal_on_left", "inventory" ); - rightOnModel = new ModelResourceLocation( "computercraft:turtle_modem_normal_on_right", "inventory" ); - } - } + return side == TurtleSide.LEFT + ? TransformedModel.of( active ? leftOnModel : leftOffModel ) + : TransformedModel.of( active ? rightOnModel : rightOffModel ); } @Override @@ -103,48 +126,15 @@ public void update( @Nonnull ITurtleAccess turtle, @Nonnull TurtleSide side ) if( !turtle.getLevel().isClientSide ) { IPeripheral peripheral = turtle.getPeripheral( side ); - if( peripheral instanceof Peripheral ) + if( peripheral instanceof Peripheral modem ) { - ModemState state = ((Peripheral) peripheral).getModemState(); + ModemState state = modem.getModemState(); if( state.pollChanged() ) { - turtle.getUpgradeNBTData( side ) - .putBoolean( "active", state.isOpen() ); + turtle.getUpgradeNBTData( side ).putBoolean( "active", state.isOpen() ); turtle.updateUpgradeNBTData( side ); } } } } - - private static class Peripheral extends WirelessModemPeripheral - { - private final ITurtleAccess turtle; - - Peripheral( ITurtleAccess turtle, boolean advanced ) - { - super( new ModemState(), advanced ); - this.turtle = turtle; - } - - @Nonnull - @Override - public Level getLevel() - { - return turtle.getLevel(); - } - - @Nonnull - @Override - public Vec3 getPosition() - { - BlockPos turtlePos = turtle.getPosition(); - return new Vec3( turtlePos.getX(), turtlePos.getY(), turtlePos.getZ() ); - } - - @Override - public boolean equals( IPeripheral other ) - { - return this == other || (other instanceof Peripheral && ((Peripheral) other).turtle == turtle); - } - } } diff --git a/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleShovel.java b/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleShovel.java index a9684cedf..b19f50460 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleShovel.java +++ b/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleShovel.java @@ -6,65 +6,32 @@ package dan200.computercraft.shared.turtle.upgrades; -import dan200.computercraft.api.turtle.ITurtleAccess; import dan200.computercraft.api.turtle.TurtleCommandResult; -import dan200.computercraft.api.turtle.TurtleSide; -import dan200.computercraft.api.turtle.TurtleVerb; -import dan200.computercraft.shared.turtle.core.TurtlePlaceCommand; import dan200.computercraft.shared.turtle.core.TurtlePlayer; import net.minecraft.core.BlockPos; -import net.minecraft.core.Direction; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.Material; -import javax.annotation.Nonnull; - public class TurtleShovel extends TurtleTool { - public TurtleShovel( ResourceLocation id, String adjective, Item item ) - { - super( id, adjective, item ); - } - - public TurtleShovel( ResourceLocation id, Item item ) - { - super( id, item ); - } - public TurtleShovel( ResourceLocation id, ItemStack craftItem, ItemStack toolItem ) - { - super( id, craftItem, toolItem ); - } - - @Nonnull - @Override - public TurtleCommandResult useTool( @Nonnull ITurtleAccess turtle, @Nonnull TurtleSide side, @Nonnull TurtleVerb verb, @Nonnull Direction direction ) + public TurtleShovel( ResourceLocation id, Item item, float damageMulitiplier ) { - if( verb == TurtleVerb.DIG ) - { - ItemStack shovel = item.copy(); - ItemStack remainder = TurtlePlaceCommand.deploy( shovel, turtle, direction, null, null ); - if( remainder != shovel ) - { - return TurtleCommandResult.success(); - } - } - return super.useTool( turtle, side, verb, direction ); + super( id, item, damageMulitiplier ); } @Override - protected boolean canBreakBlock( BlockState state, Level world, BlockPos pos, TurtlePlayer player ) + protected TurtleCommandResult checkBlockBreakable( BlockState state, Level world, BlockPos pos, TurtlePlayer player ) { - if( !super.canBreakBlock( state, world, pos, player ) ) - { - return false; - } + TurtleCommandResult defaultResult = super.checkBlockBreakable( state, world, pos, player ); + if( defaultResult.isSuccess() ) return defaultResult; Material material = state.getMaterial(); - return material == Material.DIRT || material == Material.SAND || material == Material.TOP_SNOW || material == Material.CLAY || material == Material.SNOW || material == Material.PLANT || material == Material.CACTUS || material == Material.VEGETABLE || material == Material.LEAVES || material == Material.REPLACEABLE_PLANT; + return material == Material.DIRT || material == Material.SAND || material == Material.TOP_SNOW || material == Material.CLAY || material == Material.SNOW || material == Material.PLANT || material == Material.CACTUS || material == Material.VEGETABLE || material == Material.LEAVES || material == Material.REPLACEABLE_PLANT + ? TurtleCommandResult.success() + : INEFFECTIVE; } } 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 e31884bed..aea0ddf67 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleSpeaker.java +++ b/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleSpeaker.java @@ -11,13 +11,13 @@ 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.UpgradeSpeakerPeripheral; import net.fabricmc.api.EnvType; import net.fabricmc.api.Environment; import net.minecraft.client.resources.model.ModelResourceLocation; import net.minecraft.core.BlockPos; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.phys.Vec3; @@ -25,55 +25,12 @@ public class TurtleSpeaker extends AbstractTurtleUpgrade { - @Environment( EnvType.CLIENT ) - private ModelResourceLocation leftModel; - @Environment( EnvType.CLIENT ) - private ModelResourceLocation rightModel; - - public TurtleSpeaker( ResourceLocation id ) - { - super( id, TurtleUpgradeType.PERIPHERAL, ComputerCraftRegistry.ModBlocks.SPEAKER ); - } - - @Override - public IPeripheral createPeripheral( @Nonnull ITurtleAccess turtle, @Nonnull TurtleSide side ) - { - return new TurtleSpeaker.Peripheral( turtle ); - } - - @Nonnull - @Override - @Environment( EnvType.CLIENT ) - public TransformedModel getModel( ITurtleAccess turtle, @Nonnull TurtleSide side ) - { - loadModelLocations(); - return TransformedModel.of( side == TurtleSide.LEFT ? leftModel : rightModel ); - } - - @Environment( EnvType.CLIENT ) - private void loadModelLocations() - { - if( leftModel == null ) - { - leftModel = new ModelResourceLocation( "computercraft:turtle_speaker_upgrade_left", "inventory" ); - rightModel = new ModelResourceLocation( "computercraft:turtle_speaker_upgrade_right", "inventory" ); - } - } - - @Override - public void update( @Nonnull ITurtleAccess turtle, @Nonnull TurtleSide turtleSide ) - { - IPeripheral turtlePeripheral = turtle.getPeripheral( turtleSide ); - if( turtlePeripheral instanceof Peripheral ) - { - Peripheral peripheral = (Peripheral) turtlePeripheral; - peripheral.update(); - } - } + private static final ModelResourceLocation leftModel = new ModelResourceLocation( "computercraft:turtle_speaker_upgrade_left", "inventory" ); + private static final ModelResourceLocation rightModel = new ModelResourceLocation( "computercraft:turtle_speaker_upgrade_right", "inventory" ); private static class Peripheral extends UpgradeSpeakerPeripheral { - ITurtleAccess turtle; + final ITurtleAccess turtle; Peripheral( ITurtleAccess turtle ) { @@ -81,7 +38,7 @@ private static class Peripheral extends UpgradeSpeakerPeripheral } @Override - public Level getWorld() + public Level getLevel() { return turtle.getLevel(); } @@ -96,7 +53,33 @@ public Vec3 getPosition() @Override public boolean equals( IPeripheral other ) { - return this == other || (other instanceof Peripheral && turtle == ((Peripheral) other).turtle); + return this == other || (other instanceof Peripheral speaker && turtle == speaker.turtle); } } + + public TurtleSpeaker( ResourceLocation id, ItemStack item ) + { + super( id, TurtleUpgradeType.PERIPHERAL, UpgradeSpeakerPeripheral.ADJECTIVE, item ); + } + + @Override + public IPeripheral createPeripheral( @Nonnull ITurtleAccess turtle, @Nonnull TurtleSide side ) + { + return new TurtleSpeaker.Peripheral( turtle ); + } + + @Nonnull + @Override + @Environment( EnvType.CLIENT ) + public TransformedModel getModel( ITurtleAccess turtle, @Nonnull TurtleSide side ) + { + return TransformedModel.of( side == TurtleSide.LEFT ? leftModel : rightModel ); + } + + @Override + public void update( @Nonnull ITurtleAccess turtle, @Nonnull TurtleSide turtleSide ) + { + IPeripheral peripheral = turtle.getPeripheral( turtleSide ); + if( peripheral instanceof Peripheral speaker ) speaker.update(); + } } diff --git a/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleSword.java b/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleSword.java index e5fc53abc..660acb33f 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleSword.java +++ b/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleSword.java @@ -6,47 +6,31 @@ package dan200.computercraft.shared.turtle.upgrades; +import dan200.computercraft.api.turtle.TurtleCommandResult; import dan200.computercraft.shared.turtle.core.TurtlePlayer; import net.minecraft.core.BlockPos; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.Item; -import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.Material; public class TurtleSword extends TurtleTool { - public TurtleSword( ResourceLocation id, String adjective, Item item ) + public TurtleSword( ResourceLocation id, Item item, float damageMulitiplier ) { - super( id, adjective, item ); - } - - public TurtleSword( ResourceLocation id, Item item ) - { - super( id, item ); - } - - public TurtleSword( ResourceLocation id, ItemStack craftItem, ItemStack toolItem ) - { - super( id, craftItem, toolItem ); - } - - @Override - protected float getDamageMultiplier() - { - return 9.0f; + super( id, item, damageMulitiplier ); } @Override - protected boolean canBreakBlock( BlockState state, Level world, BlockPos pos, TurtlePlayer player ) + protected TurtleCommandResult checkBlockBreakable( BlockState state, Level world, BlockPos pos, TurtlePlayer player ) { - if( !super.canBreakBlock( state, world, pos, player ) ) - { - return false; - } + TurtleCommandResult defaultResult = super.checkBlockBreakable( state, world, pos, player ); + if( defaultResult.isSuccess() ) return defaultResult; Material material = state.getMaterial(); - return material == Material.PLANT || material == Material.LEAVES || material == Material.REPLACEABLE_PLANT || material == Material.WOOL || material == Material.WEB; + return material == Material.PLANT || material == Material.LEAVES || material == Material.REPLACEABLE_PLANT || material == Material.WOOL || material == Material.WEB + ? TurtleCommandResult.success() + : INEFFECTIVE; } } 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 d18ff84a5..621963129 100644 --- a/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleTool.java +++ b/src/main/java/dan200/computercraft/shared/turtle/upgrades/TurtleTool.java @@ -3,12 +3,12 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.turtle.upgrades; import com.mojang.math.Matrix4f; import com.mojang.math.Transformation; import dan200.computercraft.ComputerCraft; +import dan200.computercraft.api.ComputerCraftTags; import dan200.computercraft.api.client.TransformedModel; import dan200.computercraft.api.turtle.*; import dan200.computercraft.fabric.mixininterface.IMatrix4f; @@ -35,42 +35,44 @@ import net.minecraft.world.entity.decoration.ArmorStand; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.FluidState; import net.minecraft.world.phys.Vec3; import org.apache.commons.lang3.tuple.Pair; import javax.annotation.Nonnull; -import java.util.List; import java.util.function.Function; +import static net.minecraft.nbt.Tag.TAG_COMPOUND; +import static net.minecraft.nbt.Tag.TAG_LIST; + public class TurtleTool extends AbstractTurtleUpgrade { - protected final ItemStack item; + protected static final TurtleCommandResult UNBREAKABLE = TurtleCommandResult.failure( "Cannot break unbreakable block" ); + protected static final TurtleCommandResult INEFFECTIVE = TurtleCommandResult.failure( "Cannot break block with this tool" ); - private static final int TAG_LIST = 9; - private static final int TAG_COMPOUND = 10; - - public TurtleTool( ResourceLocation id, String adjective, Item item ) - { - super( id, TurtleUpgradeType.TOOL, adjective, item ); - this.item = new ItemStack( item ); - } + final ItemStack item; + final float damageMulitiplier; - public TurtleTool( ResourceLocation id, Item item ) + public TurtleTool( ResourceLocation id, Item item, float damageMulitiplier ) { - super( id, TurtleUpgradeType.TOOL, item ); + super( id, TurtleUpgradeType.TOOL, new ItemStack( item ) ); this.item = new ItemStack( item ); + this.damageMulitiplier = damageMulitiplier; } - public TurtleTool( ResourceLocation id, ItemStack craftItem, ItemStack toolItem ) - { - super( id, TurtleUpgradeType.TOOL, craftItem ); - item = toolItem; - } + // public TurtleTool( ResourceLocation id, String adjective, Item craftItem, ItemStack toolItem, float damageMulitiplier ) + // { + // super( id, TurtleUpgradeType.TOOL, adjective, new ItemStack( craftItem ) ); + // item = toolItem; + // this.damageMulitiplier = damageMulitiplier; + // } @Override public boolean isItemSuitable( @Nonnull ItemStack stack ) @@ -81,8 +83,21 @@ public boolean isItemSuitable( @Nonnull ItemStack stack ) // Check we've not got anything vaguely interesting on the item. We allow other mods to add their // own NBT, with the understanding such details will be lost to the mist of time. if( stack.isDamaged() || stack.isEnchanted() || stack.hasCustomHoverName() ) return false; - return !tag.contains( "AttributeModifiers", TAG_LIST ) || - tag.getList( "AttributeModifiers", TAG_COMPOUND ).isEmpty(); + if( tag.contains( "AttributeModifiers", TAG_LIST ) && + !tag.getList( "AttributeModifiers", TAG_COMPOUND ).isEmpty() ) + { + return false; + } + + return true; + } + + @Nonnull + @Override + @Environment( EnvType.CLIENT ) + public TransformedModel getModel( ITurtleAccess turtle, @Nonnull TurtleSide side ) + { + return TransformedModel.of( getCraftingItem(), side == TurtleSide.LEFT ? Transforms.leftTransform : Transforms.rightTransform ); } @Nonnull @@ -92,31 +107,35 @@ public TurtleCommandResult useTool( @Nonnull ITurtleAccess turtle, @Nonnull Turt switch( verb ) { case ATTACK: - return attack( turtle, direction, side ); + return attack( turtle, direction ); case DIG: - return dig( turtle, direction, side ); + return dig( turtle, direction ); default: return TurtleCommandResult.failure( "Unsupported action" ); } } - @Nonnull - @Override - @Environment( EnvType.CLIENT ) - public TransformedModel getModel( ITurtleAccess turtle, @Nonnull TurtleSide side ) + protected TurtleCommandResult checkBlockBreakable( BlockState state, Level world, BlockPos pos, TurtlePlayer player ) { - return TransformedModel.of( getCraftingItem(), side == TurtleSide.LEFT ? Transforms.leftTransform : Transforms.rightTransform ); + Block block = state.getBlock(); + if( state.isAir() || block == Blocks.BEDROCK + || state.getDestroyProgress( player, world, pos ) <= 0 ) + { + return UNBREAKABLE; + } + + return isTriviallyBreakable( world, pos, state ) ? TurtleCommandResult.success() : INEFFECTIVE; } - private TurtleCommandResult attack( ITurtleAccess turtle, Direction direction, TurtleSide side ) + private TurtleCommandResult attack( ITurtleAccess turtle, Direction direction ) { // Create a fake player, and orient it appropriately Level world = turtle.getLevel(); BlockPos position = turtle.getPosition(); - BlockEntity turtleBlock = turtle instanceof TurtleBrain ? ((TurtleBrain) turtle).getOwner() : world.getBlockEntity( position ); - if( turtleBlock == null ) return TurtleCommandResult.failure( "Turtle has vanished from existence." ); + BlockEntity turtleTile = turtle instanceof TurtleBrain ? ((TurtleBrain) turtle).getOwner() : world.getBlockEntity( position ); + if( turtleTile == null ) return TurtleCommandResult.failure( "Turtle has vanished from existence." ); - final TurtlePlayer turtlePlayer = TurtlePlaceCommand.createPlayer( turtle, position, direction ); + final TurtlePlayer turtlePlayer = TurtlePlayer.getWithPosition( turtle, position, direction ); // See if there is an entity present Vec3 turtlePos = turtlePlayer.position(); @@ -124,32 +143,27 @@ private TurtleCommandResult attack( ITurtleAccess turtle, Direction direction, T Pair hit = WorldUtil.rayTraceEntities( world, turtlePos, rayDir, 1.5 ); if( hit != null ) { - // Load up the turtle's inventoryf + // Load up the turtle's inventory ItemStack stackCopy = item.copy(); turtlePlayer.loadInventory( stackCopy ); Entity hitEntity = hit.getKey(); // Fire several events to ensure we have permissions. - if( AttackEntityCallback.EVENT.invoker() - .interact( turtlePlayer, - world, - InteractionHand.MAIN_HAND, - hitEntity, - null ) == InteractionResult.FAIL || !hitEntity.isAttackable() ) + if( AttackEntityCallback.EVENT.invoker().interact( turtlePlayer, world, InteractionHand.MAIN_HAND, hitEntity, null ) == InteractionResult.FAIL + || !hitEntity.isAttackable() ) { return TurtleCommandResult.failure( "Nothing to attack here" ); } // Start claiming entity drops - DropConsumer.set( hitEntity, turtleDropConsumer( turtleBlock, turtle ) ); + DropConsumer.set( hitEntity, turtleDropConsumer( turtleTile, turtle ) ); // Attack the entity boolean attacked = false; if( !hitEntity.skipAttackInteraction( turtlePlayer ) ) { - float damage = (float) turtlePlayer.getAttributeValue( Attributes.ATTACK_DAMAGE ); - damage *= getDamageMultiplier(); + float damage = (float) turtlePlayer.getAttributeValue( Attributes.ATTACK_DAMAGE ) * damageMulitiplier; if( damage > 0.0f ) { DamageSource source = DamageSource.playerAttack( turtlePlayer ); @@ -157,29 +171,23 @@ private TurtleCommandResult attack( ITurtleAccess turtle, Direction direction, T { // Special case for armor stands: attack twice to guarantee destroy hitEntity.hurt( source, damage ); - if( hitEntity.isAlive() ) - { - hitEntity.hurt( source, damage ); - } + if( hitEntity.isAlive() ) hitEntity.hurt( source, damage ); attacked = true; } else { - if( hitEntity.hurt( source, damage ) ) - { - attacked = true; - } + if( hitEntity.hurt( source, damage ) ) attacked = true; } } } // Stop claiming drops - stopConsuming( turtleBlock, turtle ); + stopConsuming( turtleTile, turtle ); // Put everything we collected into the turtles inventory, then return if( attacked ) { - turtlePlayer.unloadInventory( turtle ); + turtlePlayer.getInventory().clearContent(); return TurtleCommandResult.success(); } } @@ -187,29 +195,43 @@ private TurtleCommandResult attack( ITurtleAccess turtle, Direction direction, T return TurtleCommandResult.failure( "Nothing to attack here" ); } - private TurtleCommandResult dig( ITurtleAccess turtle, Direction direction, TurtleSide side ) + private TurtleCommandResult dig( ITurtleAccess turtle, Direction direction ) { + // TODO: HOE_TILL really, if it's ever implemented + if( item.getItem() == Items.DIAMOND_SHOVEL || item.getItem() == Items.DIAMOND_HOE ) + { + if( TurtlePlaceCommand.deployCopiedItem( item.copy(), turtle, direction, null, null ) ) + { + return TurtleCommandResult.success(); + } + } + // Get ready to dig Level world = turtle.getLevel(); BlockPos turtlePosition = turtle.getPosition(); - BlockEntity turtleBlock = turtle instanceof TurtleBrain ? ((TurtleBrain) turtle).getOwner() : world.getBlockEntity( turtlePosition ); - if( turtleBlock == null ) return TurtleCommandResult.failure( "Turtle has vanished from existence." ); - + BlockEntity turtleTile = turtle instanceof TurtleBrain ? ((TurtleBrain) turtle).getOwner() : world.getBlockEntity( turtlePosition ); + if( turtleTile == null ) return TurtleCommandResult.failure( "Turtle has vanished from existence." ); BlockPos blockPosition = turtlePosition.relative( direction ); - if( world.isEmptyBlock( blockPosition ) || WorldUtil.isLiquidBlock( world, blockPosition ) ) { return TurtleCommandResult.failure( "Nothing to dig here" ); } BlockState state = world.getBlockState( blockPosition ); + FluidState fluidState = world.getFluidState( blockPosition ); - TurtlePlayer turtlePlayer = TurtlePlaceCommand.createPlayer( turtle, turtlePosition, direction ); + TurtlePlayer turtlePlayer = TurtlePlayer.getWithPosition( turtle, turtlePosition, direction ); turtlePlayer.loadInventory( item.copy() ); if( ComputerCraft.turtlesObeyBlockProtection ) { + // Check spawn protection + if( !PlayerBlockBreakEvents.BEFORE.invoker().beforeBlockBreak( world, turtlePlayer, blockPosition, state, null ) ) + { + return TurtleCommandResult.failure( "Cannot break protected block" ); + } + if( !TurtlePermissions.isBlockEditable( world, blockPosition, turtlePlayer ) ) { return TurtleCommandResult.failure( "Cannot break protected block" ); @@ -217,18 +239,11 @@ private TurtleCommandResult dig( ITurtleAccess turtle, Direction direction, Turt } // Check if we can break the block - if( !canBreakBlock( state, world, blockPosition, turtlePlayer ) ) - { - return TurtleCommandResult.failure( "Unbreakable block detected" ); - } - - if( !PlayerBlockBreakEvents.BEFORE.invoker().beforeBlockBreak( world, turtlePlayer, blockPosition, state, null ) ) - { - return TurtleCommandResult.failure( "Break cancelled" ); - } + TurtleCommandResult breakable = checkBlockBreakable( state, world, blockPosition, turtlePlayer ); + if( !breakable.isSuccess() ) return breakable; // Consume the items the block drops - DropConsumer.set( world, blockPosition, turtleDropConsumer( turtleBlock, turtle ) ); + DropConsumer.set( world, blockPosition, turtleDropConsumer( turtleTile, turtle ) ); BlockEntity tile = world.getBlockEntity( blockPosition ); @@ -239,52 +254,28 @@ private TurtleCommandResult dig( ITurtleAccess turtle, Direction direction, Turt world.levelEvent( 2001, blockPosition, Block.getId( state ) ); // Destroy the block - state.getBlock() - .playerWillDestroy( world, blockPosition, state, turtlePlayer ); - if( world.removeBlock( blockPosition, false ) ) + state.getBlock().destroy( world, blockPosition, state ); + boolean canHarvest = turtlePlayer.hasCorrectToolForDrops( state ); + if( canHarvest ) { - state.getBlock() - .destroy( world, blockPosition, state ); - if( turtlePlayer.hasCorrectToolForDrops( state ) ) - { - state.getBlock() - .playerDestroy( world, turtlePlayer, blockPosition, state, tile, turtlePlayer.getMainHandItem() ); - } + state.getBlock().playerDestroy( world, turtlePlayer, blockPosition, state, tile, turtlePlayer.getMainHandItem() ); } - stopConsuming( turtleBlock, turtle ); + stopConsuming( turtleTile, turtle ); return TurtleCommandResult.success(); } - private static Function turtleDropConsumer( BlockEntity turtleBlock, ITurtleAccess turtle ) - { - return drop -> turtleBlock.isRemoved() ? drop : InventoryUtil.storeItems( drop, turtle.getItemHandler(), turtle.getSelectedSlot() ); - } - - protected float getDamageMultiplier() - { - return 3.0f; - } - - private static void stopConsuming( BlockEntity turtleBlock, ITurtleAccess turtle ) + private static Function turtleDropConsumer( BlockEntity tile, ITurtleAccess turtle ) { - Direction direction = turtleBlock.isRemoved() ? null : turtle.getDirection().getOpposite(); - List extra = DropConsumer.clear(); - for( ItemStack remainder : extra ) - { - WorldUtil.dropItemStack( remainder, - turtle.getLevel(), - turtle.getPosition(), - direction ); - } + return drop -> tile.isRemoved() ? drop : InventoryUtil.storeItems( drop, turtle.getItemHandler(), turtle.getSelectedSlot() ); } - protected boolean canBreakBlock( BlockState state, Level world, BlockPos pos, TurtlePlayer player ) + private static void stopConsuming( BlockEntity tile, ITurtleAccess turtle ) { - Block block = state.getBlock(); - return !state.isAir() && block != Blocks.BEDROCK && state.getDestroyProgress( player, world, pos ) > 0; + Direction direction = tile.isRemoved() ? null : turtle.getDirection().getOpposite(); + DropConsumer.clearAndDrop( turtle.getLevel(), turtle.getPosition(), direction ); } private static class Transforms @@ -304,4 +295,11 @@ private static Transformation getMatrixFor( float offset ) return new Transformation( matrix ); } } + + protected boolean isTriviallyBreakable( BlockGetter reader, BlockPos pos, BlockState state ) + { + return state.is( ComputerCraftTags.Blocks.TURTLE_ALWAYS_BREAKABLE ) + // Allow breaking any "instabreak" block. + || state.getDestroySpeed( reader, pos ) == 0; + } } diff --git a/src/main/java/dan200/computercraft/shared/util/Colour.java b/src/main/java/dan200/computercraft/shared/util/Colour.java index 73589ee0f..daba8d632 100644 --- a/src/main/java/dan200/computercraft/shared/util/Colour.java +++ b/src/main/java/dan200/computercraft/shared/util/Colour.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.util; public enum Colour @@ -26,18 +25,6 @@ public enum Colour WHITE( 0xf0f0f0 ); public static final Colour[] VALUES = values(); - private final int hex; - private final float[] rgb; - - Colour( int hex ) - { - this.hex = hex; - rgb = new float[] { - ((hex >> 16) & 0xFF) / 255.0f, - ((hex >> 8) & 0xFF) / 255.0f, - (hex & 0xFF) / 255.0f, - }; - } public static Colour fromInt( int colour ) { @@ -48,18 +35,23 @@ public static Colour fromHex( int colour ) { for( Colour entry : VALUES ) { - if( entry.getHex() == colour ) - { - return entry; - } + if( entry.getHex() == colour ) return entry; } return null; } - public int getHex() + private final int hex; + private final float[] rgb; + + Colour( int hex ) { - return hex; + this.hex = hex; + rgb = new float[] { + ((hex >> 16) & 0xFF) / 255.0f, + ((hex >> 8) & 0xFF) / 255.0f, + (hex & 0xFF) / 255.0f, + }; } public Colour getNext() @@ -72,6 +64,11 @@ public Colour getPrevious() return VALUES[(ordinal() + 15) % 16]; } + public int getHex() + { + return hex; + } + public float[] getRGB() { return rgb; diff --git a/src/main/java/dan200/computercraft/shared/util/ColourTracker.java b/src/main/java/dan200/computercraft/shared/util/ColourTracker.java index c0d1532a0..4175265e8 100644 --- a/src/main/java/dan200/computercraft/shared/util/ColourTracker.java +++ b/src/main/java/dan200/computercraft/shared/util/ColourTracker.java @@ -3,13 +3,14 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.util; import net.minecraft.world.item.DyeColor; +import net.minecraft.world.item.crafting.ArmorDyeRecipe; /** - * A reimplementation of the colour system in {@link ArmorDyeRecipe}, but bundled together as an object. + * A reimplementation of the colour system in {@link ArmorDyeRecipe}, but + * bundled together as an object. */ public class ColourTracker { @@ -19,11 +20,6 @@ public class ColourTracker private int totalB; private int count; - public void addColour( float r, float g, float b ) - { - addColour( (int) (r * 255), (int) (g * 255), (int) (b * 255) ); - } - public void addColour( int r, int g, int b ) { total += Math.max( r, Math.max( g, b ) ); @@ -33,6 +29,11 @@ public void addColour( int r, int g, int b ) count++; } + public void addColour( float r, float g, float b ) + { + addColour( (int) (r * 255), (int) (g * 255), (int) (b * 255) ); + } + public void addColour( DyeColor dye ) { Colour colour = Colour.VALUES[15 - dye.getId()]; diff --git a/src/main/java/dan200/computercraft/shared/util/ColourUtils.java b/src/main/java/dan200/computercraft/shared/util/ColourUtils.java index cb288ef30..3ef206303 100644 --- a/src/main/java/dan200/computercraft/shared/util/ColourUtils.java +++ b/src/main/java/dan200/computercraft/shared/util/ColourUtils.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.util; import net.minecraft.world.item.DyeColor; @@ -11,16 +10,15 @@ import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; -import javax.annotation.Nullable; - public final class ColourUtils { - @Nullable private ColourUtils() {} public static DyeColor getStackColour( ItemStack stack ) { + if( stack.isEmpty() ) return null; + Item item = stack.getItem(); - return item instanceof DyeItem ? ((DyeItem) item).getDyeColor() : null; + return item instanceof DyeItem di ? di.getDyeColor() : null; } } diff --git a/src/main/java/dan200/computercraft/shared/util/Config.java b/src/main/java/dan200/computercraft/shared/util/Config.java index 1f46afc71..15000129b 100644 --- a/src/main/java/dan200/computercraft/shared/util/Config.java +++ b/src/main/java/dan200/computercraft/shared/util/Config.java @@ -82,10 +82,6 @@ private Config() "autocompletion" ); serverSpec.define( "default_computer_settings", ComputerCraft.defaultComputerSettings ); - serverSpec.comment( "debug_enabled", - "Enable Lua's debug library. This is sandboxed to each computer, so is generally safe to be used by players." ); - serverSpec.define( "debug_enabled", ComputerCraft.debugEnable ); - serverSpec.comment( "log_computer_errors", "Log exceptions thrown by peripherals and other Lua objects.\n" + "This makes it easier for mod authors to debug problems, but may result in log spam should people use buggy methods." ); @@ -337,7 +333,6 @@ public static void sync() ComputerCraft.maximumFilesOpen = serverConfig.get( "maximum_open_files" ); ComputerCraft.disableLua51Features = serverConfig.get( "disable_lua51_features" ); ComputerCraft.defaultComputerSettings = serverConfig.get( "default_computer_settings" ); - ComputerCraft.debugEnable = serverConfig.get( "debug_enabled" ); ComputerCraft.logComputerErrors = serverConfig.get( "log_computer_errors" ); ComputerCraft.commandRequireCreative = serverConfig.get( "command_require_creative" ); @@ -383,8 +378,7 @@ public static void sync() if( clientConfig != null ) { ComputerCraft.monitorRenderer = clientConfig.getEnum( "monitor_renderer", MonitorRenderer.class ); - int distance = clientConfig.get( "monitor_distance" ); - ComputerCraft.monitorDistanceSq = distance * distance; + ComputerCraft.monitorDistance = clientConfig.get( "monitor_distance" ); } } diff --git a/src/main/java/dan200/computercraft/shared/util/DirectionUtil.java b/src/main/java/dan200/computercraft/shared/util/DirectionUtil.java index 5744d00e4..64c7d1e01 100644 --- a/src/main/java/dan200/computercraft/shared/util/DirectionUtil.java +++ b/src/main/java/dan200/computercraft/shared/util/DirectionUtil.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.util; import dan200.computercraft.core.computer.ComputerSide; @@ -11,37 +10,19 @@ public final class DirectionUtil { - public static final Direction[] FACINGS = Direction.values(); - private DirectionUtil() {} + public static final Direction[] FACINGS = Direction.values(); + public static ComputerSide toLocal( Direction front, Direction dir ) { - if( front.getAxis() == Direction.Axis.Y ) - { - front = Direction.NORTH; - } + if( front.getAxis() == Direction.Axis.Y ) front = Direction.NORTH; - if( dir == front ) - { - return ComputerSide.FRONT; - } - if( dir == front.getOpposite() ) - { - return ComputerSide.BACK; - } - if( dir == front.getCounterClockWise() ) - { - return ComputerSide.LEFT; - } - if( dir == front.getClockWise() ) - { - return ComputerSide.RIGHT; - } - if( dir == Direction.UP ) - { - return ComputerSide.TOP; - } + if( dir == front ) return ComputerSide.FRONT; + if( dir == front.getOpposite() ) return ComputerSide.BACK; + if( dir == front.getCounterClockWise() ) return ComputerSide.LEFT; + if( dir == front.getClockWise() ) return ComputerSide.RIGHT; + if( dir == Direction.UP ) return ComputerSide.TOP; return ComputerSide.BOTTOM; } diff --git a/src/main/java/dan200/computercraft/shared/util/DropConsumer.java b/src/main/java/dan200/computercraft/shared/util/DropConsumer.java index 7ceba6b3f..032309167 100644 --- a/src/main/java/dan200/computercraft/shared/util/DropConsumer.java +++ b/src/main/java/dan200/computercraft/shared/util/DropConsumer.java @@ -3,40 +3,39 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.util; import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.phys.AABB; -import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; import java.util.function.Function; public final class DropConsumer { + private DropConsumer() + { + } + private static Function dropConsumer; private static List remainingDrops; - private static WeakReference dropWorld; + private static Level dropWorld; private static BlockPos dropPos; private static AABB dropBounds; - private static WeakReference dropEntity; - - private DropConsumer() - { - } + private static Entity dropEntity; public static void set( Entity entity, Function consumer ) { dropConsumer = consumer; remainingDrops = new ArrayList<>(); - dropEntity = new WeakReference<>( entity ); - dropWorld = new WeakReference<>( entity.level ); + dropEntity = entity; + dropWorld = entity.level; dropPos = null; dropBounds = new AABB( entity.blockPosition() ).inflate( 2, 2, 2 ); } @@ -46,7 +45,7 @@ public static void set( Level world, BlockPos pos, Function( 2 ); dropEntity = null; - dropWorld = new WeakReference<>( world ); + dropWorld = world; dropBounds = new AABB( pos ).inflate( 2, 2, 2 ); } @@ -63,29 +62,23 @@ public static List clear() return remainingStacks; } - public static boolean onHarvestDrops( Level world, BlockPos pos, ItemStack stack ) + public static void clearAndDrop( Level world, BlockPos pos, Direction direction ) { - if( dropWorld != null && dropWorld.get() == world && dropPos != null && dropPos.equals( pos ) ) - { - handleDrops( stack ); - return true; - } - return false; + List remainingDrops = clear(); + for( ItemStack remaining : remainingDrops ) WorldUtil.dropItemStack( remaining, world, pos, direction ); } private static void handleDrops( ItemStack stack ) { ItemStack remaining = dropConsumer.apply( stack ); - if( !remaining.isEmpty() ) - { - remainingDrops.add( remaining ); - } + if( !remaining.isEmpty() ) remainingDrops.add( remaining ); } public static boolean onEntitySpawn( Entity entity ) { // Capture any nearby item spawns - if( dropWorld != null && dropWorld.get() == entity.getCommandSenderWorld() && entity instanceof ItemEntity && dropBounds.contains( entity.position() ) ) + if( dropWorld != null && dropWorld == entity.getCommandSenderWorld() && entity instanceof ItemEntity + && dropBounds.contains( entity.position() ) ) { handleDrops( ((ItemEntity) entity).getItem() ); return true; @@ -95,7 +88,15 @@ public static boolean onEntitySpawn( Entity entity ) public static boolean onLivingDrops( Entity entity, ItemStack stack ) { - if( dropEntity != null && entity == dropEntity.get() ) + if( dropEntity == null || entity != dropEntity ) return false; + + handleDrops( stack ); + return true; + } + + public static boolean onHarvestDrops( Level world, BlockPos pos, ItemStack stack ) + { + if( dropWorld != null && dropWorld == world && dropPos != null && dropPos.equals( pos ) ) { handleDrops( stack ); return true; diff --git a/src/main/java/dan200/computercraft/shared/util/Holiday.java b/src/main/java/dan200/computercraft/shared/util/Holiday.java index 2b1dd385b..ce32b0dec 100644 --- a/src/main/java/dan200/computercraft/shared/util/Holiday.java +++ b/src/main/java/dan200/computercraft/shared/util/Holiday.java @@ -3,10 +3,13 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.util; public enum Holiday { - NONE, VALENTINES, APRIL_FOOLS_DAY, HALLOWEEN, CHRISTMAS, + NONE, + VALENTINES, + APRIL_FOOLS_DAY, + HALLOWEEN, + CHRISTMAS, } diff --git a/src/main/java/dan200/computercraft/shared/util/HolidayUtil.java b/src/main/java/dan200/computercraft/shared/util/HolidayUtil.java index e24ce3610..05ee0143f 100644 --- a/src/main/java/dan200/computercraft/shared/util/HolidayUtil.java +++ b/src/main/java/dan200/computercraft/shared/util/HolidayUtil.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.util; import java.util.Calendar; @@ -21,22 +20,10 @@ private static Holiday getHoliday( Calendar calendar ) { int month = calendar.get( Calendar.MONTH ); int day = calendar.get( Calendar.DAY_OF_MONTH ); - if( month == Calendar.FEBRUARY && day == 14 ) - { - return Holiday.VALENTINES; - } - if( month == Calendar.APRIL && day == 1 ) - { - return Holiday.APRIL_FOOLS_DAY; - } - if( month == Calendar.OCTOBER && day == 31 ) - { - return Holiday.HALLOWEEN; - } - if( month == Calendar.DECEMBER && day >= 24 && day <= 30 ) - { - return Holiday.CHRISTMAS; - } + if( month == Calendar.FEBRUARY && day == 14 ) return Holiday.VALENTINES; + if( month == Calendar.APRIL && day == 1 ) return Holiday.APRIL_FOOLS_DAY; + if( month == Calendar.OCTOBER && day == 31 ) return Holiday.HALLOWEEN; + if( month == Calendar.DECEMBER && day >= 24 && day <= 30 ) return Holiday.CHRISTMAS; return Holiday.NONE; } } diff --git a/src/main/java/dan200/computercraft/shared/util/IDAssigner.java b/src/main/java/dan200/computercraft/shared/util/IDAssigner.java index 19a37cb91..9db127687 100644 --- a/src/main/java/dan200/computercraft/shared/util/IDAssigner.java +++ b/src/main/java/dan200/computercraft/shared/util/IDAssigner.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.util; import com.google.gson.Gson; @@ -29,17 +28,33 @@ public final class IDAssigner { private static final LevelResource FOLDER = LevelResourceAccess.create( ComputerCraft.MOD_ID ); - private static final Gson GSON = new GsonBuilder().setPrettyPrinting() - .create(); + private static final Gson GSON = new GsonBuilder().setPrettyPrinting().create(); private static final Type ID_TOKEN = new TypeToken>() { }.getType(); + + private IDAssigner() + { + } + private static Map ids; private static WeakReference server; private static Path idFile; - private IDAssigner() + public static File getDir() + { + return GameInstanceUtils.getServer().getWorldPath( FOLDER ).toFile(); + } + + private static MinecraftServer getCachedServer() { + if( server == null ) return null; + + MinecraftServer currentServer = server.get(); + if( currentServer == null ) return null; + + if( currentServer != GameInstanceUtils.getServer() ) return null; + return currentServer; } public static synchronized int getNextId( String kind ) @@ -48,32 +63,29 @@ public static synchronized int getNextId( String kind ) if( currentServer == null ) { // The server has changed, refetch our ID map - if( GameInstanceUtils.getServer() != null ) - { - server = new WeakReference<>( GameInstanceUtils.getServer() ); + server = new WeakReference<>( GameInstanceUtils.getServer() ); - File dir = getDir(); - dir.mkdirs(); + File dir = getDir(); + dir.mkdirs(); - // Load our ID file from disk - idFile = new File( dir, "ids.json" ).toPath(); - if( Files.isRegularFile( idFile ) ) + // Load our ID file from disk + idFile = new File( dir, "ids.json" ).toPath(); + if( Files.isRegularFile( idFile ) ) + { + try( Reader reader = Files.newBufferedReader( idFile, StandardCharsets.UTF_8 ) ) { - try( Reader reader = Files.newBufferedReader( idFile, StandardCharsets.UTF_8 ) ) - { - ids = GSON.fromJson( reader, ID_TOKEN ); - } - catch( Exception e ) - { - ComputerCraft.log.error( "Cannot load id file '" + idFile + "'", e ); - ids = new HashMap<>(); - } + ids = GSON.fromJson( reader, ID_TOKEN ); } - else + catch( Exception e ) { + ComputerCraft.log.error( "Cannot load id file '" + idFile + "'", e ); ids = new HashMap<>(); } } + else + { + ids = new HashMap<>(); + } } Integer existing = ids.get( kind ); @@ -92,31 +104,4 @@ public static synchronized int getNextId( String kind ) return next; } - - private static MinecraftServer getCachedServer() - { - if( server == null ) - { - return null; - } - - MinecraftServer currentServer = server.get(); - if( currentServer == null ) - { - return null; - } - - if( currentServer != GameInstanceUtils.getServer() ) - { - return null; - } - return currentServer; - } - - public static File getDir() - { - return GameInstanceUtils.getServer() - .getWorldPath( FOLDER ) - .toFile(); - } } diff --git a/src/main/java/dan200/computercraft/shared/util/ImpostorRecipe.java b/src/main/java/dan200/computercraft/shared/util/ImpostorRecipe.java index e9c39bc97..1ad4c3bd6 100644 --- a/src/main/java/dan200/computercraft/shared/util/ImpostorRecipe.java +++ b/src/main/java/dan200/computercraft/shared/util/ImpostorRecipe.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.util; import com.google.gson.JsonObject; @@ -22,6 +21,41 @@ public final class ImpostorRecipe extends ShapedRecipe { + private final String group; + + private ImpostorRecipe( @Nonnull ResourceLocation id, @Nonnull String group, int width, int height, NonNullList ingredients, @Nonnull ItemStack result ) + { + super( id, group, width, height, ingredients, result ); + this.group = group; + } + + @Nonnull + @Override + public String getGroup() + { + return group; + } + + @Override + public boolean matches( @Nonnull CraftingContainer inv, @Nonnull Level world ) + { + return false; + } + + @Nonnull + @Override + public ItemStack assemble( @Nonnull CraftingContainer inventory ) + { + return ItemStack.EMPTY; + } + + @Nonnull + @Override + public RecipeSerializer getSerializer() + { + return SERIALIZER; + } + public static final RecipeSerializer SERIALIZER = new RecipeSerializer() { @Override @@ -42,10 +76,7 @@ public ImpostorRecipe fromNetwork( @Nonnull ResourceLocation identifier, @Nonnul int height = buf.readVarInt(); String group = buf.readUtf( Short.MAX_VALUE ); NonNullList items = NonNullList.withSize( width * height, Ingredient.EMPTY ); - for( int k = 0; k < items.size(); ++k ) - { - items.set( k, Ingredient.fromNetwork( buf ) ); - } + for( int k = 0; k < items.size(); ++k ) items.set( k, Ingredient.fromNetwork( buf ) ); ItemStack result = buf.readItem(); return new ImpostorRecipe( identifier, group, width, height, items, result ); } @@ -56,46 +87,8 @@ public void toNetwork( @Nonnull FriendlyByteBuf buf, @Nonnull ImpostorRecipe rec buf.writeVarInt( recipe.getWidth() ); buf.writeVarInt( recipe.getHeight() ); buf.writeUtf( recipe.getGroup() ); - for( Ingredient ingredient : recipe.getIngredients() ) - { - ingredient.toNetwork( buf ); - } + for( Ingredient ingredient : recipe.getIngredients() ) ingredient.toNetwork( buf ); buf.writeItem( recipe.getResultItem() ); } }; - private final String group; - - private ImpostorRecipe( @Nonnull ResourceLocation id, @Nonnull String group, int width, int height, NonNullList ingredients, - @Nonnull ItemStack result ) - { - super( id, group, width, height, ingredients, result ); - this.group = group; - } - - @Nonnull - @Override - public RecipeSerializer getSerializer() - { - return SERIALIZER; - } - - @Nonnull - @Override - public String getGroup() - { - return group; - } - - @Override - public boolean matches( @Nonnull CraftingContainer inv, @Nonnull Level world ) - { - return false; - } - - @Nonnull - @Override - public ItemStack assemble( @Nonnull CraftingContainer inventory ) - { - return ItemStack.EMPTY; - } } diff --git a/src/main/java/dan200/computercraft/shared/util/ImpostorShapelessRecipe.java b/src/main/java/dan200/computercraft/shared/util/ImpostorShapelessRecipe.java index 9b35f3146..92f471238 100644 --- a/src/main/java/dan200/computercraft/shared/util/ImpostorShapelessRecipe.java +++ b/src/main/java/dan200/computercraft/shared/util/ImpostorShapelessRecipe.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.util; import com.google.gson.JsonArray; @@ -25,18 +24,51 @@ public final class ImpostorShapelessRecipe extends ShapelessRecipe { + private final String group; + + private ImpostorShapelessRecipe( @Nonnull ResourceLocation id, @Nonnull String group, @Nonnull ItemStack result, NonNullList ingredients ) + { + super( id, group, result, ingredients ); + this.group = group; + } + + @Nonnull + @Override + public String getGroup() + { + return group; + } + + @Override + public boolean matches( @Nonnull CraftingContainer inv, @Nonnull Level world ) + { + return false; + } + + @Nonnull + @Override + public ItemStack assemble( @Nonnull CraftingContainer inventory ) + { + return ItemStack.EMPTY; + } + + @Nonnull + @Override + public RecipeSerializer getSerializer() + { + return SERIALIZER; + } + public static final RecipeSerializer SERIALIZER = new RecipeSerializer() { + @Nonnull @Override public ImpostorShapelessRecipe fromJson( @Nonnull ResourceLocation id, @Nonnull JsonObject json ) { String s = GsonHelper.getAsString( json, "group", "" ); NonNullList ingredients = readIngredients( GsonHelper.getAsJsonArray( json, "ingredients" ) ); - if( ingredients.isEmpty() ) - { - throw new JsonParseException( "No ingredients for shapeless recipe" ); - } + if( ingredients.isEmpty() ) throw new JsonParseException( "No ingredients for shapeless recipe" ); if( ingredients.size() > 9 ) { throw new JsonParseException( "Too many ingredients for shapeless recipe the max is 9" ); @@ -53,10 +85,7 @@ private NonNullList readIngredients( JsonArray arrays ) for( int i = 0; i < arrays.size(); ++i ) { Ingredient ingredient = Ingredient.fromJson( arrays.get( i ) ); - if( !ingredient.isEmpty() ) - { - items.add( ingredient ); - } + if( !ingredient.isEmpty() ) items.add( ingredient ); } return items; @@ -69,10 +98,7 @@ public ImpostorShapelessRecipe fromNetwork( @Nonnull ResourceLocation id, Friend int i = buffer.readVarInt(); NonNullList items = NonNullList.withSize( i, Ingredient.EMPTY ); - for( int j = 0; j < items.size(); j++ ) - { - items.set( j, Ingredient.fromNetwork( buffer ) ); - } + for( int j = 0; j < items.size(); j++ ) items.set( j, Ingredient.fromNetwork( buffer ) ); ItemStack result = buffer.readItem(); return new ImpostorShapelessRecipe( id, s, result, items ); @@ -82,48 +108,10 @@ public ImpostorShapelessRecipe fromNetwork( @Nonnull ResourceLocation id, Friend public void toNetwork( @Nonnull FriendlyByteBuf buffer, @Nonnull ImpostorShapelessRecipe recipe ) { buffer.writeUtf( recipe.getGroup() ); - buffer.writeVarInt( recipe.getIngredients() - .size() ); + buffer.writeVarInt( recipe.getIngredients().size() ); - for( Ingredient ingredient : recipe.getIngredients() ) - { - ingredient.toNetwork( buffer ); - } + for( Ingredient ingredient : recipe.getIngredients() ) ingredient.toNetwork( buffer ); buffer.writeItem( recipe.getResultItem() ); } }; - private final String group; - - private ImpostorShapelessRecipe( @Nonnull ResourceLocation id, @Nonnull String group, @Nonnull ItemStack result, NonNullList ingredients ) - { - super( id, group, result, ingredients ); - this.group = group; - } - - @Nonnull - @Override - public RecipeSerializer getSerializer() - { - return SERIALIZER; - } - - @Nonnull - @Override - public String getGroup() - { - return group; - } - - @Override - public boolean matches( @Nonnull CraftingContainer inv, @Nonnull Level world ) - { - return false; - } - - @Nonnull - @Override - public ItemStack assemble( @Nonnull CraftingContainer inventory ) - { - return ItemStack.EMPTY; - } } diff --git a/src/main/java/dan200/computercraft/shared/util/InventoryDelegate.java b/src/main/java/dan200/computercraft/shared/util/InventoryDelegate.java index 88c548cb1..2f4aa9fd2 100644 --- a/src/main/java/dan200/computercraft/shared/util/InventoryDelegate.java +++ b/src/main/java/dan200/computercraft/shared/util/InventoryDelegate.java @@ -3,13 +3,13 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.util; import net.minecraft.world.Container; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.entity.BlockEntity; import javax.annotation.Nonnull; import java.util.Set; @@ -17,20 +17,20 @@ /** * Provides a delegate over inventories. * - * This may be used both on {@link net.minecraft.tileentity.TileEntity}s to redirect the inventory to another tile, and by other interfaces to have - * inventories which change their backing store. + * This may be used both on {@link BlockEntity}s to redirect the inventory to another tile, + * and by other interfaces to have inventories which change their backing store. */ @FunctionalInterface public interface InventoryDelegate extends Container { + Container getInventory(); + @Override default int getContainerSize() { return getInventory().getContainerSize(); } - Container getInventory(); - @Override default boolean isEmpty() { @@ -101,20 +101,20 @@ default boolean canPlaceItem( int slot, @Nonnull ItemStack stack ) } @Override - default int countItem( @Nonnull Item stack ) + default void clearContent() { - return getInventory().countItem( stack ); + getInventory().clearContent(); } @Override - default boolean hasAnyOf( @Nonnull Set set ) + default int countItem( @Nonnull Item stack ) { - return getInventory().hasAnyOf( set ); + return getInventory().countItem( stack ); } @Override - default void clearContent() + default boolean hasAnyOf( @Nonnull Set set ) { - getInventory().clearContent(); + return getInventory().hasAnyOf( set ); } } diff --git a/src/main/java/dan200/computercraft/shared/util/InventoryUtil.java b/src/main/java/dan200/computercraft/shared/util/InventoryUtil.java index c520c0646..5b39ffbdb 100644 --- a/src/main/java/dan200/computercraft/shared/util/InventoryUtil.java +++ b/src/main/java/dan200/computercraft/shared/util/InventoryUtil.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.util; import net.minecraft.core.BlockPos; @@ -28,6 +27,11 @@ public final class InventoryUtil private InventoryUtil() {} // Methods for comparing things: + public static boolean areItemsEqual( @Nonnull ItemStack a, @Nonnull ItemStack b ) + { + return a == b || ItemStack.matches( a, b ); + } + public static boolean areItemsStackable( @Nonnull ItemStack a, @Nonnull ItemStack b ) { return a == b || (a.getItem() == b.getItem() && ItemStack.tagMatches( a, b )); @@ -44,9 +48,9 @@ public static Container getInventory( Level world, BlockPos pos, Direction side // Check if block is InventoryProvider BlockState blockState = world.getBlockState( pos ); Block block = blockState.getBlock(); - if( block instanceof WorldlyContainerHolder ) + if( block instanceof WorldlyContainerHolder containerHolder ) { - return ((WorldlyContainerHolder) block).getContainer( blockState, world, pos ); + return containerHolder.getContainer( blockState, world, pos ); } // Check if block is BlockEntity w/ Inventory if( blockState.hasBlockEntity() ) @@ -62,11 +66,15 @@ public static Container getInventory( Level world, BlockPos pos, Direction side } // Look for entity with inventory - Vec3 vecStart = new Vec3( pos.getX() + 0.5 + 0.6 * side.getStepX(), + Vec3 vecStart = new Vec3( + pos.getX() + 0.5 + 0.6 * side.getStepX(), pos.getY() + 0.5 + 0.6 * side.getStepY(), - pos.getZ() + 0.5 + 0.6 * side.getStepZ() ); + pos.getZ() + 0.5 + 0.6 * side.getStepZ() + ); Direction dir = side.getOpposite(); - Vec3 vecDir = new Vec3( dir.getStepX(), dir.getStepY(), dir.getStepZ() ); + Vec3 vecDir = new Vec3( + dir.getStepX(), dir.getStepY(), dir.getStepZ() + ); Pair hit = WorldUtil.rayTraceEntities( world, vecStart, vecDir, 1.1 ); if( hit != null ) { @@ -99,67 +107,61 @@ public static Container getInventory( BlockEntity tileEntity ) return null; } + // Methods for placing into inventories: + @Nonnull public static ItemStack storeItems( @Nonnull ItemStack itemstack, ItemStorage inventory, int begin ) { return storeItems( itemstack, inventory, 0, inventory.size(), begin ); } - // Methods for placing into inventories: + @Nonnull + public static ItemStack storeItems( @Nonnull ItemStack itemstack, ItemStorage inventory ) + { + return storeItems( itemstack, inventory, 0, inventory.size(), 0 ); + } @Nonnull public static ItemStack storeItems( @Nonnull ItemStack stack, ItemStorage inventory, int start, int range, int begin ) { - if( stack.isEmpty() ) - { - return ItemStack.EMPTY; - } + if( stack.isEmpty() ) return ItemStack.EMPTY; // Inspect the slots in order and try to find empty or stackable slots ItemStack remainder = stack.copy(); for( int i = 0; i < range; i++ ) { int slot = start + (i + begin - start) % range; - if( remainder.isEmpty() ) - { - break; - } + if( remainder.isEmpty() ) break; remainder = inventory.store( slot, remainder, false ); } return areItemsEqual( stack, remainder ) ? stack : remainder; } - public static boolean areItemsEqual( @Nonnull ItemStack a, @Nonnull ItemStack b ) - { - return a == b || ItemStack.matches( a, b ); - } + // Methods for taking out of inventories @Nonnull - public static ItemStack storeItems( @Nonnull ItemStack itemstack, ItemStorage inventory ) + public static ItemStack takeItems( int count, ItemStorage inventory, int begin ) { - return storeItems( itemstack, inventory, 0, inventory.size(), 0 ); + return takeItems( count, inventory, 0, inventory.size(), begin ); } - // Methods for taking out of inventories - @Nonnull - public static ItemStack takeItems( int count, ItemStorage inventory, int begin ) + public static ItemStack takeItems( int count, ItemStorage inventory ) { - return takeItems( count, inventory, 0, inventory.size(), begin ); + return takeItems( count, inventory, 0, inventory.size(), 0 ); } @Nonnull public static ItemStack takeItems( int count, ItemStorage inventory, int start, int range, int begin ) { + // Combine multiple stacks from inventory into one if necessary ItemStack partialStack = ItemStack.EMPTY; for( int i = 0; i < range; i++ ) { int slot = start + (i + begin - start) % range; - if( count <= 0 ) - { - break; - } + // If we've extracted all items, return + if( count <= 0 ) break; // If this doesn't slot, abort. ItemStack extracted = inventory.take( slot, count, partialStack, false ); @@ -179,14 +181,9 @@ public static ItemStack takeItems( int count, ItemStorage inventory, int start, { partialStack.grow( extracted.getCount() ); } + } return partialStack; } - - @Nonnull - public static ItemStack takeItems( int count, ItemStorage inventory ) - { - return takeItems( count, inventory, 0, inventory.size(), 0 ); - } } diff --git a/src/main/java/dan200/computercraft/shared/util/IoUtil.java b/src/main/java/dan200/computercraft/shared/util/IoUtil.java index eb915596b..1e16a3ece 100644 --- a/src/main/java/dan200/computercraft/shared/util/IoUtil.java +++ b/src/main/java/dan200/computercraft/shared/util/IoUtil.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.util; import javax.annotation.Nullable; @@ -18,10 +17,7 @@ public static void closeQuietly( @Nullable Closeable closeable ) { try { - if( closeable != null ) - { - closeable.close(); - } + if( closeable != null ) closeable.close(); } catch( IOException ignored ) { diff --git a/src/main/java/dan200/computercraft/shared/util/ItemStorage.java b/src/main/java/dan200/computercraft/shared/util/ItemStorage.java index dcca803fe..c6b10fef2 100644 --- a/src/main/java/dan200/computercraft/shared/util/ItemStorage.java +++ b/src/main/java/dan200/computercraft/shared/util/ItemStorage.java @@ -42,6 +42,8 @@ static boolean areStackable( @Nonnull ItemStack a, @Nonnull ItemStack b ) @Nonnull ItemStack getStack( int slot ); + void setStack( int slot, ItemStack stack ); + @Nonnull ItemStack take( int slot, int limit, @Nonnull ItemStack filter, boolean simulate ); @@ -75,6 +77,12 @@ public ItemStack getStack( int slot ) return inventory.getItem( slot ); } + @Override + public void setStack( int slot, ItemStack stack ) + { + setAndDirty( slot, stack ); + } + @Override @Nonnull public ItemStack take( int slot, int limit, @Nonnull ItemStack filter, boolean simulate ) @@ -272,6 +280,16 @@ public ItemStack getStack( int slot ) return parent.getStack( slot - start ); } + @Override + public void setStack( int slot, ItemStack stack ) + { + if( slot < start || slot >= start + size ) + { + return; + } + parent.setStack( slot - start, stack ); + } + @Nonnull @Override public ItemStack take( int slot, int limit, @Nonnull ItemStack filter, boolean simulate ) diff --git a/src/main/java/dan200/computercraft/shared/util/LuaUtil.java b/src/main/java/dan200/computercraft/shared/util/LuaUtil.java new file mode 100644 index 000000000..79cf1a445 --- /dev/null +++ b/src/main/java/dan200/computercraft/shared/util/LuaUtil.java @@ -0,0 +1,23 @@ +/* + * 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 java.util.Collection; + +public class LuaUtil +{ + public static Object[] consArray( Object value, Collection rest ) + { + if( rest.isEmpty() ) return new Object[] { value }; + + // I'm not proud of this code. + Object[] out = new Object[rest.size() + 1]; + out[0] = value; + int i = 1; + for( Object additionalType : rest ) out[i++] = additionalType; + return out; + } +} diff --git a/src/main/java/dan200/computercraft/shared/util/NBTUtil.java b/src/main/java/dan200/computercraft/shared/util/NBTUtil.java index 80d3c67f3..9c30d5ce8 100644 --- a/src/main/java/dan200/computercraft/shared/util/NBTUtil.java +++ b/src/main/java/dan200/computercraft/shared/util/NBTUtil.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.util; import dan200.computercraft.ComputerCraft; @@ -23,44 +22,16 @@ public final class NBTUtil { - public static final int TAG_END = 0; - public static final int TAG_BYTE = 1; - public static final int TAG_SHORT = 2; - public static final int TAG_INT = 3; - public static final int TAG_LONG = 4; - public static final int TAG_FLOAT = 5; - public static final int TAG_DOUBLE = 6; - public static final int TAG_BYTE_ARRAY = 7; - public static final int TAG_STRING = 8; - public static final int TAG_LIST = 9; - public static final int TAG_COMPOUND = 10; - public static final int TAG_INT_ARRAY = 11; - public static final int TAG_LONG_ARRAY = 12; - public static final int TAG_ANY_NUMERIC = 99; - private NBTUtil() {} private static Tag toNBTTag( Object object ) { - if( object == null ) - { - return null; - } - if( object instanceof Boolean ) - { - return ByteTag.valueOf( (byte) ((boolean) (Boolean) object ? 1 : 0) ); - } - if( object instanceof Number ) - { - return DoubleTag.valueOf( ((Number) object).doubleValue() ); - } - if( object instanceof String ) + if( object == null ) return null; + if( object instanceof Boolean ) return ByteTag.valueOf( (byte) ((boolean) (Boolean) object ? 1 : 0) ); + if( object instanceof Number ) return DoubleTag.valueOf( ((Number) object).doubleValue() ); + if( object instanceof String ) return StringTag.valueOf( object.toString() ); + if( object instanceof Map m ) { - return StringTag.valueOf( object.toString() ); - } - if( object instanceof Map ) - { - Map m = (Map) object; CompoundTag nbt = new CompoundTag(); int i = 0; for( Map.Entry entry : m.entrySet() ) @@ -83,40 +54,32 @@ private static Tag toNBTTag( Object object ) public static CompoundTag encodeObjects( Object[] objects ) { - if( objects == null || objects.length <= 0 ) - { - return null; - } + if( objects == null || objects.length <= 0 ) return null; CompoundTag nbt = new CompoundTag(); nbt.putInt( "len", objects.length ); for( int i = 0; i < objects.length; i++ ) { Tag child = toNBTTag( objects[i] ); - if( child != null ) - { - nbt.put( Integer.toString( i ), child ); - } + if( child != null ) nbt.put( Integer.toString( i ), child ); } return nbt; } private static Object fromNBTTag( Tag tag ) { - if( tag == null ) - { - return null; - } + if( tag == null ) return null; switch( tag.getId() ) { - case TAG_BYTE: + case Tag.TAG_BYTE: return ((ByteTag) tag).getAsByte() > 0; - case TAG_DOUBLE: + case Tag.TAG_DOUBLE: return ((DoubleTag) tag).getAsDouble(); default: - case TAG_STRING: + case Tag.TAG_STRING: return tag.getAsString(); - case TAG_COMPOUND: + case Tag.TAG_COMPOUND: + { CompoundTag c = (CompoundTag) tag; int len = c.getInt( "len" ); Map map = new HashMap<>( len ); @@ -124,77 +87,62 @@ private static Object fromNBTTag( Tag tag ) { Object key = fromNBTTag( c.get( "k" + i ) ); Object value = fromNBTTag( c.get( "v" + i ) ); - if( key != null && value != null ) - { - map.put( key, value ); - } + if( key != null && value != null ) map.put( key, value ); } return map; + } } } public static Object toLua( Tag tag ) { - if( tag == null ) - { - return null; - } + if( tag == null ) return null; byte typeID = tag.getId(); switch( typeID ) { - case TAG_BYTE: - case TAG_SHORT: - case TAG_INT: - case TAG_LONG: + case Tag.TAG_BYTE: + case Tag.TAG_SHORT: + case Tag.TAG_INT: + case Tag.TAG_LONG: return ((NumericTag) tag).getAsLong(); - case TAG_FLOAT: - case TAG_DOUBLE: + case Tag.TAG_FLOAT: + case Tag.TAG_DOUBLE: return ((NumericTag) tag).getAsDouble(); - case TAG_STRING: // String + case Tag.TAG_STRING: // String return tag.getAsString(); - case TAG_COMPOUND: // Compound + case Tag.TAG_COMPOUND: // Compound { CompoundTag compound = (CompoundTag) tag; Map map = new HashMap<>( compound.size() ); for( String key : compound.getAllKeys() ) { Object value = toLua( compound.get( key ) ); - if( value != null ) - { - map.put( key, value ); - } + if( value != null ) map.put( key, value ); } return map; } - case TAG_LIST: + case Tag.TAG_LIST: { ListTag list = (ListTag) tag; Map map = new HashMap<>( list.size() ); - for( int i = 0; i < list.size(); i++ ) - { - map.put( i, toLua( list.get( i ) ) ); - } + for( int i = 0; i < list.size(); i++ ) map.put( i, toLua( list.get( i ) ) ); return map; } - case TAG_BYTE_ARRAY: + case Tag.TAG_BYTE_ARRAY: { byte[] array = ((ByteArrayTag) tag).getAsByteArray(); Map map = new HashMap<>( array.length ); - for( int i = 0; i < array.length; i++ ) - { - map.put( i + 1, array[i] ); - } + for( int i = 0; i < array.length; i++ ) map.put( i + 1, array[i] ); return map; } - case TAG_INT_ARRAY: + case Tag.TAG_INT_ARRAY: + { int[] array = ((IntArrayTag) tag).getAsIntArray(); Map map = new HashMap<>( array.length ); - for( int i = 0; i < array.length; i++ ) - { - map.put( i + 1, array[i] ); - } + for( int i = 0; i < array.length; i++ ) map.put( i + 1, array[i] ); return map; + } default: return null; @@ -204,10 +152,7 @@ public static Object toLua( Tag tag ) public static Object[] decodeObjects( CompoundTag tag ) { int len = tag.getInt( "len" ); - if( len <= 0 ) - { - return null; - } + if( len <= 0 ) return null; Object[] objects = new Object[len]; for( int i = 0; i < len; i++ ) @@ -224,10 +169,7 @@ public static Object[] decodeObjects( CompoundTag tag ) @Nullable public static String getNBTHash( @Nullable CompoundTag tag ) { - if( tag == null ) - { - return null; - } + if( tag == null ) return null; try { @@ -254,15 +196,15 @@ private static final class DigestOutputStream extends OutputStream } @Override - public void write( int b ) + public void write( @Nonnull byte[] b, int off, int len ) { - digest.update( (byte) b ); + digest.update( b, off, len ); } @Override - public void write( @Nonnull byte[] b, int off, int len ) + public void write( int b ) { - digest.update( b, off, len ); + digest.update( (byte) b ); } } } diff --git a/src/main/java/dan200/computercraft/shared/util/Palette.java b/src/main/java/dan200/computercraft/shared/util/Palette.java index 4f4713f30..6bdc161bf 100644 --- a/src/main/java/dan200/computercraft/shared/util/Palette.java +++ b/src/main/java/dan200/computercraft/shared/util/Palette.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.util; import net.minecraft.nbt.CompoundTag; @@ -11,29 +10,24 @@ public class Palette { - public static final Palette DEFAULT = new Palette(); private static final int PALETTE_SIZE = 16; private final double[][] colours = new double[PALETTE_SIZE][3]; + public static final Palette DEFAULT = new Palette(); + public Palette() { // Get the default palette resetColours(); } - public void resetColours() - { - for( int i = 0; i < Colour.VALUES.length; i++ ) - { - resetColour( i ); - } - } - - public void resetColour( int i ) + public void setColour( int i, double r, double g, double b ) { if( i >= 0 && i < colours.length ) { - setColour( i, Colour.VALUES[i] ); + colours[i][0] = r; + colours[i][1] = g; + colours[i][2] = b; } } @@ -42,33 +36,54 @@ public void setColour( int i, Colour colour ) setColour( i, colour.getR(), colour.getG(), colour.getB() ); } - public void setColour( int i, double r, double g, double b ) + public double[] getColour( int i ) { if( i >= 0 && i < colours.length ) { - colours[i][0] = r; - colours[i][1] = g; - colours[i][2] = b; + return colours[i]; } + return null; } - public double[] getColour( int i ) + public void resetColour( int i ) { if( i >= 0 && i < colours.length ) { - return colours[i]; + setColour( i, Colour.VALUES[i] ); } - return null; + } + + public void resetColours() + { + for( int i = 0; i < Colour.VALUES.length; i++ ) + { + resetColour( i ); + } + } + + public static int encodeRGB8( double[] rgb ) + { + int r = (int) (rgb[0] * 255) & 0xFF; + int g = (int) (rgb[1] * 255) & 0xFF; + int b = (int) (rgb[2] * 255) & 0xFF; + + return (r << 16) | (g << 8) | b; + } + + public static double[] decodeRGB8( int rgb ) + { + return new double[] { + ((rgb >> 16) & 0xFF) / 255.0f, + ((rgb >> 8) & 0xFF) / 255.0f, + (rgb & 0xFF) / 255.0f, + }; } public void write( FriendlyByteBuf buffer ) { for( double[] colour : colours ) { - for( double channel : colour ) - { - buffer.writeByte( (int) (channel * 0xFF) & 0xFF ); - } + for( double channel : colour ) buffer.writeByte( (int) (channel * 0xFF) & 0xFF ); } } @@ -76,10 +91,7 @@ public void read( FriendlyByteBuf buffer ) { for( double[] colour : colours ) { - for( int i = 0; i < colour.length; i++ ) - { - colour[i] = (buffer.readByte() & 0xFF) / 255.0; - } + for( int i = 0; i < colour.length; i++ ) colour[i] = (buffer.readByte() & 0xFF) / 255.0; } } @@ -96,40 +108,16 @@ public CompoundTag writeToNBT( CompoundTag nbt ) return nbt; } - public static int encodeRGB8( double[] rgb ) - { - int r = (int) (rgb[0] * 255) & 0xFF; - int g = (int) (rgb[1] * 255) & 0xFF; - int b = (int) (rgb[2] * 255) & 0xFF; - - return (r << 16) | (g << 8) | b; - } - public void readFromNBT( CompoundTag nbt ) { - if( !nbt.contains( "term_palette" ) ) - { - return; - } + if( !nbt.contains( "term_palette" ) ) return; int[] rgb8 = nbt.getIntArray( "term_palette" ); - if( rgb8.length != colours.length ) - { - return; - } + if( rgb8.length != colours.length ) return; for( int i = 0; i < colours.length; i++ ) { colours[i] = decodeRGB8( rgb8[i] ); } } - - public static double[] decodeRGB8( int rgb ) - { - return new double[] { - ((rgb >> 16) & 0xFF) / 255.0f, - ((rgb >> 8) & 0xFF) / 255.0f, - (rgb & 0xFF) / 255.0f, - }; - } } diff --git a/src/main/java/dan200/computercraft/shared/util/RecipeUtil.java b/src/main/java/dan200/computercraft/shared/util/RecipeUtil.java index 24babefbc..ed3280856 100644 --- a/src/main/java/dan200/computercraft/shared/util/RecipeUtil.java +++ b/src/main/java/dan200/computercraft/shared/util/RecipeUtil.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.util; import com.google.common.collect.Maps; @@ -28,14 +27,26 @@ public final class RecipeUtil private RecipeUtil() {} + public static class ShapedTemplate + { + public final int width; + public final int height; + public final NonNullList ingredients; + + public ShapedTemplate( int width, int height, NonNullList ingredients ) + { + this.width = width; + this.height = height; + this.ingredients = ingredients; + } + } + public static ShapedTemplate getTemplate( JsonObject json ) { Map ingMap = Maps.newHashMap(); - for( Map.Entry entry : GsonHelper.getAsJsonObject( json, "key" ) - .entrySet() ) + for( Map.Entry entry : GsonHelper.getAsJsonObject( json, "key" ).entrySet() ) { - if( entry.getKey() - .length() != 1 ) + if( entry.getKey().length() != 1 ) { throw new JsonSyntaxException( "Invalid key entry: '" + entry.getKey() + "' is an invalid symbol (must be 1 character only)." ); } @@ -44,8 +55,7 @@ public static ShapedTemplate getTemplate( JsonObject json ) throw new JsonSyntaxException( "Invalid key entry: ' ' is a reserved symbol." ); } - ingMap.put( entry.getKey() - .charAt( 0 ), Ingredient.fromJson( entry.getValue() ) ); + ingMap.put( entry.getKey().charAt( 0 ), Ingredient.fromJson( entry.getValue() ) ); } ingMap.put( ' ', Ingredient.EMPTY ); @@ -103,11 +113,7 @@ public static ComputerFamily getFamily( JsonObject json, String name ) String familyName = GsonHelper.getAsString( json, name ); for( ComputerFamily family : ComputerFamily.values() ) { - if( family.name() - .equalsIgnoreCase( familyName ) ) - { - return family; - } + if( family.name().equalsIgnoreCase( familyName ) ) return family; } throw new JsonSyntaxException( "Unknown computer family '" + familyName + "' for field " + name ); @@ -128,18 +134,4 @@ public static void setNbt( ItemStack itemStack, JsonObject result ) } } } - - public static class ShapedTemplate - { - public final int width; - public final int height; - public final NonNullList ingredients; - - public ShapedTemplate( int width, int height, NonNullList ingredients ) - { - this.width = width; - this.height = height; - this.ingredients = ingredients; - } - } } diff --git a/src/main/java/dan200/computercraft/shared/util/RedstoneUtil.java b/src/main/java/dan200/computercraft/shared/util/RedstoneUtil.java index 5de8bdd7f..66343458a 100644 --- a/src/main/java/dan200/computercraft/shared/util/RedstoneUtil.java +++ b/src/main/java/dan200/computercraft/shared/util/RedstoneUtil.java @@ -3,16 +3,42 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.util; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.DiodeBlock; +import net.minecraft.world.level.block.RedStoneWireBlock; import net.minecraft.world.level.block.state.BlockState; public final class RedstoneUtil { + private RedstoneUtil() + { + } + + /** + * Gets the redstone input for an adjacent block. + * + * @param world The world we exist in + * @param pos The position of the neighbour + * @param side The side we are reading from + * @return The effective redstone power + * @see DiodeBlock#getInputSignal(Level, BlockPos, BlockState) + */ + public static int getRedstoneInput( Level world, BlockPos pos, Direction side ) + { + int power = world.getSignal( pos, side ); + if( power >= 15 ) return power; + + BlockState neighbour = world.getBlockState( pos ); + return neighbour.getBlock() == Blocks.REDSTONE_WIRE + ? Math.max( power, neighbour.getValue( RedStoneWireBlock.POWER ) ) + : power; + } + public static void propagateRedstoneOutput( Level world, BlockPos pos, Direction side ) { // Propagate ordinary output. See BlockRedstoneDiode.notifyNeighbors diff --git a/src/main/java/dan200/computercraft/shared/util/SingleIntArray.java b/src/main/java/dan200/computercraft/shared/util/SingleIntArray.java index 1329f1352..b06e1bd88 100644 --- a/src/main/java/dan200/computercraft/shared/util/SingleIntArray.java +++ b/src/main/java/dan200/computercraft/shared/util/SingleIntArray.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.util; import net.minecraft.world.inventory.ContainerData; @@ -11,14 +10,14 @@ @FunctionalInterface public interface SingleIntArray extends ContainerData { + int get(); + @Override default int get( int property ) { return property == 0 ? get() : 0; } - int get(); - @Override default void set( int property, int value ) { diff --git a/src/main/java/dan200/computercraft/shared/util/StringUtil.java b/src/main/java/dan200/computercraft/shared/util/StringUtil.java index 2c8dd68b8..cecece354 100644 --- a/src/main/java/dan200/computercraft/shared/util/StringUtil.java +++ b/src/main/java/dan200/computercraft/shared/util/StringUtil.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.util; import javax.annotation.Nullable; @@ -14,10 +13,7 @@ private StringUtil() {} public static String normaliseLabel( String label ) { - if( label == null ) - { - return null; - } + if( label == null ) return null; int length = Math.min( 32, label.length() ); StringBuilder builder = new StringBuilder( length ); diff --git a/src/main/java/dan200/computercraft/shared/util/ThreadUtils.java b/src/main/java/dan200/computercraft/shared/util/ThreadUtils.java index 24f487d4f..88b24f5dd 100644 --- a/src/main/java/dan200/computercraft/shared/util/ThreadUtils.java +++ b/src/main/java/dan200/computercraft/shared/util/ThreadUtils.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.util; import com.google.common.util.concurrent.ThreadFactoryBuilder; @@ -33,25 +32,21 @@ public static ThreadGroup group() } /** - * Create a new {@link ThreadFactory}, which constructs threads under a group of the given {@code name}. - * - * Each thread will be of the format {@code ComputerCraft--}, and belong to a group called {@code ComputerCraft-} (which in turn - * will be a child group of the main {@code ComputerCraft} group. + * Construct a group under ComputerCraft's shared group. * - * @param name The name for the thread group and child threads. - * @return The constructed thread factory. - * @see #builder(String) + * @param name The group's name. This will be prefixed with "ComputerCraft-". + * @return The constructed thread group. */ - public static ThreadFactory factory( String name ) + public static ThreadGroup group( String name ) { - return builder( name ).build(); + return new ThreadGroup( baseGroup, baseGroup.getName() + "-" + name ); } /** * Create a new {@link ThreadFactoryBuilder}, which constructs threads under a group of the given {@code name}. * - * Each thread will be of the format {@code ComputerCraft--}, and belong to a group called {@code ComputerCraft-} (which in turn - * will be a child group of the main {@code ComputerCraft} group. + * Each thread will be of the format {@code ComputerCraft--}, and belong to a group + * called {@code ComputerCraft-} (which in turn will be a child group of the main {@code ComputerCraft} group. * * @param name The name for the thread group and child threads. * @return The constructed thread factory builder, which may be extended with other properties. @@ -60,21 +55,25 @@ public static ThreadFactory factory( String name ) public static ThreadFactoryBuilder builder( String name ) { ThreadGroup group = group( name ); - return new ThreadFactoryBuilder().setDaemon( true ) - .setNameFormat( group.getName() - .replace( "%", "%%" ) + "-%d" ) + return new ThreadFactoryBuilder() + .setDaemon( true ) + .setNameFormat( group.getName().replace( "%", "%%" ) + "-%d" ) .setUncaughtExceptionHandler( ( t, e ) -> ComputerCraft.log.error( "Exception in thread " + t.getName(), e ) ) .setThreadFactory( x -> new Thread( group, x ) ); } /** - * Construct a group under ComputerCraft's shared group. + * Create a new {@link ThreadFactory}, which constructs threads under a group of the given {@code name}. * - * @param name The group's name. This will be prefixed with "ComputerCraft-". - * @return The constructed thread group. + * Each thread will be of the format {@code ComputerCraft--}, and belong to a group + * called {@code ComputerCraft-} (which in turn will be a child group of the main {@code ComputerCraft} group. + * + * @param name The name for the thread group and child threads. + * @return The constructed thread factory. + * @see #builder(String) */ - public static ThreadGroup group( String name ) + public static ThreadFactory factory( String name ) { - return new ThreadGroup( baseGroup, baseGroup.getName() + "-" + name ); + return builder( name ).build(); } } diff --git a/src/main/java/dan200/computercraft/shared/util/TickScheduler.java b/src/main/java/dan200/computercraft/shared/util/TickScheduler.java index d5b91017c..1252d97b9 100644 --- a/src/main/java/dan200/computercraft/shared/util/TickScheduler.java +++ b/src/main/java/dan200/computercraft/shared/util/TickScheduler.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.util; import com.google.common.collect.MapMaker; @@ -21,20 +20,20 @@ */ public final class TickScheduler { - private static final Set toTick = Collections.newSetFromMap( new MapMaker().weakKeys() - .makeMap() ); - private TickScheduler() { } + private static final Set toTick = Collections.newSetFromMap( + new MapMaker() + .weakKeys() + .makeMap() + ); + public static void schedule( TileGeneric tile ) { Level world = tile.getLevel(); - if( world != null && !world.isClientSide ) - { - toTick.add( tile ); - } + if( world != null && !world.isClientSide ) toTick.add( tile ); } public static void tick() @@ -48,7 +47,7 @@ public static void tick() Level world = tile.getLevel(); BlockPos pos = tile.getBlockPos(); - if( world != null && pos != null && world.hasChunkAt( pos ) && world.getBlockEntity( pos ) == tile ) + if( world != null && pos != null && world.isLoaded( pos ) && world.getBlockEntity( pos ) == tile ) { world.scheduleTick( pos, tile.getBlockState().getBlock(), 0 ); } diff --git a/src/main/java/dan200/computercraft/shared/util/WaterloggableHelpers.java b/src/main/java/dan200/computercraft/shared/util/WaterloggableHelpers.java index b385138aa..ded90fe2e 100644 --- a/src/main/java/dan200/computercraft/shared/util/WaterloggableHelpers.java +++ b/src/main/java/dan200/computercraft/shared/util/WaterloggableHelpers.java @@ -3,10 +3,10 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.util; import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.block.state.BlockState; @@ -16,9 +16,7 @@ import net.minecraft.world.level.material.Fluids; /** - * Represents a block which can be filled with water - * - * I'm fairly sure this exists on 1.14, but it's a useful convenience wrapper to have on 1.13. + * Helpers for working with waterlogged blocks. */ public final class WaterloggableHelpers { @@ -34,19 +32,19 @@ private WaterloggableHelpers() * @param state The current state * @return This waterlogged block's current fluid */ - public static FluidState getWaterloggedFluidState( BlockState state ) + public static FluidState getFluidState( BlockState state ) { return state.getValue( WATERLOGGED ) ? Fluids.WATER.getSource( false ) : Fluids.EMPTY.defaultFluidState(); } /** - * Call from {@link net.minecraft.world.level.block.Block#updatePostPlacement(BlockState, Direction, BlockState, IWorld, BlockPos, BlockPos)}. + * Call from {@link net.minecraft.world.level.block.Block#updateShape(BlockState, Direction, BlockState, LevelAccessor, BlockPos, BlockPos)}. * * @param state The current state * @param world The position of this block * @param pos The world this block exists in */ - public static void updateWaterloggedPostPlacement( BlockState state, LevelAccessor world, BlockPos pos ) + public static void updateShape( BlockState state, LevelAccessor world, BlockPos pos ) { if( state.getValue( WATERLOGGED ) ) { @@ -54,10 +52,8 @@ public static void updateWaterloggedPostPlacement( BlockState state, LevelAccess } } - public static boolean getWaterloggedStateForPlacement( BlockPlaceContext context ) + public static boolean getFluidStateForPlacement( BlockPlaceContext context ) { - return context.getLevel() - .getFluidState( context.getClickedPos() ) - .getType() == Fluids.WATER; + return context.getLevel().getFluidState( context.getClickedPos() ).getType() == Fluids.WATER; } } diff --git a/src/main/java/dan200/computercraft/shared/util/WorldUtil.java b/src/main/java/dan200/computercraft/shared/util/WorldUtil.java index 42bb791b3..644cc4f59 100644 --- a/src/main/java/dan200/computercraft/shared/util/WorldUtil.java +++ b/src/main/java/dan200/computercraft/shared/util/WorldUtil.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.util; import com.google.common.base.Predicate; @@ -31,27 +30,39 @@ public final class WorldUtil @SuppressWarnings( "Guava" ) private static final Predicate CAN_COLLIDE = x -> x != null && x.isAlive() && x.isPickable(); - private static final Map entityCache = new MapMaker().weakKeys() - .weakValues() - .makeMap(); + private static final Map entityCache = new MapMaker().weakKeys().weakValues().makeMap(); - public static boolean isLiquidBlock( Level world, BlockPos pos ) + private static synchronized Entity getEntity( Level world ) { - if( !world.isInWorldBounds( pos ) ) + // TODO: It'd be nice if we could avoid this. Maybe always use the turtle player (if it's available). + Entity entity = entityCache.get( world ); + if( entity != null ) return entity; + + entity = new ItemEntity( EntityType.ITEM, world ) { - return false; - } - return world.getBlockState( pos ) - .getMaterial() - .isLiquid(); + @Nonnull + @Override + public EntityDimensions getDimensions( @Nonnull Pose pose ) + { + return EntityDimensions.fixed( 0, 0 ); + } + }; + + entity.noPhysics = true; + entity.refreshDimensions(); + entityCache.put( world, entity ); + return entity; + } + + public static boolean isLiquidBlock( Level world, BlockPos pos ) + { + if( !world.isInWorldBounds( pos ) ) return false; + return world.getBlockState( pos ).getMaterial().isLiquid(); } public static boolean isVecInside( VoxelShape shape, Vec3 vec ) { - if( shape.isEmpty() ) - { - return false; - } + if( shape.isEmpty() ) return false; // AxisAlignedBB.contains, but without strict inequalities. AABB bb = shape.bounds(); return vec.x >= bb.minX && vec.x <= bb.maxX && vec.y >= bb.minY && vec.y <= bb.maxY && vec.z >= bb.minZ && vec.z <= bb.maxZ; @@ -64,11 +75,7 @@ public static Pair rayTraceEntities( Level world, Vec3 vecStart, V // Raycast for blocks Entity collisionEntity = getEntity( world ); collisionEntity.setPos( vecStart.x, vecStart.y, vecStart.z ); - ClipContext context = new ClipContext( vecStart, - vecEnd, - ClipContext.Block.COLLIDER, - ClipContext.Fluid.NONE, - collisionEntity ); + ClipContext context = new ClipContext( vecStart, vecEnd, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, collisionEntity ); HitResult result = world.clip( context ); if( result != null && result.getType() == HitResult.Type.BLOCK ) { @@ -80,12 +87,14 @@ public static Pair rayTraceEntities( Level world, Vec3 vecStart, V float xStretch = Math.abs( vecDir.x ) > 0.25f ? 0.0f : 1.0f; float yStretch = Math.abs( vecDir.y ) > 0.25f ? 0.0f : 1.0f; float zStretch = Math.abs( vecDir.z ) > 0.25f ? 0.0f : 1.0f; - AABB bigBox = new AABB( Math.min( vecStart.x, vecEnd.x ) - 0.375f * xStretch, + AABB bigBox = new AABB( + Math.min( vecStart.x, vecEnd.x ) - 0.375f * xStretch, Math.min( vecStart.y, vecEnd.y ) - 0.375f * yStretch, Math.min( vecStart.z, vecEnd.z ) - 0.375f * zStretch, Math.max( vecStart.x, vecEnd.x ) + 0.375f * xStretch, Math.max( vecStart.y, vecEnd.y ) + 0.375f * yStretch, - Math.max( vecStart.z, vecEnd.z ) + 0.375f * zStretch ); + Math.max( vecStart.z, vecEnd.z ) + 0.375f * zStretch + ); Entity closest = null; double closestDist = 99.0; @@ -100,8 +109,7 @@ public static Pair rayTraceEntities( Level world, Vec3 vecStart, V continue; } - Vec3 littleBoxResult = littleBox.clip( vecStart, vecEnd ) - .orElse( null ); + Vec3 littleBoxResult = littleBox.clip( vecStart, vecEnd ).orElse( null ); if( littleBoxResult != null ) { double dist = vecStart.distanceTo( littleBoxResult ); @@ -128,29 +136,9 @@ else if( littleBox.intersects( bigBox ) ) return null; } - private static synchronized Entity getEntity( Level world ) + public static Vec3 getRayStart( LivingEntity entity ) { - // TODO: It'd be nice if we could avoid this. Maybe always use the turtle player (if it's available). - Entity entity = entityCache.get( world ); - if( entity != null ) - { - return entity; - } - - entity = new ItemEntity( EntityType.ITEM, world ) - { - @Nonnull - @Override - public EntityDimensions getDimensions( @Nonnull Pose pose ) - { - return EntityDimensions.fixed( 0, 0 ); - } - }; - - entity.noPhysics = true; - entity.refreshDimensions(); - entityCache.put( world, entity ); - return entity; + return entity.getEyePosition( 1 ); } public static Vec3 getRayEnd( Player player ) @@ -160,11 +148,6 @@ public static Vec3 getRayEnd( Player player ) return getRayStart( player ).add( look.x * reach, look.y * reach, look.z * reach ); } - public static Vec3 getRayStart( LivingEntity entity ) - { - return entity.getEyePosition( 1 ); - } - public static void dropItemStack( @Nonnull ItemStack stack, Level world, BlockPos pos ) { dropItemStack( stack, world, pos, null ); @@ -194,21 +177,20 @@ public static void dropItemStack( @Nonnull ItemStack stack, Level world, BlockPo dropItemStack( stack, world, new Vec3( xPos, yPos, zPos ), xDir, yDir, zDir ); } + public static void dropItemStack( @Nonnull ItemStack stack, Level world, Vec3 pos ) + { + dropItemStack( stack, world, pos, 0.0, 0.0, 0.0 ); + } + public static void dropItemStack( @Nonnull ItemStack stack, Level world, Vec3 pos, double xDir, double yDir, double zDir ) { ItemEntity item = new ItemEntity( world, pos.x, pos.y, pos.z, stack.copy() ); - item.setDeltaMovement( xDir * 0.7 + world.getRandom() - .nextFloat() * 0.2 - 0.1, - yDir * 0.7 + world.getRandom() - .nextFloat() * 0.2 - 0.1, - zDir * 0.7 + world.getRandom() - .nextFloat() * 0.2 - 0.1 ); + item.setDeltaMovement( + xDir * 0.7 + world.getRandom().nextFloat() * 0.2 - 0.1, + yDir * 0.7 + world.getRandom().nextFloat() * 0.2 - 0.1, + zDir * 0.7 + world.getRandom().nextFloat() * 0.2 - 0.1 + ); item.setDefaultPickUpDelay(); world.addFreshEntity( item ); } - - public static void dropItemStack( @Nonnull ItemStack stack, Level world, Vec3 pos ) - { - dropItemStack( stack, world, pos, 0.0, 0.0, 0.0 ); - } } diff --git a/src/main/java/dan200/computercraft/shared/wired/InvariantChecker.java b/src/main/java/dan200/computercraft/shared/wired/InvariantChecker.java index ab790e922..0bff2c83f 100644 --- a/src/main/java/dan200/computercraft/shared/wired/InvariantChecker.java +++ b/src/main/java/dan200/computercraft/shared/wired/InvariantChecker.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.wired; import dan200.computercraft.ComputerCraft; @@ -11,7 +10,8 @@ /** * Verifies certain elements of a network are "well formed". * - * This adds substantial overhead to network modification, and so should only be enabled in a development environment. + * This adds substantial overhead to network modification, and so should only be enabled + * in a development environment. */ public final class InvariantChecker { @@ -19,25 +19,9 @@ public final class InvariantChecker private InvariantChecker() {} - public static void checkNetwork( WiredNetwork network ) - { - if( !ENABLED ) - { - return; - } - - for( WiredNode node : network.nodes ) - { - checkNode( node ); - } - } - public static void checkNode( WiredNode node ) { - if( !ENABLED ) - { - return; - } + if( !ENABLED ) return; WiredNetwork network = node.network; if( network == null ) @@ -59,4 +43,11 @@ public static void checkNode( WiredNode node ) } } } + + public static void checkNetwork( WiredNetwork network ) + { + if( !ENABLED ) return; + + for( WiredNode node : network.nodes ) checkNode( node ); + } } diff --git a/src/main/java/dan200/computercraft/shared/wired/WiredNetworkChange.java b/src/main/java/dan200/computercraft/shared/wired/WiredNetworkChange.java index fda849f10..c59640052 100644 --- a/src/main/java/dan200/computercraft/shared/wired/WiredNetworkChange.java +++ b/src/main/java/dan200/computercraft/shared/wired/WiredNetworkChange.java @@ -3,7 +3,6 @@ * Copyright Daniel Ratcliffe, 2011-2021. Do not distribute without permission. * Send enquiries to dratcliffe@gmail.com */ - package dan200.computercraft.shared.wired; import dan200.computercraft.api.network.wired.IWiredNetworkChange; @@ -27,6 +26,11 @@ private WiredNetworkChange( Map removed, Map removed, Map added ) + { + return new WiredNetworkChange( Collections.unmodifiableMap( removed ), Collections.unmodifiableMap( added ) ); + } + public static WiredNetworkChange added( Map added ) { return added.isEmpty() ? EMPTY : new WiredNetworkChange( Collections.emptyMap(), Collections.unmodifiableMap( added ) ); @@ -81,9 +85,11 @@ else if( newPeripherals.isEmpty() ) return changed( removed, added ); } - public static WiredNetworkChange changed( Map removed, Map added ) + @Nonnull + @Override + public Map peripheralsAdded() { - return new WiredNetworkChange( Collections.unmodifiableMap( removed ), Collections.unmodifiableMap( added ) ); + return added; } @Nonnull @@ -93,29 +99,19 @@ public Map peripheralsRemoved() return removed; } - @Nonnull - @Override - public Map peripheralsAdded() + public boolean isEmpty() { - return added; + return added.isEmpty() && removed.isEmpty(); } void broadcast( Iterable nodes ) { if( !isEmpty() ) { - for( WiredNode node : nodes ) - { - node.element.networkChanged( this ); - } + for( WiredNode node : nodes ) node.element.networkChanged( this ); } } - public boolean isEmpty() - { - return added.isEmpty() && removed.isEmpty(); - } - void broadcast( WiredNode node ) { if( !isEmpty() ) 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 c3a6ce314..d0ed2ba24 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/paintutils.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/paintutils.lua @@ -68,7 +68,7 @@ end -- @{paintutils.drawImage}, or `nil` if the file does not exist. -- @usage Load an image and draw it. -- --- local image = paintutils.loadImage("test-image.nfp") +-- local image = paintutils.loadImage("data/example.nfp") -- paintutils.drawImage(image, term.getCursorPos()) function loadImage(path) expect(1, path, "string") 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 5266678e7..f3c74a1c2 100644 --- a/src/main/resources/data/computercraft/lua/rom/apis/peripheral.lua +++ b/src/main/resources/data/computercraft/lua/rom/apis/peripheral.lua @@ -1,18 +1,90 @@ ---- The Peripheral API is for interacting with peripherals connected to the --- computer, such as the Disk Drive, the Advanced Monitor and Monitor. --- --- Each peripheral block has a name, either referring to the side the peripheral --- can be found on, or a name on an adjacent wired network. --- --- If the peripheral is next to the computer, its side is either `front`, --- `back`, `left`, `right`, `top` or `bottom`. If the peripheral is attached by --- a cable, its side will follow the format `type_id`, for example `printer_0`. --- --- Peripheral functions are called *methods*, a term borrowed from Java. --- --- @module peripheral --- @since 1.3 --- @changed 1.51 Add support for wired modems. +--[[- Peripherals are blocks (or turtle and pocket computer upgrades) which can +be controlled by a computer. For instance, the @{speaker} peripheral allows a +computer to play music and the @{monitor} peripheral allows you to display text +in the world. + +## Referencing peripherals + +Computers can interact with adjacent peripherals. Each peripheral is given a +name based on which direction it is in. For instance, a disk drive below your +computer will be called `"bottom"` in your Lua code, one to the left called +`"left"` , and so on for all 6 directions (`"bottom"`, `"top"`, `"left"`, +`"right"`, `"front"`, `"back"`). + +You can list the names of all peripherals with the `peripherals` program, or the +@{peripheral.getNames} function. + +It's also possible to use peripherals which are further away from your computer +through the use of @{modem|Wired Modems}. Place one modem against your computer, +run Networking Cable to your peripheral, and then place another modem against +that block. You can then right click the modem to use (or *attach*) the +peripheral. This will print a peripheral name to chat, which can then be used +just like a direction name to access the peripheral. You can click on the message +to copy the name to your clipboard. + +## Using peripherals + +Once you have the name of a peripheral, you can call functions on it using the +@{peripheral.call} function. This takes the name of our peripheral, the name of +the function we want to call, and then its arguments. + +> Some bits of the peripheral API call peripheral functions *methods* instead +> (for example, the @{peripheral.getMethods} function). Don't worry, they're the +> same thing! + +Let's say we have a monitor above our computer (and so "top") and want to +@{monitor.write|write some text to it}. We'd write the following: + +```lua +peripheral.call("top", "write", "This is displayed on a monitor!") +``` + +Once you start calling making a couple of peripheral calls this can get very +repetitive, and so we can @{peripheral.wrap|wrap} a peripheral. This builds a +table of all the peripheral's functions so you can use it like an API or module. + +For instance, we could have written the above example as follows: + +```lua +local my_monitor = peripheral.wrap("top") +my_monitor.write("This is displayed on a monitor!") +``` + +## Finding peripherals + +Sometimes when you're writing a program you don't care what a peripheral is +called, you just need to know it's there. For instance, if you're writing a +music player, you just need a speaker - it doesn't matter if it's above or below +the computer. + +Thankfully there's a quick way to do this: @{peripheral.find}. This takes a +*peripheral type* and returns all the attached peripherals which are of this +type. + +What is a peripheral type though? This is a string which describes what a +peripheral is, and so what functions are available on it. For instance, speakers +are just called `"speaker"`, and monitors `"monitor"`. Some peripherals might +have more than one type; a Minecraft chest is both a `"minecraft:chest"` and +`"inventory"`. + +You can get all the types a peripheral has with @{peripheral.getType}, and check +a peripheral is a specific type with @{peripheral.hasType}. + +To return to our original example, let's use @{peripheral.find} to find an +attached speaker: + +```lua +local speaker = peripheral.find("speaker") +speaker.playNote("harp") +``` + +@module peripheral +@see event!peripheral This event is fired whenever a new peripheral is attached. +@see event!peripheral_detach This event is fired whenever a peripheral is detached. +@since 1.3 +@changed 1.51 Add support for wired modems. +@changed 1.99 Peripherals can have multiple types. +]] local expect = dofile("rom/modules/main/cc/expect.lua").expect @@ -33,7 +105,7 @@ function getNames() local side = sides[n] if native.isPresent(side) then table.insert(results, side) - if native.getType(side) == "modem" and not native.call(side, "isWireless") then + if native.hasType(side, "modem") and not native.call(side, "isWireless") then local remote = native.call(side, "getNamesRemote") for _, name in ipairs(remote) do table.insert(results, name) @@ -58,7 +130,7 @@ function isPresent(name) for n = 1, #sides do local side = sides[n] - if native.getType(side) == "modem" and not native.call(side, "isWireless") and + if native.hasType(side, "modem") and not native.call(side, "isWireless") and native.call(side, "isPresentRemote", name) then return true @@ -67,12 +139,17 @@ function isPresent(name) return false end ---- Get the type of a wrapped peripheral, or a peripheral with the given name. --- --- @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. +--[[- Get the types of a named or wrapped peripheral. + +@tparam string|table peripheral The name of the peripheral to find, or a +wrapped peripheral instance. +@treturn string... The peripheral's types, or `nil` if it is not present. +@changed 1.88.0 Accepts a wrapped peripheral as an argument. +@changed 1.99 Now returns multiple types. +@usage Get the type of a peripheral above this computer. + + peripheral.getType("top") +]] function getType(peripheral) expect(1, peripheral, "string", "table") if type(peripheral) == "string" then -- Peripheral name passed @@ -81,7 +158,7 @@ function getType(peripheral) end for n = 1, #sides do local side = sides[n] - if native.getType(side) == "modem" and not native.call(side, "isWireless") and + if native.hasType(side, "modem") and not native.call(side, "isWireless") and native.call(side, "isPresentRemote", peripheral) then return native.call(side, "getTypeRemote", peripheral) @@ -90,10 +167,43 @@ function getType(peripheral) return nil else local mt = getmetatable(peripheral) - if not mt or mt.__name ~= "peripheral" or type(mt.type) ~= "string" then + if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then + error("bad argument #1 (table is not a peripheral)", 2) + end + return table.unpack(mt.types) + end +end + +--[[- Check if a peripheral is of a particular type. + +@tparam string|table peripheral The name of the peripheral or a wrapped peripheral instance. +@tparam string peripheral_type The type to check. + +@treturn boolean|nil If a peripheral has a particular type, or `nil` if it is not present. +@since 1.99 +]] +function hasType(peripheral, peripheral_type) + expect(1, peripheral, "string", "table") + expect(2, peripheral_type, "string") + if type(peripheral) == "string" then -- Peripheral name passed + if native.isPresent(peripheral) then + return native.hasType(peripheral, peripheral_type) + end + for n = 1, #sides do + local side = sides[n] + if native.hasType(side, "modem") and not native.call(side, "isWireless") and + native.call(side, "isPresentRemote", peripheral) + then + return native.call(side, "hasTypeRemote", peripheral, peripheral_type) + end + end + return nil + else + local mt = getmetatable(peripheral) + if not mt or mt.__name ~= "peripheral" or type(mt.types) ~= "table" then error("bad argument #1 (table is not a peripheral)", 2) end - return mt.type + return mt.types[peripheral_type] ~= nil end end @@ -109,7 +219,7 @@ function getMethods(name) end for n = 1, #sides do local side = sides[n] - if native.getType(side) == "modem" and not native.call(side, "isWireless") and + if native.hasType(side, "modem") and not native.call(side, "isWireless") and native.call(side, "isPresentRemote", name) then return native.call(side, "getMethodsRemote", name) @@ -151,7 +261,7 @@ function call(name, method, ...) for n = 1, #sides do local side = sides[n] - if native.getType(side) == "modem" and not native.call(side, "isWireless") and + if native.hasType(side, "modem") and not native.call(side, "isWireless") and native.call(side, "isPresentRemote", name) then return native.call(side, "callRemote", name, method, ...) @@ -160,15 +270,16 @@ function call(name, method, ...) return nil end ---- Get a table containing functions pointing to the peripheral's methods, which --- can then be called as if using @{peripheral.call}. +--- Get a table containing all functions available on a peripheral. These can +-- then be called instead of using @{peripheral.call} every time. -- -- @tparam string name The name of the peripheral to wrap. -- @treturn table|nil The table containing the peripheral's methods, or `nil` if -- there is no peripheral present with the given name. -- @usage Open the modem on the top of this computer. -- --- peripheral.wrap("top").open(1) +-- local modem = peripheral.wrap("top") +-- modem.open(1) function wrap(name) expect(1, name, "string") @@ -177,10 +288,14 @@ function wrap(name) return nil end + -- We store our types array as a list (for getType) and a lookup table (for hasType). + local types = { peripheral.getType(name) } + for i = 1, #types do types[types[i]] = true end local result = setmetatable({}, { __name = "peripheral", name = name, - type = peripheral.getType(name), + type = types[1], + types = types, }) for _, method in ipairs(methods) do result[method] = function(...) @@ -222,7 +337,7 @@ function find(ty, filter) local results = {} for _, name in ipairs(peripheral.getNames()) do - if peripheral.getType(name) == ty then + if peripheral.hasType(name, ty) then local wrapped = peripheral.wrap(name) if filter == nil or filter(name, wrapped) then table.insert(results, wrapped) diff --git a/src/main/resources/data/computercraft/lua/rom/help/changelog.md b/src/main/resources/data/computercraft/lua/rom/help/changelog.md index c0ff4d7ae..cf2e4558d 100644 --- a/src/main/resources/data/computercraft/lua/rom/help/changelog.md +++ b/src/main/resources/data/computercraft/lua/rom/help/changelog.md @@ -1,10 +1,131 @@ -# New features in CC: Restitched 1.96.1-rc1 +# New features in CC: Tweaked 1.99.0 + +* Pocket computers in their offhand will open without showing a terminal. You can look around and interact with the world, but your keyboard will be forwarded to the computer. (Wojbie, MagGen-hub). +* Peripherals can now have multiple types. `peripheral.getType` now returns multiple values, and `peripheral.hasType` checks if a peripheral has a specific type. +* Add several missing keys to the `keys` table. (ralphgod3) +* Add feature introduction/changed version information to the documentation. (MCJack123) +* Increase the file upload limit to 512KiB. +* Rednet can now handle computer IDs larger than 65535. (Ale32bit) +* Optimise deduplication of rednet messages (MCJack123) +* Make `term.blit` colours case insensitive. (Ocawesome101) +* Add a new `about` program for easier version identification. (MCJack123) +* Optimise peripheral calls in `rednet.run`. (xAnavrins) +* Add dimension parameter to `commands.getBlockInfo`. +* Add `cc.pretty.pretty_print` helper function (Lupus590). +* Add back JEI integration. +* Turtle and pocket computer upgrades can now be added and modified with data packs. +* Various translation updates (MORIMORI3017, Ale2Bit, mindy15963) -A Few bug fixes: -* Fix crash with `getItemDetails(slot, true)` on servers -* Fix Optifine Shaders +And several bug fixes: +* Fix various computer commands failing when OP level was 4. +* Various documentation fixes. (xXTurnerLP, MCJack123) +* Fix `textutils.serialize` not serialising infinity and nan values. (Wojbie) +* Wired modems now correctly clean up mounts when a peripheral is detached. +* Fix incorrect turtle and pocket computer upgrade recipes in the recipe book. +* Fix speakers not playing sounds added via resource packs which are not registered in-game. +* Fix speaker upgrades sending packets after the server has stopped. +* Monitor sizing has been rewritten, hopefully making it more stable. +* Peripherals are now invalidated when the computer ticks, rather than when the peripheral changes. +* Fix printouts and pocket computers rendering at fullbright when in item frames. +* All mod blocks now have an effective tool (pickaxe). + +# New features in CC: Tweaked 1.98.2 + +* Add JP translation (MORIMORI0317) +* Migrate several recipes to data generators. + +Several bug fixes: +* Fix volume speaker sounds are played at. +* Fix several rendering issues when holding pocket computers and printouts in + hand. +* Ensure wired modems and cables join the wired network on chunk load. +* Fix stack overflow when using wired networks. + +# New features in CC: Tweaked 1.98.1 + +Several bug fixes: +* Fix monitors not correctly resizing when placed. +* Update Russian translation (DrHesperus). + +# New features in CC: Tweaked 1.98.0 +* Add motd for file uploading. +* Add config options to limit total bandwidth used by the HTTP API. + +And several bug fixes: +* Fix `settings.define` not accepting a nil second argument (SkyTheCodeMaster). +* Various documentation fixes (Angalexik, emiliskiskis, SkyTheCodeMaster). +* Fix selected slot indicator not appearing in turtle interface. +* Fix crash when printers are placed as part of world generation. +* Fix crash when breaking a speaker on a multiplayer world. +* Add a missing type check for `http.checkURL`. +* Prevent `parallel.*` from hanging when no arguments are given. +* Prevent issue in rednet when the message ID is NaN. +* Fix `help` program crashing when terminal changes width. +* Ensure monitors are well-formed when placed, preventing graphical glitches + when using Carry On or Quark. +* Accept several more extensions in the websocket client. +* Prevent `wget` crashing when given an invalid URL and no filename. +* Correctly wrap string within `textutils.slowWrite`. + +# New features in CC: Tweaked 1.97.0 + +* Update several translations (Anavrins, Jummit, Naheulf). +* Add button to view a computer's folder to `/computercraft dump`. +* Allow cleaning dyed turtles in a cauldron. +* Add scale subcommand to `monitor` program (MCJack123). +* Add option to make `textutils.serialize` not write an indent (magiczocker10). +* Allow comparing vectors using `==` (fatboychummy). +* Improve HTTP error messages for SSL failures. +* Allow `craft` program to craft unlimited items (fatboychummy). +* Impose some limits on various command queues. +* Add buttons to shutdown and terminate to computer GUIs. +* Add program subcompletion to several programs (Wojbie). +* Update the `help` program to accept and (partially) highlight markdown files. +* Remove config option for the debug API. +* Allow setting the subprotocol header for websockets. +* Add basic JMX monitoring on dedicated servers. +* Add support for MoreRed bundled. +* Allow uploading files by dropping them onto a computer. + +And several bug fixes: +* Fix NPE when using a treasure disk when no treasure disks are available. +* Prevent command computers discarding command ouput when certain game rules are off. +* Fix turtles not updating peripherals when upgrades are unequipped (Ronan-H). +* Fix computers not shutting down on fatal errors within the Lua VM. +* Speakers now correctly stop playing when broken, and sound follows noisy turtles and pocket computers. +* Update the `wget` to be more resiliant in the face of user-errors. +* Fix exiting `paint` typing "e" in the shell. +* Fix coloured pocket computers using the wrong texture. +* Correctly render the transparent background on pocket/normal computers. +* Don't apply CraftTweaker actions twice on single-player worlds. + +# New features in CC: Tweaked 1.97.0 + +* Update several translations (Anavrins, Jummit, Naheulf). +* Add button to view a computer's folder to `/computercraft dump`. +* Allow cleaning dyed turtles in a cauldron. +* Add scale subcommand to `monitor` program (MCJack123). +* Add option to make `textutils.serialize` not write an indent (magiczocker10). +* Allow comparing vectors using `==` (fatboychummy). +* Improve HTTP error messages for SSL failures. +* Allow `craft` program to craft unlimited items (fatboychummy). +* Impose some limits on various command queues. +* Add buttons to shutdown and terminate to computer GUIs. +* Add program subcompletion to several programs (Wojbie). +* Update the `help` program to accept and (partially) highlight markdown files. +* Remove config option for the debug API. +* Allow setting the subprotocol header for websockets. + +And several bug fixes: +* Fix NPE when using a treasure disk when no treasure disks are available. +* Prevent command computers discarding command ouput when certain game rules are off. +* Fix turtles not updating peripherals when upgrades are unequipped (Ronan-H). +* Fix computers not shutting down on fatal errors within the Lua VM. +* Speakers now correctly stop playing when broken, and sound follows noisy turtles and pocket computers. +* Update the `wget` to be more resiliant in the face of user-errors. +* Fix exiting `paint` typing "e" in the shell. -# New features in CC: Restitched 1.96.0 +# New features in CC: Tweaked 1.96.0 * Use lightGrey for folders within the "list" program. * Add getLimit to inventory peripherals. @@ -22,32 +143,37 @@ And several bug fixes: * Allow turtles to use compostors. * Fix dupe bug when colouring turtles. -# New features in CC: Restitched 1.95.3 +# New features in CC: Tweaked 1.95.3 Several bug fixes: * Correctly serialise sparse arrays into JSON (livegamer999) +* Fix hasAudio/playAudio failing on record discs. * Fix rs.getBundledInput returning the output instead (SkyTheCodeMaster) * Programs run via edit are now a little better behaved (Wojbie) * Add User-Agent to a websocket's headers. -# New features in CC: Restitched 1.95.2 +# New features in CC: Tweaked 1.95.2 * Add `isReadOnly` to `fs.attributes` (Lupus590) * Many more programs now support numpad enter (Wojbie) Several bug fixes: * Fix some commands failing to parse on dedicated servers. +* Fix all disk recipes appearing to produce a white disk in JEI/recipe book. * Hopefully improve edit's behaviour with AltGr on some European keyboards. * Prevent files being usable after their mount was removed. * Fix the `id` program crashing on non-disk items (Wojbie). +* Preserve registration order of turtle/pocket upgrades when displaying in JEI. -# New features in CC: Restitched 1.95.1 +# New features in CC: Tweaked 1.95.1 Several bug fixes: * Command computers now drop items again. * Restore crafting of disks with dyes. +* Fix CraftTweaker integrations for damageable items. +* Catch reflection errors in the generic peripheral system, resolving crashes with Botania. -# New features in CC: Restitched 1.95.0 +# New features in CC: Tweaked 1.95.0 * Optimise the paint program's initial render. * Several documentation improvments (Gibbo3771, MCJack123). @@ -107,6 +233,10 @@ And several bug fixes: * Correctly handle tabs within textutils.unserailizeJSON. * Fix sheep not dropping items when sheared by turtles. +# New features in CC: Tweaked 1.91.1 + +* Fix crash when turtles interact with an entity. + # New features in CC: Tweaked 1.91.0 * [Generic peripherals] Expose NBT hashes of items to inventory methods. @@ -153,8 +283,6 @@ And several bug fixes: * Fix deadlock when mistakenly "watching" an unloaded chunk. * Fix full path of files being leaked in some errors. -Type "help changelog" to see the full version history. - # New features in CC: Tweaked 1.89.1 * Fix crashes when rendering monitors of varying sizes. diff --git a/src/main/resources/data/computercraft/lua/rom/help/whatsnew.md b/src/main/resources/data/computercraft/lua/rom/help/whatsnew.md index 6b93a582f..5c906b4f9 100644 --- a/src/main/resources/data/computercraft/lua/rom/help/whatsnew.md +++ b/src/main/resources/data/computercraft/lua/rom/help/whatsnew.md @@ -1,7 +1,32 @@ -New features in CC: Restitched 1.96.1-rc1 +New features in CC: Tweaked 1.99.0 + +* Pocket computers in their offhand will open without showing a terminal. You can look around and interact with the world, but your keyboard will be forwarded to the computer. (Wojbie, MagGen-hub). +* Peripherals can now have multiple types. `peripheral.getType` now returns multiple values, and `peripheral.hasType` checks if a peripheral has a specific type. +* Add several missing keys to the `keys` table. (ralphgod3) +* Add feature introduction/changed version information to the documentation. (MCJack123) +* Increase the file upload limit to 512KiB. +* Rednet can now handle computer IDs larger than 65535. (Ale32bit) +* Optimise deduplication of rednet messages (MCJack123) +* Make `term.blit` colours case insensitive. (Ocawesome101) +* Add a new `about` program for easier version identification. (MCJack123) +* Optimise peripheral calls in `rednet.run`. (xAnavrins) +* Add dimension parameter to `commands.getBlockInfo`. +* Add `cc.pretty.pretty_print` helper function (Lupus590). +* Add back JEI integration. +* Turtle and pocket computer upgrades can now be added and modified with data packs. +* Various translation updates (MORIMORI3017, Ale2Bit, mindy15963) And several bug fixes: -* Fix crash with `getItemDetails(slot, true)` on servers -* Fix Optifine Shaders +* Fix various computer commands failing when OP level was 4. +* Various documentation fixes. (xXTurnerLP, MCJack123) +* Fix `textutils.serialize` not serialising infinity and nan values. (Wojbie) +* Wired modems now correctly clean up mounts when a peripheral is detached. +* Fix incorrect turtle and pocket computer upgrade recipes in the recipe book. +* Fix speakers not playing sounds added via resource packs which are not registered in-game. +* Fix speaker upgrades sending packets after the server has stopped. +* Monitor sizing has been rewritten, hopefully making it more stable. +* Peripherals are now invalidated when the computer ticks, rather than when the peripheral changes. +* Fix printouts and pocket computers rendering at fullbright when in item frames. +* All mod blocks now have an effective tool (pickaxe). Type "help changelog" to see the full version history. 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 410934c5d..c188e7823 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 @@ -9,8 +9,8 @@ -- @usage Load an image from `example.nft` and draw it. -- -- local nft = require "cc.image.nft" --- local image = assert(nft.load("example.nft")) --- nft.draw(image) +-- local image = assert(nft.load("data/example.nft")) +-- nft.draw(image, term.getCursorPos()) local expect = require "cc.expect".expect @@ -41,6 +41,7 @@ local function parse(image) end line = line + 1 + foreground, background = "0", "f" else local next = image:find("[\n\30\31]", i) or #image + 1 local seg_len = next - i 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 f5cdeee6c..7a918df59 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 @@ -19,7 +19,7 @@ The structure of this module is based on [A Prettier Printer][prettier]. @usage Print a table to the terminal local pretty = require "cc.pretty" - pretty.print(pretty.pretty({ 1, 2, 3 })) + pretty.pretty_print({ 1, 2, 3 }) @usage Build a custom document and display it @@ -463,6 +463,7 @@ end -- -- local pretty = require "cc.pretty" -- pretty.print(pretty.pretty({ 1, 2, 3 })) +-- @see pretty_print for a shorthand to prettify and print an object. local function pretty(obj, options) expect(2, options, "table", "nil") options = options or {} @@ -474,6 +475,33 @@ local function pretty(obj, options) return pretty_impl(obj, actual_options, {}) end +--[[- A shortcut for calling @{pretty} and @{print} together. + +@param obj The object to pretty-print. +@tparam[opt] { function_args = boolean, function_source = boolean } options +Controls how various properties are displayed. + - `function_args`: Show the arguments to a function if known (`false` by default). + - `function_source`: Show where the function was defined, instead of + `function: xxxxxxxx` (`false` by default). +@tparam[opt] number ribbon_frac The maximum fraction of the width that we should write in. + +@usage Display a table on the screen. + + local pretty = require "cc.pretty" + pretty.pretty_print({ 1, 2, 3 }) + +@see pretty +@see print +@since 1.99 +]] +local function pretty_print(obj, options, ribbon_frac) + expect(2, options, "table", "nil") + options = options or {} + expect(3, ribbon_frac, "number", "nil") + + return print(pretty(obj, options), ribbon_frac) +end + return { empty = empty, space = space, @@ -489,4 +517,6 @@ return { render = render, pretty = pretty, + + pretty_print = pretty_print, } diff --git a/src/main/resources/data/computercraft/lua/rom/motd.txt b/src/main/resources/data/computercraft/lua/rom/motd.txt index 29992e714..972589050 100644 --- a/src/main/resources/data/computercraft/lua/rom/motd.txt +++ b/src/main/resources/data/computercraft/lua/rom/motd.txt @@ -1,4 +1,4 @@ -Please report bugs at https://github.com/cc-tweaked/cc-restitched/issues. Thanks! +Please report bugs at https://github.com/cc-tweaked/CC-Tweaked. Thanks! View the documentation at https://tweaked.cc Show off your programs or ask for help at our forum: https://forums.computercraft.cc You can disable these messages by running "set motd.enable false". diff --git a/src/main/resources/data/computercraft/lua/rom/programs/peripherals.lua b/src/main/resources/data/computercraft/lua/rom/programs/peripherals.lua index 290619614..17d4877e8 100644 --- a/src/main/resources/data/computercraft/lua/rom/programs/peripherals.lua +++ b/src/main/resources/data/computercraft/lua/rom/programs/peripherals.lua @@ -3,7 +3,7 @@ print("Attached Peripherals:") if #tPeripherals > 0 then for n = 1, #tPeripherals do local sPeripheral = tPeripherals[n] - print(sPeripheral .. " (" .. peripheral.getType(sPeripheral) .. ")") + print(sPeripheral .. " (" .. table.concat({ peripheral.getType(sPeripheral) }, ", ") .. ")") end else print("None")